]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/API2/LXC.pm
fix #1147: allow marking non-volume mps as shared
[pve-container.git] / src / PVE / API2 / LXC.pm
index 5e987926627609f1754d01ef13487864799f8be0..d0e558a41d02cbdffa06c843f0f2cd1490a63fcb 100644 (file)
@@ -107,7 +107,7 @@ __PACKAGE__->register_method({
     proxyto => 'node',
     parameters => {
        additionalProperties => 0,
-       properties => PVE::LXC::json_config_properties({
+       properties => PVE::LXC::Config->json_config_properties({
            node => get_standard_option('pve-node'),
            vmid => get_standard_option('pve-vmid', { completion => \&PVE::Cluster::complete_next_vmid }),
            ostemplate => {
@@ -148,6 +148,12 @@ __PACKAGE__->register_method({
                type => 'boolean',
                description => "Ignore errors when extracting the template.",
            },
+           'ssh-public-keys' => {
+               optional => 1,
+               type => 'string',
+               description => "Setup public SSH keys (one key per line, " .
+                               "OpenSSH format).",
+           },
        }),
     },
     returns => {
@@ -191,6 +197,9 @@ __PACKAGE__->register_method({
 
        my $password = extract_param($param, 'password');
 
+       my $ssh_keys = extract_param($param, 'ssh-public-keys');
+       PVE::Tools::validate_ssh_public_keys($ssh_keys) if defined($ssh_keys);
+
        my $pool = extract_param($param, 'pool');
 
        if (defined($pool)) {
@@ -247,18 +256,32 @@ __PACKAGE__->register_method({
        my $conf = {};
 
        my $no_disk_param = {};
+       my $mp_param = {};
+       my $storage_only_mode = 1;
        foreach my $opt (keys %$param) {
            my $value = $param->{$opt};
            if ($opt eq 'rootfs' || $opt =~ m/^mp\d+$/) {
                # allow to use simple numbers (add default storage in that case)
-               $param->{$opt} = "$storage:$value" if $value =~ m/^\d+(\.\d+)?$/;
+               if ($value =~ m/^\d+(\.\d+)?$/) {
+                   $mp_param->{$opt} = "$storage:$value";
+               } else {
+                   $mp_param->{$opt} = $value;
+               }
+               $storage_only_mode = 0;
+           } elsif ($opt =~ m/^unused\d+$/) {
+               warn "ignoring '$opt', cannot create/restore with unused volume\n";
+               delete $param->{$opt};
            } else {
                $no_disk_param->{$opt} = $value;
            }
        }
 
+       die "mountpoints configured, but 'rootfs' not set - aborting\n"
+           if !$storage_only_mode && !defined($mp_param->{rootfs});
+
        # check storage access, activate storage
-       PVE::LXC::foreach_mountpoint($param, sub {
+       my $delayed_mp_param = {};
+       PVE::LXC::Config->foreach_mountpoint($mp_param, sub {
            my ($ms, $mountpoint) = @_;
 
            my $volid = $mountpoint->{volume};
@@ -274,9 +297,9 @@ __PACKAGE__->register_method({
        });
 
        # check/activate default storage
-       &$check_and_activate_storage($storage) if !defined($param->{rootfs});
+       &$check_and_activate_storage($storage) if !defined($mp_param->{rootfs});
 
-       PVE::LXC::update_pct_config($vmid, $conf, 0, $no_disk_param);
+       PVE::LXC::Config->update_pct_config($vmid, $conf, 0, $no_disk_param);
 
        $conf->{unprivileged} = 1 if $unprivileged;
 
@@ -291,30 +314,93 @@ __PACKAGE__->register_method({
 
        my $code = sub {
            &$check_vmid_usage(); # final check after locking
-                   
+           my $old_conf;
+
+           my $config_fn = PVE::LXC::Config->config_file($vmid);
+           if (-f $config_fn) {
+               die "container exists" if !$restore; # just to be sure
+               $old_conf = PVE::LXC::Config->load_config($vmid);
+           } else {
+               eval {
+                   # try to create empty config on local node, we have an flock
+                   PVE::LXC::Config->write_config($vmid, {});
+               };
+
+               # another node was faster, abort
+               die "Could not reserve ID $vmid, already taken\n" if $@;
+           }
+
            PVE::Cluster::check_cfs_quorum();
            my $vollist = [];
 
            eval {
-               if (!defined($param->{rootfs})) {
+               if ($storage_only_mode) {
                    if ($restore) {
-                       my (undef, $disksize) = PVE::LXC::Create::recover_config($archive);
-                       die "unable to detect disk size - please specify rootfs (size)\n"
-                           if !$disksize;
-                       $disksize /= 1024 * 1024 * 1024; # create_disks expects GB as unit size
-                       $param->{rootfs} = "$storage:$disksize";
+                       (undef, $mp_param) = PVE::LXC::Create::recover_config($archive);
+                       die "rootfs configuration could not be recovered, please check and specify manually!\n"
+                           if !defined($mp_param->{rootfs});
+                       PVE::LXC::Config->foreach_mountpoint($mp_param, sub {
+                           my ($ms, $mountpoint) = @_;
+                           my $type = $mountpoint->{type};
+                           if ($type eq 'volume') {
+                               die "unable to detect disk size - please specify $ms (size)\n"
+                                   if !defined($mountpoint->{size});
+                               my $disksize = $mountpoint->{size} / (1024 * 1024 * 1024); # create_disks expects GB as unit size
+                               delete $mountpoint->{size};
+                               $mountpoint->{volume} = "$storage:$disksize";
+                               $mp_param->{$ms} = PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+                           } else {
+                               my $type = $mountpoint->{type};
+                               die "restoring rootfs to $type mount is only possible by specifying -rootfs manually!\n"
+                                   if ($ms eq 'rootfs');
+                               die "restoring '$ms' to $type mount is only possible for root\n"
+                                   if $authuser ne 'root@pam';
+
+                               if ($mountpoint->{backup}) {
+                                   warn "WARNING - unsupported configuration!\n";
+                                   warn "backup was enabled for $type mountpoint $ms ('$mountpoint->{mp}')\n";
+                                   warn "mountpoint configuration will be restored after archive extraction!\n";
+                                   warn "contained files will be restored to wrong directory!\n";
+                               }
+                               delete $mp_param->{$ms}; # actually delay bind/dev mps
+                               $delayed_mp_param->{$ms} = PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+                           }
+                       });
                    } else {
-                       $param->{rootfs} = "$storage:4"; # defaults to 4GB
+                       $mp_param->{rootfs} = "$storage:4"; # defaults to 4GB
                    }
                }
 
-               $vollist = PVE::LXC::create_disks($storage_cfg, $vmid, $param, $conf);
+               $vollist = PVE::LXC::create_disks($storage_cfg, $vmid, $mp_param, $conf);
+
+               if (defined($old_conf)) {
+                   # destroy old container volumes
+                   PVE::LXC::destroy_lxc_container($storage_cfg, $vmid, $old_conf, {});
+               }
+
+               eval {
+                   my $rootdir = PVE::LXC::mount_all($vmid, $storage_cfg, $conf, 1);
+                   PVE::LXC::Create::restore_archive($archive, $rootdir, $conf, $ignore_unpack_errors);
 
-               PVE::LXC::Create::create_rootfs($storage_cfg, $vmid, $conf, $archive, $password, $restore, $ignore_unpack_errors);
+                   if ($restore) {
+                       PVE::LXC::Create::restore_configuration($vmid, $rootdir, $conf, $authuser ne 'root@pam');
+                   } else {
+                       my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir); # detect OS
+                       PVE::LXC::Config->write_config($vmid, $conf); # safe config (after OS detection)
+                       $lxc_setup->post_create_hook($password, $ssh_keys);
+                   }
+               };
+               my $err = $@;
+               PVE::LXC::umount_all($vmid, $storage_cfg, $conf, $err ? 1 : 0);
+               PVE::Storage::deactivate_volumes($storage_cfg, PVE::LXC::Config->get_vm_volumes($conf));
+               die $err if $err;
                # set some defaults
                $conf->{hostname} ||= "CT$vmid";
                $conf->{memory} ||= 512;
                $conf->{swap} //= 512;
+               foreach my $mp (keys %$delayed_mp_param) {
+                   $conf->{$mp} = $delayed_mp_param->{$mp};
+               }
                PVE::LXC::Config->write_config($vmid, $conf);
            };
            if (my $err = $@) {
@@ -759,6 +845,12 @@ __PACKAGE__->register_method({
                description => "Use online/live migration.",
                optional => 1,
            },
+           force => {
+               type => 'boolean',
+               description => "Force migration despite local bind / device" .
+                   " mounts. NOTE: deprecated, use 'shared' property of mount point instead.",
+               optional => 1,
+           },
        },
     },
     returns => {
@@ -882,7 +974,7 @@ __PACKAGE__->register_method({
        my $storage_cfg = PVE::Storage::config();
        #Maybe include later
        #my $nodelist = PVE::LXC::shared_nodes($conf, $storage_cfg);
-       my $hasFeature = PVE::LXC::has_feature($feature, $conf, $storage_cfg, $snapname);
+       my $hasFeature = PVE::LXC::Config->has_feature($feature, $conf, $storage_cfg, $snapname);
 
        return {
            hasFeature => $hasFeature,
@@ -906,6 +998,12 @@ __PACKAGE__->register_method({
        properties => {
            node => get_standard_option('pve-node'),
            vmid => get_standard_option('pve-vmid', { completion => \&PVE::LXC::complete_ctid_stopped }),
+           experimental => {
+               type => 'boolean',
+               description => "The template feature is experimental, set this " .
+                   "flag if you know what you are doing.",
+               default => 0,
+           },
        },
     },
     returns => { type => 'null'},
@@ -942,7 +1040,7 @@ __PACKAGE__->register_method({
 
            PVE::LXC::Config->write_config($vmid, $conf);
            # and remove lxc config
-           PVE::LXC::update_lxc_config(undef, $vmid, $conf);
+           PVE::LXC::update_lxc_config($vmid, $conf);
 
            return $rpcenv->fork_worker('vztemplate', $vmid, $authuser, $realcmd);
        };
@@ -1011,6 +1109,12 @@ __PACKAGE__->register_method({
                    "you clone a normal CT. For CT templates, we try to create a linked clone by default.",
                default => 0,
            },
+           experimental => {
+               type => 'boolean',
+               description => "The clone feature is experimental, set this " .
+                   "flag if you know what you are doing.",
+               default => 0,
+           },
 #          target => get_standard_option('pve-node', {
 #              description => "Target node. Only allowed if the original VM is on shared storage.",
 #              optional => 1,
@@ -1090,8 +1194,8 @@ __PACKAGE__->register_method({
 
                if (($opt eq 'rootfs') || ($opt =~ m/^mp\d+$/)) {
                    my $mp = $opt eq 'rootfs' ?
-                       PVE::LXC::parse_ct_rootfs($value) :
-                       PVE::LXC::parse_ct_mountpoint($value);
+                       PVE::LXC::Config->parse_ct_rootfs($value) :
+                       PVE::LXC::Config->parse_ct_mountpoint($value);
 
                    if ($mp->{type} eq 'volume') {
                        my $volid = $mp->{volume};
@@ -1155,7 +1259,7 @@ __PACKAGE__->register_method({
                            push @$newvollist, $newvolid;
                            $mp->{volume} = $newvolid;
 
-                           $newconf->{$opt} = PVE::LXC::print_ct_mountpoint($mp, $opt eq 'rootfs');
+                           $newconf->{$opt} = PVE::LXC::Config->print_ct_mountpoint($mp, $opt eq 'rootfs');
                            PVE::LXC::Config->write_config($newid, $newconf);
                        }
                    }
@@ -1208,7 +1312,7 @@ __PACKAGE__->register_method({
            disk => {
                type => 'string',
                description => "The disk you want to resize.",
-               enum => [PVE::LXC::mountpoint_names()],
+               enum => [PVE::LXC::Config->mountpoint_names()],
            },
            size => {
                type => 'string',
@@ -1261,8 +1365,8 @@ __PACKAGE__->register_method({
            my $running = PVE::LXC::check_running($vmid);
 
            my $disk = $param->{disk};
-           my $mp = $disk eq 'rootfs' ? PVE::LXC::parse_ct_rootfs($conf->{$disk}) :
-               PVE::LXC::parse_ct_mountpoint($conf->{$disk});
+           my $mp = $disk eq 'rootfs' ? PVE::LXC::Config->parse_ct_rootfs($conf->{$disk}) :
+               PVE::LXC::Config->parse_ct_mountpoint($conf->{$disk});
 
            my $volid = $mp->{volume};
 
@@ -1279,6 +1383,8 @@ __PACKAGE__->register_method({
 
            $rpcenv->check($authuser, "/storage/$storeid", ['Datastore.AllocateSpace']);
 
+           PVE::Storage::activate_volumes($storage_cfg, [$volid]);
+
            my $size = PVE::Storage::volume_size_info($storage_cfg, $volid, 5);
            $newsize += $size if $ext;
            $newsize = int($newsize);
@@ -1294,7 +1400,7 @@ __PACKAGE__->register_method({
                PVE::Storage::volume_resize($storage_cfg, $volid, $newsize, 0);
 
                $mp->{size} = $newsize;
-               $conf->{$disk} = PVE::LXC::print_ct_mountpoint($mp, $disk eq 'rootfs');
+               $conf->{$disk} = PVE::LXC::Config->print_ct_mountpoint($mp, $disk eq 'rootfs');
 
                PVE::LXC::Config->write_config($vmid, $conf);