[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