[pve-devel] [PATCH RFC container] Refactor snapshot code
Fabian Grünbichler
f.gruenbichler at proxmox.com
Mon Feb 15 09:57:42 CET 2016
Make various snapshot related methods more similar to
QemuServer codebase:
- CT descriptions are not overwritten on rollback anymore
- create, delete and rollback flow like in QemuServer.pm
---
Still open:
- freeze check and actual (un)freezing should probably get its own sub
- mp snapshot support for LXC, needs changes in VZDump
- common wrapper or name change for foreach_drive/foreach_mountpoint?
src/PVE/API2/LXC/Snapshot.pm | 2 +-
src/PVE/LXC.pm | 221 +++++++++++++++++++++++++++----------------
src/PVE/VZDump/LXC.pm | 2 +-
3 files changed, 140 insertions(+), 85 deletions(-)
diff --git a/src/PVE/API2/LXC/Snapshot.pm b/src/PVE/API2/LXC/Snapshot.pm
index 49fd4f5..4da2d87 100644
--- a/src/PVE/API2/LXC/Snapshot.pm
+++ b/src/PVE/API2/LXC/Snapshot.pm
@@ -128,7 +128,7 @@ __PACKAGE__->register_method({
my $realcmd = sub {
PVE::Cluster::log_msg('info', $authuser, "snapshot container $vmid: $snapname");
- PVE::LXC::snapshot_create($vmid, $snapname, $param->{description});
+ PVE::LXC::snapshot_create($vmid, $snapname, 0, $param->{description});
};
return $rpcenv->fork_worker('pctsnapshot', $vmid, $authuser, $realcmd);
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index a737fc0..8b18ee0 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -1710,13 +1710,37 @@ my $snapshot_copy_config = sub {
next if $k eq 'lock';
next if $k eq 'digest';
next if $k eq 'description';
+ next if $k =~ m/^unused\d+$/;
$dest->{$k} = $source->{$k};
}
};
+my $snapshot_apply_config = sub {
+ my ($conf, $snap) = @_;
+
+ # copy snapshot list
+ my $newconf = {
+ snapshots => $conf->{snapshots},
+ };
+
+ # keep description and list of unused disks
+ foreach my $k (keys %$conf) {
+ next if !($k =~ m/^unused\d+$/ || $k eq 'description');
+ $newconf->{$k} = $conf->{$k};
+ }
+
+ &$snapshot_copy_config($snap, $newconf);
+
+ return $newconf;
+};
+
+my $snapshot_save_vmstate = sub {
+ die "implement me - snapshot_save_vmstate";
+};
+
my $snapshot_prepare = sub {
- my ($vmid, $snapname, $comment) = @_;
+ my ($vmid, $snapname, $save_vmstate, $comment) = @_;
my $snap;
@@ -1735,17 +1759,22 @@ my $snapshot_prepare = sub {
if defined($conf->{snapshots}->{$snapname});
my $storecfg = PVE::Storage::config();
+
+ # workaround until mp snapshots are implemented
my $feature = $snapname eq 'vzdump' ? 'vzdump' : 'snapshot';
die "snapshot feature is not available\n" if !has_feature($feature, $conf, $storecfg);
$snap = $conf->{snapshots}->{$snapname} = {};
+ if ($save_vmstate && check_running($vmid)) {
+ &$snapshot_save_vmstate($vmid, $conf, $snapname, $storecfg);
+ }
+
&$snapshot_copy_config($conf, $snap);
- $snap->{'snapstate'} = "prepare";
- $snap->{'snaptime'} = time();
- $snap->{'description'} = $comment if $comment;
- $conf->{snapshots}->{$snapname} = $snap;
+ $snap->{snapstate} = "prepare";
+ $snap->{snaptime} = time();
+ $snap->{description} = $comment if $comment;
write_config($vmid, $conf);
};
@@ -1765,21 +1794,23 @@ my $snapshot_commit = sub {
die "missing snapshot lock\n"
if !($conf->{lock} && $conf->{lock} eq 'snapshot');
- die "snapshot '$snapname' does not exist\n"
- if !defined($conf->{snapshots}->{$snapname});
+ my $snap = $conf->{snapshots}->{$snapname};
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
die "wrong snapshot state\n"
- if !($conf->{snapshots}->{$snapname}->{'snapstate'} &&
- $conf->{snapshots}->{$snapname}->{'snapstate'} eq "prepare");
+ if !($snap->{snapstate} && $snap->{snapstate} eq "prepare");
- delete $conf->{snapshots}->{$snapname}->{'snapstate'};
+ delete $snap->{snapstate};
delete $conf->{lock};
- $conf->{parent} = $snapname;
- write_config($vmid, $conf);
+ my $newconf = &$snapshot_apply_config($conf, $snap);
+
+ $newconf->{parent} = $snapname;
+
+ write_config($vmid, $newconf);
};
- lock_config($vmid ,$updatefn);
+ lock_config($vmid, $updatefn);
};
sub has_feature {
@@ -1875,9 +1906,9 @@ sub sync_container_namespace {
}
sub snapshot_create {
- my ($vmid, $snapname, $comment) = @_;
+ my ($vmid, $snapname, $save_vmstate, $comment) = @_;
- my $snap = &$snapshot_prepare($vmid, $snapname, $comment);
+ my $snap = &$snapshot_prepare($vmid, $snapname, $save_vmstate, $comment);
my $conf = load_config($vmid);
@@ -1921,36 +1952,11 @@ sub snapshot_create {
sub snapshot_delete {
my ($vmid, $snapname, $force, $drivehash) = @_;
- my $snap;
-
- my $conf;
-
- my $updatefn = sub {
-
- $conf = load_config($vmid);
-
- die "you can't delete a snapshot if vm is a template\n"
- if is_template($conf);
-
- $snap = $conf->{snapshots}->{$snapname};
-
- if (!$drivehash) {
- check_lock($conf);
- }
-
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
-
- $snap->{snapstate} = 'delete';
+ my $prepare = 1;
- write_config($vmid, $conf);
- };
-
- lock_config($vmid, $updatefn);
-
- my $storecfg = PVE::Storage::config();
+ my $snap;
my $unlink_parent = sub {
-
my ($confref, $new_parent) = @_;
if ($confref->{parent} && $confref->{parent} eq $snapname) {
@@ -1962,58 +1968,99 @@ sub snapshot_delete {
}
};
- my $del_snap = sub {
+ my $updatefn = sub {
+ my ($remove_drive) = @_;
- $conf = load_config($vmid);
+ my $conf = load_config($vmid);
- if ($drivehash) {
- delete $conf->{lock};
- } else {
+ if (!$drivehash) {
check_lock($conf);
+ die "you can't delete a snapshot if vm is a template\n"
+ if is_template($conf);
}
- my $parent = $conf->{snapshots}->{$snapname}->{parent};
- foreach my $snapkey (keys %{$conf->{snapshots}}) {
- &$unlink_parent($conf->{snapshots}->{$snapkey}, $parent);
+ $snap = $conf->{snapshots}->{$snapname};
+
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+ # remove parent refs
+ if (!$prepare) {
+ &$unlink_parent($conf, $snap->{parent});
+ foreach my $sn (keys %{$conf->{snapshots}}) {
+ next if $sn eq $snapname;
+ &$unlink_parent($conf->{snapshots}->{$sn}, $snap->{parent});
+ }
}
- &$unlink_parent($conf, $parent);
+ if ($remove_drive) {
+ if ($remove_drive eq 'vmstate') {
+ die "implement me - saving vmstate\n";
+ } else {
+ die "implement me - remove drive\n";
+ }
+ }
- delete $conf->{snapshots}->{$snapname};
+ if ($prepare) {
+ $snap->{snapstate} = 'delete';
+ } else {
+ delete $conf->{snapshots}->{$snapname};
+ delete $conf->{lock} if $drivehash;
+ }
write_config($vmid, $conf);
};
- my $rootfs = $conf->{snapshots}->{$snapname}->{rootfs};
- my $rootinfo = parse_ct_rootfs($rootfs);
- my $volid = $rootinfo->{volume};
+ lock_config($vmid, $updatefn);
+
+ # now remove vmstate file
+ # never set for LXC!
+ my $storecfg = PVE::Storage::config();
+
+ if ($snap->{vmstate}) {
+ die "implement me - saving vmstate\n";
+ };
+ # now remove all volume snapshots
+ # only rootfs for now!
eval {
+ my $rootfs = $snap->{rootfs};
+ my $rootinfo = parse_ct_rootfs($rootfs);
+ my $volid = $rootinfo->{volume};
PVE::Storage::volume_snapshot_delete($storecfg, $volid, $snapname);
};
- my $err = $@;
-
- if(!$err || ($err && $force)) {
- lock_config($vmid, $del_snap);
- if ($err) {
- die "Can't delete snapshot: $vmid $snapname $err\n";
- }
+ if (my $err = $@) {
+ die $err if !$force;
+ warn $err;
}
+
+ # now cleanup config
+ $prepare = 0;
+ lock_config($vmid, $updatefn);
}
sub snapshot_rollback {
my ($vmid, $snapname) = @_;
+ my $prepare = 1;
+
my $storecfg = PVE::Storage::config();
my $conf = load_config($vmid);
- die "you can't rollback if vm is a template\n" if is_template($conf);
+ my $get_snapshot_config = sub {
+
+ die "you can't rollback if vm is a template\n" if is_template($conf);
+
+ my $res = $conf->{snapshots}->{$snapname};
+
+ die "snapshot '$snapname' does not exist\n" if !defined($res);
- my $snap = $conf->{snapshots}->{$snapname};
+ return $res;
+ };
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
+ my $snap = &$get_snapshot_config();
+ # only for rootfs for now!
my $rootfs = $snap->{rootfs};
my $rootinfo = parse_ct_rootfs($rootfs);
my $volid = $rootinfo->{volume};
@@ -2022,42 +2069,50 @@ sub snapshot_rollback {
my $updatefn = sub {
- die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
- if $snap->{snapstate};
+ $conf = load_config($vmid);
- check_lock($conf);
+ $snap = &$get_snapshot_config();
+
+ die "unable to rollback to incomplete snapshot (snapstate = $snap->{snapstate})\n"
+ if $snap->{snapstate};
- system("lxc-stop -n $vmid --kill") if check_running($vmid);
+ if ($prepare) {
+ check_lock($conf);
+ system("lxc-stop -n $vmid --kill") if check_running($vmid);
+ }
die "unable to rollback vm $vmid: vm is running\n"
if check_running($vmid);
- $conf->{lock} = 'rollback';
+ if ($prepare) {
+ $conf->{lock} = 'rollback';
+ } else {
+ die "got wrong lock\n" if !($conf->{lock} && $conf->{lock} eq 'rollback');
+ delete $conf->{lock};
+ }
my $forcemachine;
- # copy snapshot config to current config
-
- my $tmp_conf = $conf;
- &$snapshot_copy_config($tmp_conf->{snapshots}->{$snapname}, $conf);
- $conf->{snapshots} = $tmp_conf->{snapshots};
- delete $conf->{snaptime};
- delete $conf->{snapname};
- $conf->{parent} = $snapname;
+ if (!$prepare) {
+ # copy snapshot config to current config
+ $conf = &$snapshot_apply_config($conf, $snap);
+ $conf->{parent} = $snapname;
+ }
write_config($vmid, $conf);
- };
- my $unlockfn = sub {
- delete $conf->{lock};
- write_config($vmid, $conf);
+ if (!$prepare && $snap->{vmstate}) {
+ die "implement me - save vmstate";
+ }
};
lock_config($vmid, $updatefn);
+ # only rootfs for now!
PVE::Storage::volume_snapshot_rollback($storecfg, $volid, $snapname);
- lock_config($vmid, $unlockfn);
+ $prepare = 0;
+ lock_config($vmid, $updatefn);
}
sub template_create {
diff --git a/src/PVE/VZDump/LXC.pm b/src/PVE/VZDump/LXC.pm
index 0979f15..1d24036 100644
--- a/src/PVE/VZDump/LXC.pm
+++ b/src/PVE/VZDump/LXC.pm
@@ -211,7 +211,7 @@ sub snapshot {
$self->loginfo("create storage snapshot 'vzdump'");
# todo: freeze/unfreeze if we have more than one volid
- PVE::LXC::snapshot_create($vmid, 'vzdump', "vzdump backup snapshot");
+ PVE::LXC::snapshot_create($vmid, 'vzdump', 0, "vzdump backup snapshot");
$task->{cleanup}->{remove_snapshot} = 1;
# reload config
--
2.1.4
More information about the pve-devel
mailing list