use PVE::CephConfig;
use PVE::JSONSchema qw(get_standard_option);
+use PVE::ProcFSTools;
use PVE::RADOS;
use PVE::Storage::Plugin;
use PVE::Tools qw(run_command trim);
};
# needed for volumes created using ceph jewel (or higher)
-my $krbd_feature_disable = 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();
- my $krbd_feature_blacklist = ['deep-flatten', 'fast-diff', 'object-map', 'exclusive-lock'];
- my (undef, undef, undef, undef, $features) = rbd_volume_info($scfg, $storeid, $name);
-
- my $active_features = { map { $_ => 1 } @$features };
- my $incompatible_features = join(',', grep { %$active_features{$_} } @$krbd_feature_blacklist);
-
- if ($incompatible_features) {
- my $feature_cmd = &$rbd_cmd($scfg, $storeid, 'feature', 'disable', $name, $incompatible_features);
- run_rbd_command($feature_cmd, errmsg => "could not disable krbd-incompatible image features of rbd volume $name");
+ if ($kmajor > 5 || $kmajor == 5 && $kminor >= 3) {
+ # 'deep-flatten' can only be disabled, not enabled after image creation
+ push @enable, 'fast-diff', 'object-map';
+ } else {
+ push @disable, 'fast-diff', 'object-map', 'deep-flatten';
}
-};
-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);
+ if ($kmajor >= 5) {
+ push @enable, 'exclusive-lock';
+ } else {
+ push @disable, 'exclusive-lock';
}
- warn "Could not parse Ceph version: '$ceph_version'\n";
-};
-sub ceph_version {
- my ($cache) = @_;
+ my $active_features_list = (rbd_volume_info($scfg, $storeid, $name))[4];
+ my $active_features = { map { $_ => 1 } @$active_features_list };
- 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);
+ my $to_disable = join(',', grep { $active_features->{$_} } @disable);
+ my $to_enable = join(',', grep { !$active_features->{$_} } @enable );
- return wantarray ? ($subversions, $version) : $version;
-}
+ 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);
+ run_rbd_command(
+ $cmd,
+ errmsg => "could not disable krbd-incompatible image features '$to_disable' for rbd image: $name",
+ );
+ }
+ 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);
+ run_rbd_command(
+ $cmd,
+ errmsg => "could not enable krbd-compatible image features '$to_enable' for rbd image: $name",
+ );
+ };
+ warn "$@" if $@;
+ }
+};
sub run_rbd_command {
my ($cmd, %args) = @_;
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) = @_;
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 $disk_list = [];
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) = @_;
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";
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);
run_rbd_command($cmd, errmsg => "rbd create $name' error");
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 $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");
}
sub map_volume {
my ($class, $storeid, $scfg, $volname, $snapname) = @_;
- my ($vtype, $name, $vmid) = $class->parse_volname($volname);
+ my ($vtype, $img_name, $vmid) = $class->parse_volname($volname);
+
+ my $name = $img_name;
$name .= '@'.$snapname if $snapname;
my $pool = $scfg->{pool} ? $scfg->{pool} : 'rbd';
return $kerneldev if -b $kerneldev; # already mapped
- &$krbd_feature_disable($scfg, $storeid, $name);
+ # 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);
run_rbd_command($cmd, errmsg => "can't map rbd volume $name");