[pve-devel] [PATCH storage 2/3] fix #6338: rbd: use image-/snap-spec consistently

Fabian Grünbichler f.gruenbichler at proxmox.com
Wed Apr 23 15:59:03 CEST 2025


this is the recommended way upstream to pass this information, and all commands
(other than those not operating on images/snapshots) consume them (rbd(8)):

>       image-spec      is [pool-name/[namespace-name/]]image-name
>       snap-spec       is [pool-name/[namespace-name/]]image-name at snap-name
> [..]
> You may specify each name individually, using --pool, --namespace, --image,
> and --snap options, but this is discouraged in favor of the above spec
> syntax.

We already had a few places calling `get_rbd_path` previously (which meant
passing the pool and namespace information twice to Ceph).

It also allows us to replace the custom import and unmap handling with just
custom `ls` handling, reducing the workarounds in rbd_cmd.

Fixes: #6338 , because `rbd import` doesn't handle a separate `--namespace ..`
at all..

Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---
we could probably switch some more only-technically-public subs over to
take a spec instead of image name or image + snapshot name, if desired..

alternatively, we could also just special case the import call, and/or convince
upstream to implement --namespace handling there as well or send a patch doing
that their way..

 src/PVE/Storage/RBDPlugin.pm | 133 ++++++++++++++++++-----------------
 1 file changed, 69 insertions(+), 64 deletions(-)

