qcow2: add external snapshot support
add a snapext option to enable the feature When a snapshot is taken, the current volume is renamed to snap volname and a current image is created with the snap volume as backing file Signed-off-by: Alexandre Derumier <alexandre.derumier@groupe-cyllene.com>
This commit is contained in:
committed by
Wolfgang Bumiller
parent
b63147f5df
commit
ccbced53c5
@ -479,7 +479,6 @@ sub volume_snapshot_rollback {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# FIXME PVE 8.x remove $running parameter (needs APIAGE reset)
|
|
||||||
sub volume_snapshot_delete {
|
sub volume_snapshot_delete {
|
||||||
my ($cfg, $volid, $snap, $running) = @_;
|
my ($cfg, $volid, $snap, $running) = @_;
|
||||||
|
|
||||||
|
|||||||
@ -168,6 +168,7 @@ sub options {
|
|||||||
bwlimit => { optional => 1 },
|
bwlimit => { optional => 1 },
|
||||||
preallocation => { optional => 1 },
|
preallocation => { optional => 1 },
|
||||||
options => { optional => 1 },
|
options => { optional => 1 },
|
||||||
|
'external-snapshots' => { optional => 1, fixed => 1 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -95,6 +95,7 @@ sub options {
|
|||||||
is_mountpoint => { optional => 1 },
|
is_mountpoint => { optional => 1 },
|
||||||
bwlimit => { optional => 1 },
|
bwlimit => { optional => 1 },
|
||||||
preallocation => { optional => 1 },
|
preallocation => { optional => 1 },
|
||||||
|
'external-snapshots' => { optional => 1, fixed => 1 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -104,6 +104,7 @@ sub options {
|
|||||||
'create-subdirs' => { optional => 1 },
|
'create-subdirs' => { optional => 1 },
|
||||||
bwlimit => { optional => 1 },
|
bwlimit => { optional => 1 },
|
||||||
preallocation => { optional => 1 },
|
preallocation => { optional => 1 },
|
||||||
|
'external-snapshots' => { optional => 1, fixed => 1 },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -232,6 +232,11 @@ my $defaultData = {
|
|||||||
maximum => 65535,
|
maximum => 65535,
|
||||||
optional => 1,
|
optional => 1,
|
||||||
},
|
},
|
||||||
|
'external-snapshots' => {
|
||||||
|
type => 'boolean',
|
||||||
|
description => 'Enable external snapshot.',
|
||||||
|
optional => 1,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -695,17 +700,20 @@ sub qemu_img_create_qcow2_backed {
|
|||||||
|
|
||||||
=head3 qemu_img_info
|
=head3 qemu_img_info
|
||||||
|
|
||||||
qemu_img_info($filename, $file_format, $timeout)
|
qemu_img_info($filename, $file_format, $timeout, $follow_backing_files)
|
||||||
|
|
||||||
Returns a json with qemu image C<$filename> informations with format <$file_format>.
|
Returns a json with qemu image C<$filename> informations with format <$file_format>.
|
||||||
|
If C<$follow_backing_files> option is defined, return a json with the whole chain
|
||||||
|
of backing files images.
|
||||||
|
|
||||||
=cut
|
=cut
|
||||||
|
|
||||||
sub qemu_img_info {
|
sub qemu_img_info {
|
||||||
my ($filename, $file_format, $timeout) = @_;
|
my ($filename, $file_format, $timeout, $follow_backing_files) = @_;
|
||||||
|
|
||||||
my $cmd = ['/usr/bin/qemu-img', 'info', '--output=json', $filename];
|
my $cmd = ['/usr/bin/qemu-img', 'info', '--output=json', $filename];
|
||||||
push $cmd->@*, '-f', $file_format if $file_format;
|
push $cmd->@*, '-f', $file_format if $file_format;
|
||||||
|
push $cmd->@*, '--backing-chain' if $follow_backing_files;
|
||||||
|
|
||||||
return PVE::Storage::Common::run_qemu_img_json($cmd, $timeout);
|
return PVE::Storage::Common::run_qemu_img_json($cmd, $timeout);
|
||||||
}
|
}
|
||||||
@ -890,10 +898,22 @@ sub get_subdir {
|
|||||||
return "$path/$subdir";
|
return "$path/$subdir";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my sub get_snap_name {
|
||||||
|
my ($class, $volname, $snapname) = @_;
|
||||||
|
die "missing snapname\n" if !$snapname;
|
||||||
|
|
||||||
|
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
|
||||||
|
$class->parse_volname($volname);
|
||||||
|
$name = $snapname eq 'current' ? $name : "snap-$snapname-$name";
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
sub filesystem_path {
|
sub filesystem_path {
|
||||||
my ($class, $scfg, $volname, $snapname) = @_;
|
my ($class, $scfg, $volname, $snapname) = @_;
|
||||||
|
|
||||||
my ($vtype, $name, $vmid, undef, undef, $isBase, $format) = $class->parse_volname($volname);
|
my ($vtype, $name, $vmid, undef, undef, $isBase, $format) = $class->parse_volname($volname);
|
||||||
|
$name = get_snap_name($class, $volname, $snapname)
|
||||||
|
if $scfg->{'external-snapshots'} && $snapname;
|
||||||
|
|
||||||
# Note: qcow2/qed has internal snapshot, so path is always
|
# Note: qcow2/qed has internal snapshot, so path is always
|
||||||
# the same (with or without snapshot => same file).
|
# the same (with or without snapshot => same file).
|
||||||
@ -1096,6 +1116,28 @@ sub alloc_image {
|
|||||||
return "$vmid/$name";
|
return "$vmid/$name";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my sub alloc_backed_image {
|
||||||
|
my ($class, $storeid, $scfg, $volname, $backing_snap) = @_;
|
||||||
|
|
||||||
|
my $path = $class->path($scfg, $volname, $storeid);
|
||||||
|
my ($vmid, $backing_format) = ($class->parse_volname($volname))[2, 6];
|
||||||
|
|
||||||
|
my $backing_volname = get_snap_name($class, $volname, $backing_snap);
|
||||||
|
#qemu_img use relative path from base image for the backing_volname by default
|
||||||
|
eval { qemu_img_create_qcow2_backed($scfg, $path, $backing_volname, $backing_format) };
|
||||||
|
if ($@) {
|
||||||
|
unlink $path;
|
||||||
|
die "$@";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my sub free_snap_image {
|
||||||
|
my ($class, $storeid, $scfg, $volname, $snap) = @_;
|
||||||
|
|
||||||
|
my $path = $class->path($scfg, $volname, $storeid, $snap);
|
||||||
|
unlink($path) || die "unlink '$path' failed - $!\n";
|
||||||
|
}
|
||||||
|
|
||||||
sub free_image {
|
sub free_image {
|
||||||
my ($class, $storeid, $scfg, $volname, $isBase, $format) = @_;
|
my ($class, $storeid, $scfg, $volname, $isBase, $format) = @_;
|
||||||
|
|
||||||
@ -1118,7 +1160,25 @@ sub free_image {
|
|||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $snapshots = undef;
|
||||||
|
if ($scfg->{'external-snapshots'}) {
|
||||||
|
$snapshots = $class->volume_snapshot_info($scfg, $storeid, $volname);
|
||||||
|
}
|
||||||
unlink($path) || die "unlink '$path' failed - $!\n";
|
unlink($path) || die "unlink '$path' failed - $!\n";
|
||||||
|
|
||||||
|
#delete external snapshots
|
||||||
|
if ($scfg->{'external-snapshots'}) {
|
||||||
|
for my $snapid (
|
||||||
|
sort { $snapshots->{$b}->{order} <=> $snapshots->{$a}->{order} }
|
||||||
|
keys %$snapshots
|
||||||
|
) {
|
||||||
|
my $snap = $snapshots->{$snapid};
|
||||||
|
next if $snapid eq 'current';
|
||||||
|
next if !$snap->{ext};
|
||||||
|
eval { free_snap_image($class, $storeid, $scfg, $volname, $snapid); };
|
||||||
|
warn $@ if $@;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# try to cleanup directory to not clutter storage with empty $vmid dirs if
|
# try to cleanup directory to not clutter storage with empty $vmid dirs if
|
||||||
@ -1319,13 +1379,37 @@ sub volume_resize {
|
|||||||
sub volume_snapshot {
|
sub volume_snapshot {
|
||||||
my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
|
my ($class, $scfg, $storeid, $volname, $snap, $running) = @_;
|
||||||
|
|
||||||
die "can't snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
|
if ($scfg->{'external-snapshots'}) {
|
||||||
|
|
||||||
my $path = $class->filesystem_path($scfg, $volname);
|
die "can't snapshot this image format\n" if $volname !~ m/\.(qcow2)$/;
|
||||||
|
|
||||||
my $cmd = ['/usr/bin/qemu-img', 'snapshot', '-c', $snap, $path];
|
my $vmid = ($class->parse_volname($volname))[2];
|
||||||
|
|
||||||
run_command($cmd);
|
if (!$running) {
|
||||||
|
#rename volume unless qemu has already done it for us
|
||||||
|
$class->rename_snapshot($scfg, $storeid, $volname, 'current', $snap);
|
||||||
|
}
|
||||||
|
eval { alloc_backed_image($class, $storeid, $scfg, $volname, $snap) };
|
||||||
|
if ($@) {
|
||||||
|
warn "$@ \n";
|
||||||
|
#if running, the revert is done by qemu with blockdev-reopen
|
||||||
|
if (!$running) {
|
||||||
|
eval { $class->rename_snapshot($scfg, $storeid, $volname, $snap, 'current'); };
|
||||||
|
warn $@ if $@;
|
||||||
|
}
|
||||||
|
die "can't allocate new volume $volname with $snap backing image\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
die "can't snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
|
||||||
|
|
||||||
|
my $path = $class->filesystem_path($scfg, $volname);
|
||||||
|
|
||||||
|
my $cmd = ['/usr/bin/qemu-img', 'snapshot', '-c', $snap, $path];
|
||||||
|
|
||||||
|
run_command($cmd);
|
||||||
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -1336,6 +1420,35 @@ sub volume_snapshot {
|
|||||||
sub volume_rollback_is_possible {
|
sub volume_rollback_is_possible {
|
||||||
my ($class, $scfg, $storeid, $volname, $snap, $blockers) = @_;
|
my ($class, $scfg, $storeid, $volname, $snap, $blockers) = @_;
|
||||||
|
|
||||||
|
return 1 if !$scfg->{'external-snapshots'};
|
||||||
|
|
||||||
|
#technically, we could manage multibranch, we it need lot more work for snapshot delete
|
||||||
|
#we need to implemente block-stream from deleted snapshot to all others child branchs
|
||||||
|
#when online, we need to do a transaction for multiple disk when delete the last snapshot
|
||||||
|
#and need to merge in current running file
|
||||||
|
|
||||||
|
my $snapshots = $class->volume_snapshot_info($scfg, $storeid, $volname);
|
||||||
|
my $found;
|
||||||
|
$blockers //= []; # not guaranteed to be set by caller
|
||||||
|
for my $snapid (
|
||||||
|
sort { $snapshots->{$b}->{order} <=> $snapshots->{$a}->{order} }
|
||||||
|
keys %$snapshots
|
||||||
|
) {
|
||||||
|
next if $snapid eq 'current';
|
||||||
|
|
||||||
|
if ($snapid eq $snap) {
|
||||||
|
$found = 1;
|
||||||
|
} elsif ($found) {
|
||||||
|
push $blockers->@*, $snapid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
die "can't rollback, snapshot '$snap' does not exist on '$volname'\n"
|
||||||
|
if !$found;
|
||||||
|
|
||||||
|
die "can't rollback, '$snap' is not most recent snapshot on '$volname'\n"
|
||||||
|
if scalar($blockers->@*) > 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1344,11 +1457,22 @@ sub volume_snapshot_rollback {
|
|||||||
|
|
||||||
die "can't rollback snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
|
die "can't rollback snapshot this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
|
||||||
|
|
||||||
my $path = $class->filesystem_path($scfg, $volname);
|
if ($scfg->{'external-snapshots'}) {
|
||||||
|
#simply delete the current snapshot and recreate it
|
||||||
|
eval { free_snap_image($class, $storeid, $scfg, $volname, 'current') };
|
||||||
|
if ($@) {
|
||||||
|
die "can't delete old volume $volname: $@\n";
|
||||||
|
}
|
||||||
|
|
||||||
my $cmd = ['/usr/bin/qemu-img', 'snapshot', '-a', $snap, $path];
|
eval { alloc_backed_image($class, $storeid, $scfg, $volname, $snap) };
|
||||||
|
if ($@) {
|
||||||
run_command($cmd);
|
die "can't allocate new volume $volname: $@\n";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
my $path = $class->filesystem_path($scfg, $volname);
|
||||||
|
my $cmd = ['/usr/bin/qemu-img', 'snapshot', '-a', $snap, $path];
|
||||||
|
run_command($cmd);
|
||||||
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -1358,15 +1482,83 @@ sub volume_snapshot_delete {
|
|||||||
|
|
||||||
die "can't delete snapshot for this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
|
die "can't delete snapshot for this image format\n" if $volname !~ m/\.(qcow2|qed)$/;
|
||||||
|
|
||||||
return 1 if $running;
|
my $cmd = "";
|
||||||
|
|
||||||
my $path = $class->filesystem_path($scfg, $volname);
|
if ($scfg->{'external-snapshots'}) {
|
||||||
|
|
||||||
$class->deactivate_volume($storeid, $scfg, $volname, $snap, {});
|
#qemu has already live commit|stream the snapshot, therefore we only have to drop the image itself
|
||||||
|
if ($running) {
|
||||||
|
eval { free_snap_image($class, $storeid, $scfg, $volname, $snap) };
|
||||||
|
if ($@) {
|
||||||
|
die "can't delete snapshot $snap of volume $volname: $@\n";
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
my $cmd = ['/usr/bin/qemu-img', 'snapshot', '-d', $snap, $path];
|
my $snapshots = $class->volume_snapshot_info($scfg, $storeid, $volname);
|
||||||
|
my $snappath = $snapshots->{$snap}->{file};
|
||||||
|
my $snap_volname = $snapshots->{$snap}->{volname};
|
||||||
|
die "volume $snappath is missing" if !-e $snappath;
|
||||||
|
|
||||||
run_command($cmd);
|
my $parentsnap = $snapshots->{$snap}->{parent};
|
||||||
|
my $childsnap = $snapshots->{$snap}->{child};
|
||||||
|
my $childpath = $snapshots->{$childsnap}->{file};
|
||||||
|
|
||||||
|
#if first snapshot,as it should be bigger, we merge child, and rename the snapshot to child
|
||||||
|
if (!$parentsnap) {
|
||||||
|
print "$volname: deleting snapshot '$snap' by commiting snapshot '$childsnap'\n";
|
||||||
|
print "running 'qemu-img commit $childpath'\n";
|
||||||
|
$cmd = ['/usr/bin/qemu-img', 'commit', $childpath];
|
||||||
|
eval { run_command($cmd) };
|
||||||
|
if ($@) {
|
||||||
|
warn
|
||||||
|
"The state of $snap is now invalid. Don't try to clone or rollback it. You can only try to delete it again later\n";
|
||||||
|
die "error commiting $childsnap to $snap; $@\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
print "rename $snappath to $childpath\n";
|
||||||
|
rename($snappath, $childpath)
|
||||||
|
|| die "rename '$snappath' to '$childpath' failed - $!\n";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#we rebase the child image on the parent as new backing image
|
||||||
|
my $parentpath = $snapshots->{$parentsnap}->{file};
|
||||||
|
print
|
||||||
|
"$volname: deleting snapshot '$snap' by rebasing '$childsnap' on top of '$parentsnap'\n";
|
||||||
|
print "running 'qemu-img rebase -b $parentpath -F qcow -f qcow2 $childpath'\n";
|
||||||
|
$cmd = [
|
||||||
|
'/usr/bin/qemu-img',
|
||||||
|
'rebase',
|
||||||
|
'-b',
|
||||||
|
$parentpath,
|
||||||
|
'-F',
|
||||||
|
'qcow2',
|
||||||
|
'-f',
|
||||||
|
'qcow2',
|
||||||
|
$childpath,
|
||||||
|
];
|
||||||
|
eval { run_command($cmd) };
|
||||||
|
if ($@) {
|
||||||
|
#in case of abort, the state of the snap is still clean, just a little bit bigger
|
||||||
|
die "error rebase $childsnap from $parentsnap; $@\n";
|
||||||
|
}
|
||||||
|
#delete the old snapshot file (not part of the backing chain anymore)
|
||||||
|
eval { free_snap_image($class, $storeid, $scfg, $volname, $snap) };
|
||||||
|
if ($@) {
|
||||||
|
die "error delete old snapshot volume $snap_volname: $@\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
return 1 if $running;
|
||||||
|
|
||||||
|
my $path = $class->filesystem_path($scfg, $volname);
|
||||||
|
$class->deactivate_volume($storeid, $scfg, $volname, $snap, {});
|
||||||
|
|
||||||
|
$cmd = ['/usr/bin/qemu-img', 'snapshot', '-d', $snap, $path];
|
||||||
|
run_command($cmd);
|
||||||
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
@ -1639,6 +1831,27 @@ sub status {
|
|||||||
return ($res->{total}, $res->{avail}, $res->{used}, 1);
|
return ($res->{total}, $res->{avail}, $res->{used}, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_snap_volname {
|
||||||
|
my ($class, $volname, $snapname) = @_;
|
||||||
|
|
||||||
|
my $vmid = ($class->parse_volname($volname))[2];
|
||||||
|
my $name = get_snap_name($class, $volname, $snapname);
|
||||||
|
return "$vmid/$name";
|
||||||
|
}
|
||||||
|
|
||||||
|
#return snapshot name from a file path
|
||||||
|
sub get_snapname_from_path {
|
||||||
|
my ($class, $volname, $path) = @_;
|
||||||
|
|
||||||
|
my $basepath = basename($path);
|
||||||
|
if ($basepath =~ m/^snap-(.*)-vm(.*)$/) {
|
||||||
|
return $1;
|
||||||
|
} elsif ($basepath eq basename($volname)) {
|
||||||
|
return 'current';
|
||||||
|
}
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
|
||||||
# Returns a hash with the snapshot names as keys and the following data:
|
# Returns a hash with the snapshot names as keys and the following data:
|
||||||
# id - Unique id to distinguish different snapshots even if the have the same name.
|
# id - Unique id to distinguish different snapshots even if the have the same name.
|
||||||
# timestamp - Creation time of the snapshot (seconds since epoch).
|
# timestamp - Creation time of the snapshot (seconds since epoch).
|
||||||
@ -1646,7 +1859,54 @@ sub status {
|
|||||||
sub volume_snapshot_info {
|
sub volume_snapshot_info {
|
||||||
my ($class, $scfg, $storeid, $volname) = @_;
|
my ($class, $scfg, $storeid, $volname) = @_;
|
||||||
|
|
||||||
die "volume_snapshot_info is not implemented for $class";
|
my $path = $class->filesystem_path($scfg, $volname);
|
||||||
|
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
|
||||||
|
$class->parse_volname($volname);
|
||||||
|
|
||||||
|
my $json = qemu_img_info($path, undef, 10, 1);
|
||||||
|
die "failed to query file information with qemu-img\n" if !$json;
|
||||||
|
my $json_decode = eval { decode_json($json) };
|
||||||
|
if ($@) {
|
||||||
|
die "Can't decode qemu snapshot list. Invalid JSON: $@\n";
|
||||||
|
}
|
||||||
|
my $info = {};
|
||||||
|
my $order = 0;
|
||||||
|
if (ref($json_decode) eq 'HASH') {
|
||||||
|
#internal snapshots is a hashref
|
||||||
|
my $snapshots = $json_decode->{snapshots};
|
||||||
|
for my $snap (@$snapshots) {
|
||||||
|
my $snapname = $snap->{name};
|
||||||
|
$info->{$snapname}->{order} = $snap->{id};
|
||||||
|
$info->{$snapname}->{timestamp} = $snap->{'date-sec'};
|
||||||
|
|
||||||
|
}
|
||||||
|
} elsif (ref($json_decode) eq 'ARRAY') {
|
||||||
|
#no snapshot or external snapshots is an arrayref
|
||||||
|
my $snapshots = $json_decode;
|
||||||
|
for my $snap (@$snapshots) {
|
||||||
|
my $snapfile = $snap->{filename};
|
||||||
|
my $snapname = $class->get_snapname_from_path($volname, $snapfile);
|
||||||
|
#not a proxmox snapshot
|
||||||
|
next if !$snapname;
|
||||||
|
|
||||||
|
my $snapvolname = $class->get_snap_volname($volname, $snapname);
|
||||||
|
$info->{$snapname}->{order} = $order;
|
||||||
|
$info->{$snapname}->{file} = $snapfile;
|
||||||
|
$info->{$snapname}->{volname} = "$snapvolname";
|
||||||
|
$info->{$snapname}->{volid} = "$storeid:$snapvolname";
|
||||||
|
$info->{$snapname}->{ext} = 1;
|
||||||
|
|
||||||
|
my $parentfile = $snap->{'backing-filename'};
|
||||||
|
if ($parentfile) {
|
||||||
|
my $parentname = $class->get_snapname_from_path($volname, $parentfile);
|
||||||
|
$info->{$snapname}->{parent} = $parentname;
|
||||||
|
$info->{$parentname}->{child} = $snapname;
|
||||||
|
}
|
||||||
|
$order++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $info;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub activate_storage {
|
sub activate_storage {
|
||||||
@ -2062,7 +2322,14 @@ Rename a volume source snapshot C<$source_snap> to a target snapshot C<$target_s
|
|||||||
sub rename_snapshot {
|
sub rename_snapshot {
|
||||||
my ($class, $scfg, $storeid, $volname, $source_snap, $target_snap) = @_;
|
my ($class, $scfg, $storeid, $volname, $source_snap, $target_snap) = @_;
|
||||||
|
|
||||||
die "rename_snapshot is not implemented for $class";
|
my $source_snap_path = $class->filesystem_path($scfg, $volname, $source_snap);
|
||||||
|
my $target_snap_path = $class->filesystem_path($scfg, $volname, $target_snap);
|
||||||
|
print "rename $source_snap_path to $target_snap_path\n";
|
||||||
|
|
||||||
|
die "target snapshot '${target_snap}' already exists\n" if -e $target_snap_path;
|
||||||
|
|
||||||
|
rename($source_snap_path, $target_snap_path)
|
||||||
|
|| die "rename '$source_snap_path' to '$target_snap_path' failed - $!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
my sub blockdev_options_nbd_tcp {
|
my sub blockdev_options_nbd_tcp {
|
||||||
@ -2170,7 +2437,8 @@ sub qemu_blockdev_options {
|
|||||||
# the snapshot alone.
|
# the snapshot alone.
|
||||||
my $format = ($class->parse_volname($volname))[6];
|
my $format = ($class->parse_volname($volname))[6];
|
||||||
die "cannot attach only the snapshot of a '$format' image\n"
|
die "cannot attach only the snapshot of a '$format' image\n"
|
||||||
if $options->{'snapshot-name'} && ($format eq 'qcow2' || $format eq 'qed');
|
if $options->{'snapshot-name'}
|
||||||
|
&& ($format eq 'qcow2' && !$scfg->{'external-snapshots'} || $format eq 'qed');
|
||||||
|
|
||||||
# The 'file' driver only works for regular files. The check below is taken from
|
# The 'file' driver only works for regular files. The check below is taken from
|
||||||
# block/file-posix.c:hdev_probe_device() in QEMU. Do not bother with detecting 'host_cdrom'
|
# block/file-posix.c:hdev_probe_device() in QEMU. Do not bother with detecting 'host_cdrom'
|
||||||
|
|||||||
Reference in New Issue
Block a user