X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FStorage%2FRBDPlugin.pm;h=a0389687c341f52b25974ed36183b8305af83892;hb=72bbd8a6f7e5b7025d911ca24d439ccb0c5320db;hp=538d6bbc9c5526396c1f99cafb12c6c8b46b7289;hpb=518f3908bf8f4556b694faa83440fd83e608f9ae;p=pve-storage.git diff --git a/PVE/Storage/RBDPlugin.pm b/PVE/Storage/RBDPlugin.pm index 538d6bb..a038968 100644 --- a/PVE/Storage/RBDPlugin.pm +++ b/PVE/Storage/RBDPlugin.pm @@ -22,12 +22,13 @@ my $get_parent_image_name = sub { return $parent->{image} . "@" . $parent->{snapshot}; }; -my $add_pool_to_disk = sub { - my ($scfg, $disk) = @_; - +my sub get_rbd_path { + my ($scfg, $volume) = @_; my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd'; + my $namespace = $scfg->{namespace}; - return "$pool/$disk"; + return "${pool}/${namespace}/${volume}" if defined($namespace); + return "${pool}/${volume}"; }; my $build_cmd = sub { @@ -38,6 +39,13 @@ my $build_cmd = sub { my $cmd = [$binary, '-p', $pool]; + 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}); @@ -77,9 +85,6 @@ my $librados_connect = sub { my $krbd_feature_update = sub { my ($scfg, $storeid, $name) = @_; - my ($versionparts) = ceph_version(); - return 1 if $versionparts->[0] < 10; - my (@disable, @enable); my ($kmajor, $kminor) = PVE::ProcFSTools::kernel_version(); @@ -123,35 +128,6 @@ my $krbd_feature_update = sub { } }; -my $ceph_version_parser = sub { - my $ceph_version = shift; - # FIXME this is the same as pve-manager PVE::Ceph::Tools get_local_version - if ($ceph_version =~ /^ceph.*\s(\d+(?:\.\d+)+(?:-pve\d+)?)\s+(?:\(([a-zA-Z0-9]+)\))?/) { - my ($version, $buildcommit) = ($1, $2); - my $subversions = [ split(/\.|-/, $version) ]; - - return ($subversions, $version, $buildcommit); - } - warn "Could not parse Ceph version: '$ceph_version'\n"; -}; - -sub ceph_version { - my ($cache) = @_; - - my $version_string = $cache; - if (!defined($version_string)) { - run_command('ceph --version', outfunc => sub { - $version_string = shift; - }); - } - return undef if !defined($version_string); - # subversion is an array ref. with the version parts from major to minor - # version is the filtered version string - my ($subversions, $version) = $ceph_version_parser->($version_string); - - return wantarray ? ($subversions, $version) : $version; -} - sub run_rbd_command { my ($cmd, %args) = @_; @@ -184,12 +160,13 @@ sub run_rbd_command { sub rbd_ls { my ($scfg, $storeid) = @_; - my $cmd = &$rbd_cmd($scfg, $storeid, 'ls', '-l', '--format', 'json'); my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd'; + $pool .= "/$scfg->{namespace}" if defined($scfg->{namespace}); my $raw = ''; my $parser = sub { $raw .= shift }; + my $cmd = $rbd_cmd->($scfg, $storeid, 'ls', '-l', '--format', 'json'); eval { run_rbd_command($cmd, errmsg => "rbd error", errfunc => sub {}, outfunc => $parser); }; @@ -227,6 +204,38 @@ sub rbd_ls { return $list; } +sub rbd_ls_snap { + my ($scfg, $storeid, $name) = @_; + + my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'ls', $name, '--format', 'json'); + + my $raw = ''; + run_rbd_command($cmd, errmsg => "rbd error", errfunc => sub {}, outfunc => sub { $raw .= shift; }); + + my $list; + if ($raw =~ m/^(\[.*\])$/s) { # untaint + $list = eval { JSON::decode_json($1) }; + die "invalid JSON output from 'rbd snap ls $name': $@\n" if $@; + } else { + die "got unexpected data from 'rbd snap ls $name': '$raw'\n"; + } + + $list = [] if !defined($list); + + my $res = {}; + foreach my $el (@$list) { + my $snap = $el->{name}; + my $protected = defined($el->{protected}) && $el->{protected} eq "true" ? 1 : undef; + $res->{$snap} = { + name => $snap, + id => $el->{id} // undef, + size => $el->{size} // 0, + protected => $protected, + }; + } + return $res; +} + sub rbd_volume_info { my ($scfg, $storeid, $volname, $snap) = @_; @@ -237,7 +246,7 @@ sub rbd_volume_info { push @options, '--snap', $snap; } - $cmd = &$rbd_cmd($scfg, $storeid, @options); + $cmd = $rbd_cmd->($scfg, $storeid, @options); my $raw = ''; my $parser = sub { $raw .= shift }; @@ -281,6 +290,10 @@ sub properties { description => "Pool.", type => 'string', }, + namespace=> { + description => "RBD Namespace.", + type => 'string', + }, username => { description => "RBD Id.", type => 'string', @@ -302,6 +315,7 @@ sub options { disable => { optional => 1 }, monhost => { optional => 1}, pool => { optional => 1 }, + namespace => { optional => 1 }, username => { optional => 1 }, content => { optional => 1 }, krbd => { optional => 1 }, @@ -317,6 +331,8 @@ sub on_add_hook { return if defined($scfg->{monhost}); # nothing to do if not pve managed ceph PVE::CephConfig::ceph_create_keyfile($scfg->{type}, $storeid); + + return; } sub on_delete_hook { @@ -325,6 +341,8 @@ sub on_delete_hook { return if defined($scfg->{monhost}); # nothing to do if not pve managed ceph PVE::CephConfig::ceph_remove_keyfile($scfg->{type}, $storeid); + + return; } sub parse_volname { @@ -344,10 +362,10 @@ sub path { my ($vtype, $name, $vmid) = $class->parse_volname($volname); $name .= '@'.$snapname if $snapname; - my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd'; - return ("/dev/rbd/$pool/$name", $vmid, $vtype) if $scfg->{krbd}; + my $rbd_path = get_rbd_path($scfg, $name); + return ("/dev/rbd/${rbd_path}", $vmid, $vtype) if $scfg->{krbd}; - my $path = "rbd:$pool/$name"; + my $path = "rbd:${rbd_path}"; $path .= ":conf=$cmd_option->{ceph_conf}" if $cmd_option->{ceph_conf}; if (defined($scfg->{monhost})) { @@ -362,10 +380,11 @@ sub path { return ($path, $vmid, $vtype); } -my $find_free_diskname = sub { - my ($storeid, $scfg, $vmid) = @_; +sub find_free_diskname { + my ($class, $storeid, $scfg, $vmid, $fmt, $add_fmt_suffix) = @_; + + my $cmd = $rbd_cmd->($scfg, $storeid, 'ls'); - my $cmd = &$rbd_cmd($scfg, $storeid, 'ls'); my $disk_list = []; my $parser = sub { @@ -383,7 +402,7 @@ my $find_free_diskname = sub { die $err if $err && $err !~ m/doesn't contain rbd images/; return PVE::Storage::Plugin::get_next_vm_diskname($disk_list, $storeid, $vmid, undef, $scfg); -}; +} sub create_base { my ($class, $storeid, $scfg, $volname) = @_; @@ -408,7 +427,13 @@ sub create_base { my $newvolname = $basename ? "$basename/$newname" : "$newname"; - my $cmd = &$rbd_cmd($scfg, $storeid, 'rename', &$add_pool_to_disk($scfg, $name), &$add_pool_to_disk($scfg, $newname)); + my $cmd = $rbd_cmd->( + $scfg, + $storeid, + 'rename', + get_rbd_path($scfg, $name), + get_rbd_path($scfg, $newname), + ); run_rbd_command($cmd, errmsg => "rbd rename '$name' error"); my $running = undef; #fixme : is create_base always offline ? @@ -418,7 +443,7 @@ 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); + my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'protect', $newname, '--snap', $snap); run_rbd_command($cmd, errmsg => "rbd protect $newname snap '$snap' error"); } @@ -438,7 +463,7 @@ sub clone_image { die "$volname is not a base image and snapname is not provided\n" if !$isBase && !length($snapname); - my $name = $find_free_diskname->($storeid, $scfg, $vmid); + my $name = $class->find_free_diskname($storeid, $scfg, $vmid); warn "clone $volname: $basename snapname $snap to $name\n"; @@ -446,7 +471,7 @@ 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); + my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'protect', $volname, '--snap', $snapname); run_rbd_command($cmd, errmsg => "rbd protect $volname snap $snapname error"); } } @@ -454,8 +479,15 @@ sub clone_image { my $newvol = "$basename/$name"; $newvol = $name if length($snapname); - my $cmd = &$rbd_cmd($scfg, $storeid, 'clone', &$add_pool_to_disk($scfg, $basename), - '--snap', $snap, &$add_pool_to_disk($scfg, $name)); + my $cmd = $rbd_cmd->( + $scfg, + $storeid, + 'clone', + get_rbd_path($scfg, $basename), + '--snap', + $snap, + get_rbd_path($scfg, $name), + ); run_rbd_command($cmd, errmsg => "rbd clone '$basename' error"); @@ -469,9 +501,9 @@ sub alloc_image { die "illegal name '$name' - should be 'vm-$vmid-*'\n" if $name && $name !~ m/^vm-$vmid-/; - $name = $find_free_diskname->($storeid, $scfg, $vmid) if !$name; + $name = $class->find_free_diskname($storeid, $scfg, $vmid) if !$name; - my $cmd = &$rbd_cmd($scfg, $storeid, 'create', '--image-format' , 2, '--size', int(($size+1023)/1024), $name); + my $cmd = $rbd_cmd->($scfg, $storeid, 'create', '--image-format' , 2, '--size', int(($size+1023)/1024), $name); run_rbd_command($cmd, errmsg => "rbd create $name' error"); return $name; @@ -483,21 +515,21 @@ sub free_image { my ($vtype, $name, $vmid, undef, undef, undef) = $class->parse_volname($volname); - if ($isBase) { - my $snap = '__base__'; - my (undef, undef, undef, $protected) = rbd_volume_info($scfg, $storeid, $name, $snap); - if ($protected){ - my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'unprotect', $name, '--snap', $snap); + + my $snaps = rbd_ls_snap($scfg, $storeid, $name); + 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"); } } $class->deactivate_volume($storeid, $scfg, $volname); - my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'purge', $name); + my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'purge', $name); run_rbd_command($cmd, errmsg => "rbd snap purge '$volname' error"); - $cmd = &$rbd_cmd($scfg, $storeid, 'rm', $name); + $cmd = $rbd_cmd->($scfg, $storeid, 'rm', $name); run_rbd_command($cmd, errmsg => "rbd rm '$volname' error"); return undef; @@ -508,6 +540,7 @@ sub list_images { $cache->{rbd} = rbd_ls($scfg, $storeid) if !$cache->{rbd}; my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd'; + $pool .= "/$scfg->{namespace}" if defined($scfg->{namespace}); my $res = []; @@ -545,8 +578,7 @@ sub list_images { sub status { my ($class, $storeid, $scfg, $cache) = @_; - - my $rados = &$librados_connect($scfg, $storeid); + my $rados = $librados_connect->($scfg, $storeid); my $df = $rados->mon_command({ prefix => 'df', format => 'json' }); my ($d) = grep { $_->{name} eq $scfg->{pool} } @{$df->{pools}}; @@ -572,9 +604,8 @@ sub deactivate_storage { } my $get_kernel_device_name = sub { - my ($pool, $name) = @_; - - return "/dev/rbd/$pool/$name"; + my ($scfg, $name) = @_; + return "/dev/rbd/" . get_rbd_path($scfg, $name); }; sub map_volume { @@ -585,16 +616,14 @@ sub map_volume { my $name = $img_name; $name .= '@'.$snapname if $snapname; - my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd'; - - my $kerneldev = $get_kernel_device_name->($pool, $name); + my $kerneldev = $get_kernel_device_name->($scfg, $name); 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); - my $cmd = &$rbd_cmd($scfg, $storeid, 'map', $name); + my $cmd = $rbd_cmd->($scfg, $storeid, 'map', $name); run_rbd_command($cmd, errmsg => "can't map rbd volume $name"); return $kerneldev; @@ -606,12 +635,10 @@ sub unmap_volume { my ($vtype, $name, $vmid) = $class->parse_volname($volname); $name .= '@'.$snapname if $snapname; - my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd'; - - my $kerneldev = $get_kernel_device_name->($pool, $name); + my $kerneldev = $get_kernel_device_name->($scfg, $name); if (-b $kerneldev) { - my $cmd = &$rbd_cmd($scfg, $storeid, 'unmap', $kerneldev); + my $cmd = $rbd_cmd->($scfg, $storeid, 'unmap', $kerneldev); run_rbd_command($cmd, errmsg => "can't unmap rbd device $kerneldev"); } @@ -649,7 +676,7 @@ sub volume_resize { my ($vtype, $name, $vmid) = $class->parse_volname($volname); - my $cmd = &$rbd_cmd($scfg, $storeid, 'resize', '--allow-shrink', '--size', ($size/1024/1024), $name); + my $cmd = $rbd_cmd->($scfg, $storeid, 'resize', '--allow-shrink', '--size', ($size/1024/1024), $name); run_rbd_command($cmd, errmsg => "rbd resize '$volname' error"); return undef; } @@ -659,7 +686,7 @@ sub volume_snapshot { my ($vtype, $name, $vmid) = $class->parse_volname($volname); - my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'create', '--snap', $snap, $name); + my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'create', '--snap', $snap, $name); run_rbd_command($cmd, errmsg => "rbd snapshot '$volname' error"); return undef; } @@ -669,7 +696,7 @@ sub volume_snapshot_rollback { my ($vtype, $name, $vmid) = $class->parse_volname($volname); - my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'rollback', '--snap', $snap, $name); + my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'rollback', '--snap', $snap, $name); run_rbd_command($cmd, errmsg => "rbd snapshot $volname to '$snap' error"); } @@ -684,17 +711,22 @@ sub volume_snapshot_delete { my (undef, undef, undef, $protected) = rbd_volume_info($scfg, $storeid, $name, $snap); if ($protected){ - my $cmd = &$rbd_cmd($scfg, $storeid, 'snap', 'unprotect', $name, '--snap', $snap); + 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', 'rm', '--snap', $snap, $name); + my $cmd = $rbd_cmd->($scfg, $storeid, 'snap', 'rm', '--snap', $snap, $name); run_rbd_command($cmd, errmsg => "rbd snapshot '$volname' error"); return undef; } +sub volume_snapshot_needs_fsfreeze { + + return 1; +} + sub volume_has_feature { my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;