]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/API2/LXC.pm
use new PVE::Storage::check_volume_access()
[pve-container.git] / src / PVE / API2 / LXC.pm
index 55d4f4a0dc8a8d15534f4b248a9c68ccfa9752da..0004c97653919c2496b8442d4005edf15fe67181 100644 (file)
@@ -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)) {
@@ -209,13 +218,13 @@ __PACKAGE__->register_method({
            raise_perm_exc();
        }
 
-       PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, $param, []);
-
+       my $ostemplate = extract_param($param, 'ostemplate');
        my $storage = extract_param($param, 'storage') // 'local';
 
+       PVE::LXC::check_ct_modify_config_perm($rpcenv, $authuser, $vmid, $pool, $param, []);
+
        my $storage_cfg = cfs_read_file("storage.cfg");
 
-       my $ostemplate = extract_param($param, 'ostemplate');
 
        my $archive;
 
@@ -227,7 +236,7 @@ __PACKAGE__->register_method({
            $archive = '-';
            die "restore from pipe requires rootfs parameter\n" if !defined($param->{rootfs});
        } else {
-           $rpcenv->check_volume_access($authuser, $storage_cfg, $vmid, $ostemplate);
+           PVE::Storage::check_volume_access($rpcenv, $authuser, $storage_cfg, $vmid, $ostemplate);
            $archive = PVE::Storage::abs_filesystem_path($storage_cfg, $ostemplate);
        }
 
@@ -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 "mount points configured, but 'rootfs' not set - aborting\n"
+           if !$storage_only_mode && !defined($mp_param->{rootfs});
+
        # check storage access, activate storage
-       PVE::LXC::Config->foreach_mountpoint($param, sub {
+       my $delayed_mp_param = {};
+       PVE::LXC::Config->foreach_mountpoint($mp_param, sub {
            my ($ms, $mountpoint) = @_;
 
            my $volid = $mountpoint->{volume};
@@ -274,7 +297,7 @@ __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::Config->update_pct_config($vmid, $conf, 0, $no_disk_param);
 
@@ -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 mount point $ms ('$mountpoint->{mp}')\n";
+                                   warn "mount point 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);
 
-               PVE::LXC::Create::create_rootfs($storage_cfg, $vmid, $conf, $archive, $password, $restore, $ignore_unpack_errors);
+               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);
+
+                   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,23 @@ __PACKAGE__->register_method({
                description => "Use online/live migration.",
                optional => 1,
            },
+           restart => {
+               type => 'boolean',
+               description => "Use restart migration",
+               optional => 1,
+           },
+           timeout => {
+               type => 'integer',
+               description => "Timeout in seconds for shutdown for restart migration",
+               optional => 1,
+               default => 180,
+           },
+           force => {
+               type => 'boolean',
+               description => "Force migration despite local bind / device" .
+                   " mounts. NOTE: deprecated, use 'shared' property of mount point instead.",
+               optional => 1,
+           },
        },
     },
     returns => {
@@ -790,8 +893,8 @@ __PACKAGE__->register_method({
 
        # try to detect errors early
        if (PVE::LXC::check_running($vmid)) {
-           die "can't migrate running container without --online\n"
-               if !$param->{online};
+           die "can't migrate running container without --online or --restart\n"
+               if !$param->{online} && !$param->{restart};
        }
 
        if (PVE::HA::Config::vm_is_ha_managed($vmid) && $rpcenv->{type} ne 'ha') {
@@ -948,7 +1051,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);
        };
@@ -1162,7 +1265,7 @@ __PACKAGE__->register_method({
                        if ($fullclone->{$opt}) {
                            die "fixme: full clone not implemented\n";
                        } else {
-                           print "create linked clone of mountpoint $opt ($volid)\n";
+                           print "create linked clone of mount point $opt ($volid)\n";
                            my $newvolid = PVE::Storage::vdisk_clone($storecfg, $volid, $newid, $snapname);
                            push @$newvollist, $newvolid;
                            $mp->{volume} = $newvolid;
@@ -1208,7 +1311,7 @@ __PACKAGE__->register_method({
     method => 'PUT',
     protected => 1,
     proxyto => 'node',
-    description => "Resize a container mountpoint.",
+    description => "Resize a container mount point.",
     permissions => {
        check => ['perm', '/vms/{vmid}', ['VM.Config.Disk'], any => 1],
     },
@@ -1281,7 +1384,7 @@ __PACKAGE__->register_method({
            my (undef, undef, $owner, undef, undef, undef, $format) =
                PVE::Storage::parse_volname($storage_cfg, $volid);
 
-           die "can't resize mountpoint owned by another container ($owner)"
+           die "can't resize mount point owned by another container ($owner)"
                if $vmid != $owner;
 
            die "can't resize volume: $disk if snapshot exists\n"
@@ -1291,6 +1394,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);
@@ -1317,7 +1422,7 @@ __PACKAGE__->register_method({
                        $mp->{mp} = '/';
                        my $use_loopdev = (PVE::LXC::mountpoint_mount_path($mp, $storage_cfg))[1];
                        $path = PVE::LXC::query_loopdev($path) if $use_loopdev;
-                       die "internal error: CT running but mountpoint not attached to a loop device"
+                       die "internal error: CT running but mount point not attached to a loop device"
                            if !$path;
                        PVE::Tools::run_command(['losetup', '--set-capacity', $path]) if $use_loopdev;