]> git.proxmox.com Git - pve-container.git/blobdiff - src/PVE/API2/LXC.pm
prefer storage_check_enabled over storage_check_node
[pve-container.git] / src / PVE / API2 / LXC.pm
index 8ce462fc7f4d3f304eafd5d6b5c3d8faab93746a..b929481e3a0847b46d045388fc43142fa4896c9e 100644 (file)
@@ -275,7 +275,7 @@ __PACKAGE__->register_method({
        my $check_and_activate_storage = sub {
            my ($sid) = @_;
 
-           my $scfg = PVE::Storage::storage_check_node($storage_cfg, $sid, $node);
+           my $scfg = PVE::Storage::storage_check_enabled($storage_cfg, $sid, $node);
 
            raise_param_exc({ storage => "storage '$sid' does not support container directories"})
                if !$scfg->{content}->{rootdir};
@@ -352,16 +352,21 @@ __PACKAGE__->register_method({
                my $orig_mp_param; # only used if $restore
                if ($restore) {
                    die "can't overwrite running container\n" if PVE::LXC::check_running($vmid);
-                   if ($is_root && $archive ne '-') {
+                   if ($archive ne '-') {
                        my $orig_conf;
                        print "recovering backed-up configuration from '$archive'\n";
                        ($orig_conf, $orig_mp_param) = PVE::LXC::Create::recover_config($storage_cfg, $archive, $vmid);
+
                        $was_template = delete $orig_conf->{template};
+
                        # When we're root call 'restore_configuration' with restricted=0,
                        # causing it to restore the raw lxc entries, among which there may be
                        # 'lxc.idmap' entries. We need to make sure that the extracted contents
                        # of the container match up with the restored configuration afterwards:
-                       $conf->{lxc} = $orig_conf->{lxc};
+                       $conf->{lxc} = $orig_conf->{lxc} if $is_root;
+
+                       $conf->{unprivileged} = $orig_conf->{unprivileged}
+                           if !defined($unprivileged) && defined($orig_conf->{unprivileged});
                    }
                }
                if ($storage_only_mode) {
@@ -1071,12 +1076,6 @@ __PACKAGE__->register_method({
                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,
-           },
            bwlimit => {
                description => "Override I/O bandwidth limit (in KiB/s).",
                optional => 1,
@@ -1354,30 +1353,21 @@ __PACKAGE__->register_method({
        my ($param) = @_;
 
        my $rpcenv = PVE::RPCEnvironment::get();
-
-        my $authuser = $rpcenv->get_user();
+       my $authuser = $rpcenv->get_user();
 
        my $node = extract_param($param, 'node');
-
        my $vmid = extract_param($param, 'vmid');
-
        my $newid = extract_param($param, 'newid');
-
        my $pool = extract_param($param, 'pool');
-
        if (defined($pool)) {
            $rpcenv->check_pool_exist($pool);
        }
-
        my $snapname = extract_param($param, 'snapname');
-
        my $storage = extract_param($param, 'storage');
-
        my $target = extract_param($param, 'target');
-
         my $localnode = PVE::INotify::nodename();
 
-        undef $target if $target && ($target eq $localnode || $target eq 'localhost');
+       undef $target if $target && ($target eq $localnode || $target eq 'localhost');
 
        PVE::Cluster::check_node_exists($target) if $target;
 
@@ -1388,7 +1378,7 @@ __PACKAGE__->register_method({
            PVE::Storage::storage_check_enabled($storecfg, $storage);
            if ($target) {
                # check if storage is available on target node
-               PVE::Storage::storage_check_node($storecfg, $storage, $target);
+               PVE::Storage::storage_check_enabled($storecfg, $storage, $target);
                # clone only works if target storage is shared
                my $scfg = PVE::Storage::storage_config($storecfg, $storage);
                die "can't clone to non-shared storage '$storage'\n" if !$scfg->{shared};
@@ -1397,118 +1387,134 @@ __PACKAGE__->register_method({
 
        PVE::Cluster::check_cfs_quorum();
 
-       my $conffile;
        my $newconf = {};
        my $mountpoints = {};
        my $fullclone = {};
        my $vollist = [];
        my $running;
 
-       PVE::LXC::Config->lock_config($vmid, sub {
-           my $src_conf = PVE::LXC::Config->set_lock($vmid, 'disk');
+       PVE::LXC::Config->create_and_lock_config($newid, 0);
+       PVE::Firewall::clone_vmfw_conf($vmid, $newid);
 
-           $running = PVE::LXC::check_running($vmid) || 0;
+       my $lock_and_reload = sub {
+           my ($vmid, $code) = @_;
+           return PVE::LXC::Config->lock_config($vmid, sub {
+               my $conf = PVE::LXC::Config->load_config($vmid);
+               die "Lost 'create' config lock, aborting.\n"
+                   if !PVE::LXC::Config->has_lock($conf, 'create');
 
-           my $full = extract_param($param, 'full');
-           if (!defined($full)) {
-               $full = !PVE::LXC::Config->is_template($src_conf);
-           }
-           die "parameter 'storage' not allowed for linked clones\n" if defined($storage) && !$full;
+               return $code->($conf);
+           });
+       };
 
-           eval {
-               die "snapshot '$snapname' does not exist\n"
-                   if $snapname && !defined($src_conf->{snapshots}->{$snapname});
+       my $src_conf = PVE::LXC::Config->set_lock($vmid, 'disk');
 
+       $running = PVE::LXC::check_running($vmid) || 0;
 
-               my $src_conf = $snapname ? $src_conf->{snapshots}->{$snapname} : $src_conf;
+       my $full = extract_param($param, 'full');
+       if (!defined($full)) {
+           $full = !PVE::LXC::Config->is_template($src_conf);
+       }
 
-               $conffile = PVE::LXC::Config->config_file($newid);
-               die "unable to create CT $newid: config file already exists\n"
-                   if -f $conffile;
+       eval {
+           die "parameter 'storage' not allowed for linked clones\n"
+               if defined($storage) && !$full;
 
-               my $sharedvm = 1;
-               foreach my $opt (keys %$src_conf) {
-                   next if $opt =~ m/^unused\d+$/;
+           die "snapshot '$snapname' does not exist\n"
+               if $snapname && !defined($src_conf->{snapshots}->{$snapname});
 
-                   my $value = $src_conf->{$opt};
+           my $src_conf = $snapname ? $src_conf->{snapshots}->{$snapname} : $src_conf;
 
-                   if (($opt eq 'rootfs') || ($opt =~ m/^mp\d+$/)) {
-                       my $mp = PVE::LXC::Config->parse_volume($opt, $value);
+           my $sharedvm = 1;
+           for my $opt (sort keys %$src_conf) {
+               next if $opt =~ m/^unused\d+$/;
 
-                       if ($mp->{type} eq 'volume') {
-                           my $volid = $mp->{volume};
+               my $value = $src_conf->{$opt};
 
-                           my ($sid, $volname) = PVE::Storage::parse_volume_id($volid);
-                           $sid = $storage if defined($storage);
-                           my $scfg = PVE::Storage::storage_config($storecfg, $sid);
-                           if (!$scfg->{shared}) {
-                               $sharedvm = 0;
-                               warn "found non-shared volume: $volid\n" if $target;
-                           }
+               if (($opt eq 'rootfs') || ($opt =~ m/^mp\d+$/)) {
+                   my $mp = PVE::LXC::Config->parse_volume($opt, $value);
 
-                           $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
+                   if ($mp->{type} eq 'volume') {
+                       my $volid = $mp->{volume};
 
-                           if ($full) {
-                               die "Cannot do full clones on a running container without snapshots\n"
-                                   if $running && !defined($snapname);
-                               $fullclone->{$opt} = 1;
-                           } else {
-                               # not full means clone instead of copy
-                               die "Linked clone feature for '$volid' is not available\n"
-                                   if !PVE::Storage::volume_has_feature($storecfg, 'clone', $volid, $snapname, $running, {'valid_target_formats' => ['raw', 'subvol']});
-                           }
+                       my ($sid, $volname) = PVE::Storage::parse_volume_id($volid);
+                       $sid = $storage if defined($storage);
+                       my $scfg = PVE::Storage::storage_config($storecfg, $sid);
+                       if (!$scfg->{shared}) {
+                           $sharedvm = 0;
+                           warn "found non-shared volume: $volid\n" if $target;
+                       }
 
-                           $mountpoints->{$opt} = $mp;
-                           push @$vollist, $volid;
+                       $rpcenv->check($authuser, "/storage/$sid", ['Datastore.AllocateSpace']);
 
+                       if ($full) {
+                           die "Cannot do full clones on a running container without snapshots\n"
+                               if $running && !defined($snapname);
+                           $fullclone->{$opt} = 1;
                        } else {
-                           # TODO: allow bind mounts?
-                           die "unable to clone mountpint '$opt' (type $mp->{type})\n";
+                           # not full means clone instead of copy
+                           die "Linked clone feature for '$volid' is not available\n"
+                               if !PVE::Storage::volume_has_feature($storecfg, 'clone', $volid, $snapname, $running, {'valid_target_formats' => ['raw', 'subvol']});
                        }
-                   } elsif ($opt =~ m/^net(\d+)$/) {
-                       # always change MAC! address
-                       my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
-                       my $net = PVE::LXC::Config->parse_lxc_network($value);
-                       $net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
-                       $newconf->{$opt} = PVE::LXC::Config->print_lxc_network($net);
+
+                       $mountpoints->{$opt} = $mp;
+                       push @$vollist, $volid;
+
                    } else {
-                       # copy everything else
-                       $newconf->{$opt} = $value;
+                       # TODO: allow bind mounts?
+                       die "unable to clone mountpoint '$opt' (type $mp->{type})\n";
                    }
+               } elsif ($opt =~ m/^net(\d+)$/) {
+                   # always change MAC! address
+                   my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
+                   my $net = PVE::LXC::Config->parse_lxc_network($value);
+                   $net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
+                   $newconf->{$opt} = PVE::LXC::Config->print_lxc_network($net);
+               } else {
+                   # copy everything else
+                   $newconf->{$opt} = $value;
                }
-               die "can't clone CT to node '$target' (CT uses local storage)\n"
-                   if $target && !$sharedvm;
+           }
+           die "can't clone CT to node '$target' (CT uses local storage)\n"
+               if $target && !$sharedvm;
 
-               # Replace the 'disk' lock with a 'create' lock.
-               $newconf->{lock} = 'create';
+           # Replace the 'disk' lock with a 'create' lock.
+           $newconf->{lock} = 'create';
 
-               delete $newconf->{snapshots};
-               delete $newconf->{pending};
-               delete $newconf->{template};
-               if ($param->{hostname}) {
-                   $newconf->{hostname} = $param->{hostname};
-               }
+           delete $newconf->{snapshots};
+           delete $newconf->{pending};
+           delete $newconf->{template};
+           if ($param->{hostname}) {
+               $newconf->{hostname} = $param->{hostname};
+           }
 
-               if ($param->{description}) {
-                   $newconf->{description} = $param->{description};
-               }
+           if ($param->{description}) {
+               $newconf->{description} = $param->{description};
+           }
 
-               # create empty/temp config - this fails if CT already exists on other node
+           $lock_and_reload->($newid, sub {
                PVE::LXC::Config->write_config($newid, $newconf);
+           });
+       };
+       if (my $err = $@) {
+           eval { PVE::LXC::Config->remove_lock($vmid, 'disk') };
+           warn "Failed to remove source CT config lock - $@\n" if $@;
+
+           eval {
+               $lock_and_reload->($newid, sub {
+                   PVE::LXC::Config->destroy_config($newid);
+                   PVE::Firewall::remove_vmfw_conf($newid);
+               });
            };
-           if (my $err = $@) {
-               eval { PVE::LXC::Config->remove_lock($vmid, 'disk') };
-               warn $@ if $@;
-               die $err;
-           }
-       });
+           warn "Failed to remove target CT config - $@\n" if $@;
+
+           die $err;
+       }
 
        my $update_conf = sub {
            my ($key, $value) = @_;
-           return PVE::LXC::Config->lock_config($newid, sub {
-               my $conf = PVE::LXC::Config->load_config($newid);
-               die "Lost 'create' config lock, aborting.\n"
-                   if !PVE::LXC::Config->has_lock($conf, 'create');
+           return $lock_and_reload->($newid, sub {
+               my $conf = shift;
                $conf->{$key} = $value;
                PVE::LXC::Config->write_config($newid, $conf);
            });
@@ -1554,41 +1560,64 @@ __PACKAGE__->register_method({
                }
 
                PVE::AccessControl::add_vm_to_pool($newid, $pool) if $pool;
-               PVE::LXC::Config->remove_lock($newid, 'create');
-
-               if ($target) {
-                   # always deactivate volumes - avoid lvm LVs to be active on several nodes
-                   PVE::Storage::deactivate_volumes($storecfg, $vollist, $snapname) if !$running;
-                   PVE::Storage::deactivate_volumes($storecfg, $newvollist);
 
-                   my $newconffile = PVE::LXC::Config->config_file($newid, $target);
-                   die "Failed to move config to node '$target' - rename failed: $!\n"
-                       if !rename($conffile, $newconffile);
-               }
+               $lock_and_reload->($newid, sub {
+                   my $conf = shift;
+                   my $rootdir = PVE::LXC::mount_all($newid, $storecfg, $conf, 1);
+                   eval {
+                       my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
+                       $lxc_setup->post_clone_hook($conf);
+                   };
+                   my $err = $@;
+                   eval { PVE::LXC::umount_all($newid, $storecfg, $conf, 1); };
+                   if ($err) {
+                       warn "$@\n" if $@;
+                       die $err;
+                   } else {
+                       die $@ if $@;
+                   }
+               });
            };
            my $err = $@;
-
            # Unlock the source config in any case:
            eval { PVE::LXC::Config->remove_lock($vmid, 'disk') };
            warn $@ if $@;
 
            if ($err) {
                # Now cleanup the config & disks:
-               unlink $conffile;
-
                sleep 1; # some storages like rbd need to wait before release volume - really?
 
                foreach my $volid (@$newvollist) {
                    eval { PVE::Storage::vdisk_free($storecfg, $volid); };
                    warn $@ if $@;
                }
+
+               eval {
+                   $lock_and_reload->($newid, sub {
+                       PVE::LXC::Config->destroy_config($newid);
+                       PVE::Firewall::remove_vmfw_conf($newid);
+                   });
+               };
+               warn "Failed to remove target CT config - $@\n" if $@;
+
                die "clone failed: $err";
            }
 
+           $lock_and_reload->($newid, sub {
+               PVE::LXC::Config->remove_lock($newid, 'create');
+
+               if ($target) {
+                   # always deactivate volumes - avoid lvm LVs to be active on several nodes
+                   PVE::Storage::deactivate_volumes($storecfg, $vollist, $snapname) if !$running;
+                   PVE::Storage::deactivate_volumes($storecfg, $newvollist);
+
+                   PVE::LXC::Config->move_config_to_node($newid, $target);
+               }
+           });
+
            return;
        };
 
-       PVE::Firewall::clone_vmfw_conf($vmid, $newid);
        return $rpcenv->fork_worker('vzclone', $vmid, $authuser, $realcmd);
     }});