diff --git a/src/PVE/Storage/RBDPlugin.pm b/src/PVE/Storage/RBDPlugin.pm
index 8c67a37..3bb5807 100644
--- a/src/PVE/Storage/RBDPlugin.pm
+++ b/src/PVE/Storage/RBDPlugin.pm
@@ -89,22 +89,17 @@ my $rbd_cmd = sub {
     my ($scfg, $storeid, $op, @options) = @_;
 
     my $cmd_option = PVE::CephConfig::ceph_connect_option($scfg, $storeid);
-    my $pool =  $scfg->{pool} ? $scfg->{pool} : 'rbd';
 
     my $cmd = ['/usr/bin/rbd'];
-    if ($op eq 'import') {
-	push $cmd->@*, '--dest-pool', $pool;
-    } else {
+    # `ls` doesn't take an image-spec, otherwise pool and namespace should be specified there
+    if ($op eq 'ls') {
+	my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
 	push $cmd->@*, '-p', $pool;
+	if (defined($scfg->{namespace})) {
+	    push @$cmd, '--namespace', $cfg->{namespace};
+	}
     }
 
-    if (defined(my $namespace = $scfg->{namespace})) {
-	# some subcommands will fail if the --namespace parameter is present
-	my $no_namespace_parameter = {
-	    unmap => 1,
-	};
-	push @$cmd, '--namespace', "$namespace" if !$no_namespace_parameter->{$op};
-    }
     push @$cmd, '-c', $cmd_option->{ceph_conf} if ($cmd_option->{ceph_conf});
     push @$cmd, '-m', $cmd_option->{mon_host} if ($cmd_option->{mon_host});
     push @$cmd, '--auth_supported', $cmd_option->{auth_supported} if ($cmd_option->{auth_supported});
@@ -144,9 +139,11 @@ my $krbd_feature_update = sub {
     my $to_disable = join(',', grep {  $active_features->{$_} } @disable);
     my $to_enable  = join(',', grep { !$active_features->{$_} } @enable );
 
+    my $image_spec = get_rbd_path($scfg, $name);
+
     if ($to_disable) {
 	print "disable RBD image features this kernel RBD drivers is not compatible with: $to_disable\n";
-	my $cmd = $rbd_cmd->($scfg, $storeid, 'feature', 'disable', $name, $to_disable);
+	my $cmd = $rbd_cmd->($scfg, $storeid, 'feature', 'disable', $image_spec, $to_disable);
 	run_rbd_command(
 	    $cmd,
 	    errmsg => "could not disable krbd-incompatible image features '$to_disable' for rbd image: $name",
@@ -155,7 +152,7 @@ my $krbd_feature_update = sub {
     if ($to_enable) {
 	print "enable RBD image features this kernel RBD drivers supports: $to_enable\n";
 	eval {
-	    my $cmd = $rbd_cmd->($scfg, $storeid, 'feature', 'enable', $name, $to_enable);
+	    my $cmd = $rbd_cmd->($scfg, $storeid, 'feature', 'enable', $image_spec, $to_enable);
 	    run_rbd_command(
 		$cmd,
 		errmsg => "could not enable krbd-compatible image features '$to_enable' for rbd image: $name",
@@ -234,9 +231,9 @@ sub rbd_ls {
 }
 
 sub rbd_ls_snap {
-    my ($scfg, $storeid, $name) = @_;
+    my ($scfg, $storeid, $image_spec) = @_;
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'ls', $name, '--format', 'json');
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'ls', $image_spec, '--format', 'json');
 
     my $raw = '';
     run_rbd_command($cmd, errmsg => "rbd error", errfunc => sub {}, outfunc => sub { $raw .= shift; });
@@ -244,9 +241,9 @@ sub rbd_ls_snap {
     my $list;
     if ($raw =~ m/^(\[.*\])$/s) { # untaint
 	$list = eval { JSON::decode_json($1) };
-	die "invalid JSON output from 'rbd snap ls $name': $@\n" if $@;
+	die "invalid JSON output from 'rbd snap ls $image_spec': $@\n" if $@;
     } else {
-	die "got unexpected data from 'rbd snap ls $name': '$raw'\n";
+	die "got unexpected data from 'rbd snap ls $image_spec': '$raw'\n";
     }
 
     $list = [] if !defined($list);
@@ -269,11 +266,9 @@ sub rbd_volume_info {
     my ($scfg, $storeid, $volname, $snap) = @_;
 
     my $cmd = undef;
+    my $spec = get_rbd_path($scfg, $volname, $snap);
 
-    my @options = ('info', $volname, '--format', 'json');
-    if ($snap) {
-	push @options, '--snap', $snap;
-    }
+    my @options = ('info', $spec, '--format', 'json');
 
     $cmd = $rbd_cmd->($scfg, $storeid, @options);
 
@@ -300,7 +295,8 @@ sub rbd_volume_info {
 sub rbd_volume_du {
     my ($scfg, $storeid, $volname) = @_;
 
-    my @options = ('du', $volname, '--format', 'json');
+    my $spec = get_rbd_path($scfg, $volname);
+    my @options = ('du', $spec, '--format', 'json');
     my $cmd = $rbd_cmd->($scfg, $storeid, @options);
 
     my $raw = '';
@@ -471,14 +467,13 @@ sub path {
 
     my $cmd_option = PVE::CephConfig::ceph_connect_option($scfg, $storeid);
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
-    $name .= '@'.$snapname if $snapname;
 
     if ($scfg->{krbd}) {
-	my $rbd_dev_path = get_rbd_dev_path($scfg, $storeid, $name);
+	my $rbd_dev_path = get_rbd_dev_path($scfg, $storeid, $name, $snapname);
 	return ($rbd_dev_path, $vmid, $vtype);
     }
 
-    my $rbd_path = get_rbd_path($scfg, $name);
+    my $rbd_path = get_rbd_path($scfg, $name, $snapname);
     my $path = "rbd:${rbd_path}";
 
     $path .= ":conf=$cmd_option->{ceph_conf}" if $cmd_option->{ceph_conf};
@@ -560,8 +555,9 @@ sub create_base {
     my (undef, undef, undef, $protected) = rbd_volume_info($scfg, $storeid, $newname, $snap);
 
     if (!$protected){
-	my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'protect', $newname, '--snap', $snap);
-	run_rbd_command($cmd, errmsg => "rbd protect $newname snap '$snap' error");
+	my $snap_spec = get_rbd_path($scfg, $newname, $snap);
+	my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'protect', $snap_spec);
+	run_rbd_command($cmd, errmsg => "rbd protect '$snap_spec' error");
     }
 
     return $newvolname;
@@ -588,8 +584,9 @@ sub clone_image {
 	my (undef, undef, undef, $protected) = rbd_volume_info($scfg, $storeid, $volname, $snapname);
 
 	if (!$protected) {
-	    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'protect', $volname, '--snap', $snapname);
-	    run_rbd_command($cmd, errmsg => "rbd protect $volname snap $snapname error");
+	    my $snap_spec = get_rbd_path($scfg, $volname, $snapname);
+	    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'protect', $snap_spec);
+	    run_rbd_command($cmd, errmsg => "rbd protect '$snap_spec' error");
 	}
     }
 
@@ -597,8 +594,7 @@ sub clone_image {
     $newvol = $name if length($snapname);
 
     my @options = (
-	get_rbd_path($scfg, $basename),
-	'--snap', $snap,
+	get_rbd_path($scfg, $basename, $snap),
     );
     push @options, ('--data-pool', $scfg->{'data-pool'}) if $scfg->{'data-pool'};
 
@@ -623,7 +619,7 @@ sub alloc_image {
     );
     push @options, ('--data-pool', $scfg->{'data-pool'}) if $scfg->{'data-pool'};
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'create', @options, $name);
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'create', @options, get_rbd_path($scfg, $name));
     run_rbd_command($cmd, errmsg => "rbd create '$name' error");
 
     return $name;
@@ -635,22 +631,23 @@ sub free_image {
     my ($vtype, $name, $vmid, undef, undef, undef) =
 	$class->parse_volname($volname);
 
-
-    my $snaps = rbd_ls_snap($scfg, $storeid, $name);
+    my $image_spec = get_rbd_path($scfg, $name);
+    my $snaps = rbd_ls_snap($scfg, $storeid, $image_spec);
     foreach my $snap (keys %$snaps) {
 	if ($snaps->{$snap}->{protected}) {
-	    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'unprotect', $name, '--snap', $snap);
-	    run_rbd_command($cmd, errmsg => "rbd unprotect $name snap '$snap' error");
+	    my $snap_spec = get_rbd_path($scfg, $name, $snap);
+	    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'unprotect', $snap_spec);
+	    run_rbd_command($cmd, errmsg => "rbd unprotect $snap_spec error");
 	}
     }
 
     $class->deactivate_volume($storeid, $scfg, $volname);
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'purge',  $name);
-    run_rbd_command($cmd, errmsg => "rbd snap purge '$name' error");
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'purge',  $image_spec);
+    run_rbd_command($cmd, errmsg => "rbd snap purge '$image_spec' error");
 
-    $cmd = $rbd_cmd->($scfg, $storeid, 'rm', $name);
-    run_rbd_command($cmd, errmsg => "rbd rm '$name' error");
+    $cmd = $rbd_cmd->($scfg, $storeid, 'rm', $image_spec);
+    run_rbd_command($cmd, errmsg => "rbd rm '$image_spec' error");
 
     return undef;
 }
@@ -725,20 +722,18 @@ sub deactivate_storage {
 sub map_volume {
     my ($class, $storeid, $scfg, $volname, $snapname) = @_;
 
-    my ($vtype, $img_name, $vmid) = $class->parse_volname($volname);
+    my ($vtype, $name, $vmid) = $class->parse_volname($volname);
 
-    my $name = $img_name;
-    $name .= '@'.$snapname if $snapname;
-
-    my $kerneldev = get_rbd_dev_path($scfg, $storeid, $name);
+    my $kerneldev = get_rbd_dev_path($scfg, $storeid, $name, $snapname);
 
     return $kerneldev if -b $kerneldev; # already mapped
 
     # features can only be enabled/disabled for image, not for snapshot!
-    $krbd_feature_update->($scfg, $storeid, $img_name);
+    $krbd_feature_update->($scfg, $storeid, $name);
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'map', $name);
-    run_rbd_command($cmd, errmsg => "can't map rbd volume $name");
+    my $spec = get_rbd_path($scfg, $name, $snapname);
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'map', $spec);
+    run_rbd_command($cmd, errmsg => "can't map rbd volume '$spec'");
 
     return $kerneldev;
 }
@@ -747,9 +742,8 @@ sub unmap_volume {
     my ($class, $storeid, $scfg, $volname, $snapname) = @_;
 
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
-    $name .= '@'.$snapname if $snapname;
 
-    my $kerneldev = get_rbd_dev_path($scfg, $storeid, $name);
+    my $kerneldev = get_rbd_dev_path($scfg, $storeid, $name, $snapname);
 
     if (-b $kerneldev) {
 	my $cmd = $rbd_cmd->($scfg, $storeid, 'unmap', $kerneldev);
@@ -791,8 +785,10 @@ sub volume_resize {
 
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'resize', '--size', int(ceil($size/1024/1024)), $name);
-    run_rbd_command($cmd, errmsg => "rbd resize '$volname' error");
+    my $image_spec = get_rbd_path($scfg, $volname);
+    $size = int(ceil($size/1024/1024));
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'resize', '--size', $size, $image_spec);
+    run_rbd_command($cmd, errmsg => "rbd resize '$image_spec' error");
     return undef;
 }
 
@@ -801,8 +797,9 @@ sub volume_snapshot {
 
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'create', '--snap', $snap, $name);
-    run_rbd_command($cmd, errmsg => "rbd snapshot '$volname' error");
+    my $snap_spec = get_rbd_path($scfg, $name, $snap);
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'create', $snap_spec);
+    run_rbd_command($cmd, errmsg => "rbd snapshot '$snap_spec' error");
     return undef;
 }
 
@@ -811,8 +808,9 @@ sub volume_snapshot_rollback {
 
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'rollback', '--snap', $snap, $name);
-    run_rbd_command($cmd, errmsg => "rbd snapshot $volname to '$snap' error");
+    my $snap_spec = get_rbd_path($scfg, $name, $snap);
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'rollback', $snap_spec);
+    run_rbd_command($cmd, errmsg => "rbd snapshot rollback to '$snap_spec' error");
 }
 
 sub volume_snapshot_delete {
@@ -823,14 +821,16 @@ sub volume_snapshot_delete {
     my ($vtype, $name, $vmid) = $class->parse_volname($volname);
 
     my (undef, undef, undef, $protected) = rbd_volume_info($scfg, $storeid, $name, $snap);
+    my $snap_spec = get_rbd_path($scfg, $volname, $snap);
+
     if ($protected){
-	my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'unprotect', $name, '--snap', $snap);
-	run_rbd_command($cmd, errmsg => "rbd unprotect $name snap '$snap' error");
+	my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'unprotect', $snap_spec);
+	run_rbd_command($cmd, errmsg => "rbd unprotect '$snap_spec' error");
     }
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'rm', '--snap', $snap, $name);
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'rm', $snap_spec);
 
