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 $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) = @_;
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 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 {
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 {
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_update->($scfg, $storeid, $name) if !$snapname;
+ # 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");
return undef;
}
+sub volume_snapshot_needs_fsfreeze {
+
+ return 1;
+}
+
sub volume_has_feature {
my ($class, $scfg, $feature, $storeid, $volname, $snapname, $running) = @_;