5
0
mirror of git://git.proxmox.com/git/pve-storage.git synced 2024-12-22 13:34:16 +03:00

add disk rename feature

Functionality has been added for the following storage types:

* directory ones, based on the default implementation:
    * directory
    * NFS
    * CIFS
    * gluster
* ZFS
* (thin) LVM
* Ceph

A new feature `rename` has been introduced to mark which storage
plugin supports the feature.

Version API and AGE have been bumped.

Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>

the intention of this feature is to support the following use-cases:
- reassign a volume from one owning guest to another (which usually
  entails a rename, since the owning vmid is encoded in the volume name)
- rename a volume (e.g., to use a more meaningful name instead of the
  auto-assigned ...-disk-123)

only the former is implemented at the caller side in
qemu-server/pve-container for now, but since the lower-level feature is
basically the same for both, we can take advantage of the storage plugin
API bump now to get the building block for this future feature in place
already.

adapt ApiChangelog change to fix conflicts and added more detail above

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
Aaron Lauterer 2021-11-09 15:55:32 +01:00 committed by Fabian Grünbichler
parent 93fbc01963
commit 95dfa44ca1
7 changed files with 164 additions and 0 deletions

View File

@ -8,6 +8,11 @@ Future changes should be documented in here.
## Version 10:
* a new `rename_volume` method has been added
Storage plugins with rename support need to enable
the `rename` feature flag; e.g. in the `volume_has_feature` method.
* Replace `volume_snapshot_list` with `volume_snapshot_info`:
`volume_snapshot_list` was used exclusively by replication and currently, replication is only

View File

