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 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel