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:
committed by
Fabian Grünbichler
parent
93fbc01963
commit
95dfa44ca1
@ -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;
|
||||
|
||||
@ -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) =
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
Reference in New Issue
Block a user