-    run_rbd_command($cmd, errmsg => "rbd snapshot '$volname' error");
+    run_rbd_command($cmd, errmsg => "rbd snapshot delete '$snap_spec' error");
 
     return undef;
 }
@@ -890,7 +890,9 @@ sub volume_export {
 
     my ($size) = $class->volume_size_info($scfg, $storeid, $volname);
     PVE::Storage::Plugin::write_common_header($fh, $size);
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'export', '--export-format', '1', $volname, '-');
+
+    my $snap_spec = get_rbd_path($scfg, $volname, $snapshot);
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'export', '--export-format', '1', $snap_spec, '-');
     run_rbd_command(
 	$cmd,
 	errmsg => 'could not export image',
@@ -938,8 +940,9 @@ sub volume_import {
     my ($size) = PVE::Storage::Plugin::read_common_header($fh);
     $size = PVE::Storage::Common::align_size_up($size, 1024) / 1024;
 
+    my $image_spec = get_rbd_path($scfg, $volname);
     eval {
-	my $cmd = $rbd_cmd->($scfg, $storeid, 'import', '--export-format', '1', '-', $volname);
+	my $cmd = $rbd_cmd->($scfg, $storeid, 'import', '--export-format', '1', '-', $image_spec);
 	run_rbd_command(
 	    $cmd,
 	    errmsg => 'could not import image',
@@ -976,11 +979,13 @@ sub rename_volume {
     die "target volume '${target_volname}' already exists\n"
 	if rbd_volume_exists($scfg, $storeid, $target_volname);
 
-    my $cmd = $rbd_cmd->($scfg, $storeid, 'rename', $source_image, $target_volname);
+    my $source_spec = get_rbd_path($scfg, $source_volname);
+    my $target_spec = get_rbd_path($scfg, $target_volname);
+    my $cmd = $rbd_cmd->($scfg, $storeid, 'rename', $source_spec, $target_spec);
 
     run_rbd_command(
 	$cmd,
-	errmsg => "could not rename image '${source_image}' to '${target_volname}'",
+	errmsg => "could not rename image '${source_spec}' to '${target_spec}'",
     );
 
     eval { $class->unmap_volume($storeid, $scfg, $source_volname); };
-- 
2.39.5





More information about the pve-devel mailing list