@ -349,6 +349,7 @@ sub volume_snapshot_needs_fsfreeze {
# snapshot - taking a snapshot is possible
# sparseinit - volume is sparsely initialized
# template - conversion to base image is possible
# rename - renaming volumes is possible
# $snap - check if the feature is supported for a given snapshot
# $running - if the guest owning the volume is running
# $opts - hash with further options:
@ -1860,6 +1861,26 @@ sub complete_volume {
return $res;
}
sub rename_volume {
my ($cfg, $source_volid, $target_vmid, $target_volname) = @_;
die "no source volid provided\n" if !$source_volid;
die "no target VMID or target volname provided\n" if !$target_vmid && !$target_volname;
my ($storeid, $source_volname) = parse_volume_id($source_volid);
activate_storage($cfg, $storeid);
my $scfg = storage_config($cfg, $storeid);
my $plugin = PVE::Storage::Plugin->lookup($scfg->{type});
$target_vmid = ($plugin->parse_volname($source_volname))[3] if !$target_vmid;
return $plugin->cluster_lock_storage($storeid, $scfg->{shared}, undef, sub {
return $plugin->rename_volume($scfg, $storeid, $source_volname, $target_vmid, $target_volname);
});
}
# Various io-heavy operations require io/bandwidth limits which can be
# configured on multiple levels: The global defaults in datacenter.cfg, and
# per-storage overrides. When we want to do a restore from storage A to storage

View File

@ -339,6 +339,15 @@ sub lvcreate {
run_command($cmd, errmsg => "lvcreate '$vg/$name' error");
}
sub lvrename {
my ($vg, $oldname, $newname) = @_;
run_command(
['/sbin/lvrename', $vg, $oldname, $newname],
errmsg => "lvrename '${vg}/${oldname}' to '${newname}' error",
);
}
sub alloc_image {
my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_;
@ -584,6 +593,7 @@ sub volume_has_feature {
my $features = {
copy => { base => 1, current => 1},
rename => {current => 1},
};
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
@ -692,4 +702,28 @@ sub volume_import_write {
input => '<&'.fileno($input_fh));
}
sub rename_volume {
my ($class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname) = @_;
my (
undef,
$source_image,
$source_vmid,
$base_name,
$base_vmid,
undef,
$format
) = $class->parse_volname($source_volname);
$target_volname = $class->find_free_diskname($storeid, $scfg, $target_vmid, $format)
if !$target_volname;
my $vg = $scfg->{vgname};
my $lvs = lvm_list_volumes($vg);
die "target volume '${target_volname}' already exists\n"
if ($lvs->{$vg}->{$target_volname});
lvrename($vg, $source_volname, $target_volname);
return "${storeid}:${target_volname}";
}
1;

View File

@ -355,6 +355,7 @@ sub volume_has_feature {
template => { current => 1},
copy => { base => 1, current => 1, snap => 1},
sparseinit => { base => 1, current => 1},
rename => {current => 1},
};
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =

View File

@ -1049,6 +1049,7 @@ sub volume_has_feature {
snap => {qcow2 => 1} },
sparseinit => { base => {qcow2 => 1, raw => 1, vmdk => 1},
current => {qcow2 => 1, raw => 1, vmdk => 1} },
rename => { current => {qcow2 => 1, raw => 1, vmdk => 1} },
};
# clone_image creates a qcow2 volume
@ -1056,6 +1057,8 @@ sub volume_has_feature {
defined($opts->{valid_target_formats}) &&
!(grep { $_ eq 'qcow2' } @{$opts->{valid_target_formats}});
return 0 if $feature eq 'rename' && $class->can('api') && $class->api() < 10;
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
$class->parse_volname($volname);
@ -1578,4 +1581,39 @@ sub volume_import_formats {
return ();
}
sub rename_volume {
my ($class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname) = @_;
die "not implemented in storage plugin '$class'\n" if $class->can('api') && $class->api() < 10;
die "no path found\n" if !$scfg->{path};
my (
undef,
$source_image,
$source_vmid,
$base_name,
$base_vmid,
undef,
$format
) = $class->parse_volname($source_volname);
$target_volname = $class->find_free_diskname($storeid, $scfg, $target_vmid, $format, 1)
if !$target_volname;
my $basedir = $class->get_subdir($scfg, 'images');
mkpath "${basedir}/${target_vmid}";
my $old_path = "${basedir}/${source_vmid}/${source_image}";
my $new_path = "${basedir}/${target_vmid}/${target_volname}";
die "target volume '${target_volname}' already exists\n" if -e $new_path;
my $base = $base_name ? "${base_vmid}/${base_name}/" : '';
rename($old_path, $new_path) ||
die "rename '$old_path' to '$new_path' failed - $!\n";
return "${storeid}:${base}${target_vmid}/${target_volname}";
}
1;

View File

@ -742,6 +742,7 @@ sub volume_has_feature {
template => { current => 1},
copy => { base => 1, current => 1, snap => 1},
sparseinit => { base => 1, current => 1},
rename => {current => 1},
};
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) = $class->parse_volname($volname);
@ -757,4 +758,37 @@ sub volume_has_feature {
return undef;
}
sub rename_volume {
my ($class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname) = @_;
my (
undef,
$source_image,
$source_vmid,
$base_name,
$base_vmid,
undef,
$format
) = $class->parse_volname($source_volname);
$target_volname = $class->find_free_diskname($storeid, $scfg, $target_vmid, $format)
if !$target_volname;
eval {
my $cmd = $rbd_cmd->($scfg, $storeid, 'info', $target_volname);
run_rbd_command($cmd, errmsg => "exist check", quiet => 1);
};
die "target volume '${target_volname}' already exists\n" if !$@;
my $cmd = $rbd_cmd->($scfg, $storeid, 'rename', $source_image, $target_volname);
run_rbd_command(
$cmd,
errmsg => "could not rename image '${source_image}' to '${target_volname}'",
);
$base_name = $base_name ? "${base_name}/" : '';
return "${storeid}:${base_name}${target_volname}";
}
1;

View File

@ -696,6 +696,7 @@ sub volume_has_feature {
copy => { base => 1, current => 1},
sparseinit => { base => 1, current => 1},
replicate => { base => 1, current => 1},
rename => {current => 1},
};
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase) =
@ -798,4 +799,34 @@ sub volume_import_formats {
return $class->volume_export_formats($scfg, $storeid, $volname, $snapshot, $base_snapshot, $with_snapshots);
}
sub rename_volume {
my ($class, $scfg, $storeid, $source_volname, $target_vmid, $target_volname) = @_;
my (
undef,
$source_image,
$source_vmid,
$base_name,
$base_vmid,
undef,
$format
) = $class->parse_volname($source_volname);
$target_volname = $class->find_free_diskname($storeid, $scfg, $target_vmid, $format)
if !$target_volname;
my $pool = $scfg->{pool};
my $source_zfspath = "${pool}/${source_image}";
my $target_zfspath = "${pool}/${target_volname}";
my $exists = 0 == run_command(['zfs', 'get', '-H', 'name', $target_zfspath],
noerr => 1, quiet => 1);
die "target volume '${target_volname}' already exists\n" if $exists;
$class->zfs_request($scfg, 5, 'rename', ${source_zfspath}, ${target_zfspath});
$base_name = $base_name ? "${base_name}/" : '';
return "${storeid}:${base_name}${target_volname}";
}
1;