On Tue, Sep 26, 2017 at 02:43:04PM +0200, Wolfgang Bumiller wrote: > --- > Changes: > * Instead of refusing moves when any snapshots exist, use the new > 'is_volume_in_use' variant to check whether the volume is actually > used in a snapshot, in which case we simply don't allow the --delete > parameter. > * Don't use both 'disk' and 'volume' in the parameter descriptions. > * - Let the config key be referred to as $mpkey instead of $volume, > - its parsed data s $mpdata > - the actual storage volumes only as $old_volid and $new_volid > instead of mixing $mp, $mp->{volume}, $volume, etc. in confusing > ways. > > src/PVE/API2/LXC.pm | 153 > ++++++++++++++++++++++++++++++++++++++++++++++++++++ > src/PVE/CLI/pct.pm | 1 + > 2 files changed, 154 insertions(+) > > diff --git a/src/PVE/API2/LXC.pm b/src/PVE/API2/LXC.pm > index ac3eefa..b2685ce 100644 > --- a/src/PVE/API2/LXC.pm > +++ b/src/PVE/API2/LXC.pm > @@ -1494,4 +1494,157 @@ __PACKAGE__->register_method({ > return PVE::LXC::Config->lock_config($vmid, $code);; > }}); > > +__PACKAGE__->register_method({ > + name => 'move_volume', > + path => '{vmid}/move_volume', > + method => 'POST', > + protected => 1, > + proxyto => 'node', > + description => "Move a rootfs-/mp-volume to a different storage", > + permissions => { > + description => "You need 'VM.Config.Disk' permissions on /vms/{vmid}, " > . > + "and 'Datastore.AllocateSpace' permissions on the storage.", > + check => > + [ 'and', > + ['perm', '/vms/{vmid}', [ 'VM.Config.Disk' ]], > + ['perm', '/storage/{storage}', [ 'Datastore.AllocateSpace' ]], > + ],
indentation seems wrong here. not sure whether I prefer the original one in qemu-server.. maybe check => [ 'and', ['perm', '/vms/{vmid}', [ 'VM.Config.Disk' ]], ['perm', '/storage/{storage}', [ 'Datastore.AllocateSpace' ]], ], ? > + }, > + parameters => { > + additionalProperties => 0, > + properties => { > + node => get_standard_option('pve-node'), > + vmid => get_standard_option('pve-vmid', { completion => > \&PVE::LXC::complete_ctid }), > + volume => { > + type => 'string', > + enum => [ PVE::LXC::Config->mountpoint_names() ], > + description => "Volume which will move.", volumes hopefully don't move on their own ;) "Moved volume" "Volume which will be moved" "Source volume" > + }, > + storage => get_standard_option('pve-storage-id', { > + description => "Target Storage.", > + completion => \&PVE::Storage::complete_storage_enabled, > + }), > + delete => { > + type => 'boolean', > + description => "Delete the original volume after successful > copy. By default the original is kept as an unused volume entry.", > + optional => 1, > + default => 0, > + }, > + digest => { > + type => 'string', > + description => 'Prevent changes if current configuration file > has different SHA1 digest. This can be used to prevent concurrent > modifications.', > + maxLength => 40, > + optional => 1, > + } > + }, > + }, > + returns => { > + type => 'string', > + }, > + code => sub { > + my ($param) = @_; > + > + my $rpcenv = PVE::RPCEnvironment::get(); > + > + my $authuser = $rpcenv->get_user(); > + > + my $node = extract_param($param, 'node'); > + > + my $vmid = extract_param($param, 'vmid'); > + > + my $storage = extract_param($param, 'storage'); > + > + my $mpkey = extract_param($param, 'volume'); > + > + my $delete = extract_param($param, 'delete'); > + > + my $digest = extract_param($param, 'digest'); why? you don't pass $param to anything (so no need to clean it up), and you don't even use all of the above variables (e.g., $node). I understand $mpkey, and $vmid and $storage can be argued because they are used often and benefit from a shorter name, but the rest seems a bit much. > + > + my $lockname = "move-disk"; > + > + my ($mpdata, $old_volid); > + > + PVE::LXC::Config->lock_config($vmid, sub { > + my $conf = PVE::LXC::Config->load_config($vmid); > + PVE::LXC::Config->check_lock($conf); > + > + die "cannot move volumes of a running container\n" if > PVE::LXC::check_running($vmid); > + > + if ($mpkey eq 'rootfs') { > + $mpdata = PVE::LXC::Config->parse_ct_rootfs($conf->{$mpkey}); > + } elsif ($mpkey =~ m/mp\d+/) { > + $mpdata = > PVE::LXC::Config->parse_ct_mountpoint($conf->{$mpkey}); > + } else { > + die "Can't parse $mpkey\n"; > + } > + $old_volid = $mpdata->{volume}; > + my ($old_storage, undef) = > PVE::Storage::parse_volume_id($old_volid); > + die "you can't move on the same storage\n" # ...with same format - > but we only have raw currently but this is not true - we do have the old-style subvol mps as well.. also, moving to a new volume on the same storage can make sense (think ZFS with changed compression/copies/deduplication/recordsize/.. properties) > + if $old_storage eq $storage; > + > + die "you can't move a volume with snapshots and delete the source\n" > + if $delete && PVE::LXC::Config->is_volume_in_use($conf, > $old_volid, 1, 1); > + > + PVE::Tools::assert_if_modified($digest, $conf->{digest}); > + > + PVE::LXC::Config->set_lock($vmid, $lockname); > + }); > + > + my $realcmd = sub { > + eval { > + PVE::Cluster::log_msg('info', $authuser, "move volume CT $vmid: > move --volume $mpkey --storage $storage"); > + > + my $conf = PVE::LXC::Config->load_config($vmid); > + my $storage_cfg = PVE::Storage::config(); > + > + my $new_volid; > + > + eval { > + PVE::Storage::activate_volumes($storage_cfg, [ $old_volid > ]); > + $new_volid = PVE::LXC::copy_volume($mpdata, $vmid, > $storage, $storage_cfg, $conf); > + $mpdata->{volume} = $new_volid; > + > + $conf->{$mpkey} = > PVE::LXC::Config->print_ct_mountpoint($mpdata, $mpkey eq 'rootfs'); > + > + PVE::LXC::Config->add_unused_volume($conf, $old_volid) if > !$delete; > + > + PVE::LXC::Config->write_config($vmid, $conf); IMHO we want to re-flock and re-check the (updated) digest or config lock before writing out the config here.. (copy_volume only reads the uid/guid maps from the config, so that should be okay). > + > + eval { > + # try to deactivate volumes - avoid lvm LVs to be > active on several nodes > + PVE::Storage::deactivate_volumes($storage_cfg, [ > $new_volid ]) > + }; > + warn $@ if $@; > + }; > + if (my $err = $@) { > + eval { > + PVE::Storage::vdisk_free($storage_cfg, $new_volid) > + if defined($new_volid); > + }; > + warn $@ if $@; shouldn't we remove the lock here if the cleanup was successful? > + die $err; > + } > + > + if ($delete) { > + eval { > + PVE::Storage::deactivate_volumes($storage_cfg, [ > $old_volid ]); > + PVE::Storage::vdisk_free($storage_cfg, $old_volid); > + }; > + warn $@ if $@; > + } > + }; > + my $err = $@; > + PVE::LXC::Config->remove_lock($vmid, $lockname); needs to be guarded by an eval, otherwise the actual error is masked by a potential error of remove_lock > + die $err if $err; > + }; > + my $task = eval { > + $rpcenv->fork_worker('move_volume', $vmid, $authuser, $realcmd); > + }; > + if (my $err = $@) { > + PVE::LXC::Config->remove_lock($vmid, $lockname); same as above! > + die $err; > + } > + return $task; > + }}); > + > 1; > diff --git a/src/PVE/CLI/pct.pm b/src/PVE/CLI/pct.pm > index 3253906..9bdefc6 100755 > --- a/src/PVE/CLI/pct.pm > +++ b/src/PVE/CLI/pct.pm > @@ -759,6 +759,7 @@ our $cmddef = { > > clone => [ "PVE::API2::LXC", 'clone_vm', ['vmid', 'newid'], { node => > $nodename }, $upid_exit ], > migrate => [ "PVE::API2::LXC", 'migrate_vm', ['vmid', 'target'], { node > => $nodename }, $upid_exit], > + move_volume => [ "PVE::API2::LXC", 'move_volume', ['vmid', 'volume', > 'storage'], { node => $nodename }, $upid_exit ], > > status => [ __PACKAGE__, 'status', ['vmid']], > console => [ __PACKAGE__, 'console', ['vmid']], > -- > 2.11.0 > > > _______________________________________________ > pve-devel mailing list > pve-devel@pve.proxmox.com > https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel