$class->lock_config($vmid, sub {
my $conf = $class->load_config($vmid);
if (!$conf->{lock}) {
- die "no lock found trying to remove lock '$lock'\n";
+ my $lockstring = defined($lock) ? "'$lock' " : "any";
+ die "no lock found trying to remove $lockstring lock\n";
} elsif (defined($lock) && $conf->{lock} ne $lock) {
- die "found lock '$conf->{lock}' trying to remove lock '$lock'\n";
+ die "found lock '$conf->{lock}' trying to remove '$lock' lock\n";
}
delete $conf->{lock};
$class->write_config($vmid, $conf);
die "abstract method - implement me\n";
}
+# Code run before and after creating all the volume snapshots
+# base: noop
+sub __snapshot_create_vol_snapshots_hook {
+ my ($class, $vmid, $snap, $running, $hook) = @_;
+
+ return;
+}
+
# Create the volume snapshots for the VM/CT.
sub __snapshot_create_vol_snapshot {
my ($class, $vmid, $vs, $volume, $snapname) = @_;
# Remove a drive from the snapshot config.
sub __snapshot_delete_remove_drive {
- my ($class, $snap, $remove_drive) = @_;
+ my ($class, $snap, $drive) = @_;
die "abstract method - implement me\n";
}
die "abstract method - implement me\n";
}
+# Get list of volume IDs which are referenced in $conf, but not in $snap.
+sub __snapshot_rollback_get_unused {
+ my ($class, $conf, $snap) = @_;
+
+ die "abstract method - implement me\n";
+}
+
# Iterate over all configured volumes, calling $func for each key/value pair.
sub __snapshot_foreach_volume {
my ($class, $conf, $func) = @_;
$class->__snapshot_freeze($vmid, 0);
}
+ $class->__snapshot_create_vol_snapshots_hook($vmid, $snap, $running, "before");
+
$class->__snapshot_foreach_volume($snap, sub {
my ($vs, $volume) = @_;
my $err = $@;
if ($running) {
+ $class->__snapshot_create_vol_snapshots_hook($vmid, $snap, $running, "after");
if ($freezefs) {
$class->__snapshot_freeze($vmid, 1);
}
+ $class->__snapshot_create_vol_snapshots_hook($vmid, $snap, $running, "after-unfreeze");
}
if ($err) {
my $snap;
my $unused = [];
+ $class->set_lock($vmid, 'snapshot-delete')
+ if (!$drivehash); # doesn't already have a 'snapshot' lock
+
my $unlink_parent = sub {
my ($confref, $new_parent) = @_;
}
};
- my $updatefn = sub {
- my ($remove_drive) = @_;
+ my $remove_drive = sub {
+ my ($drive) = @_;
my $conf = $class->load_config($vmid);
+ $snap = $conf->{snapshots}->{$snapname};
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
- if (!$drivehash) {
- $class->check_lock($conf);
- die "you can't delete a snapshot if vm is a template\n"
- if $class->is_template($conf);
- }
+ $class->__snapshot_delete_remove_drive($snap, $drive);
- $snap = $conf->{snapshots}->{$snapname};
+ $class->write_config($vmid, $conf);
+ };
- die "snapshot '$snapname' does not exist\n" if !defined($snap);
+ #prepare
+ $class->lock_config($vmid, sub {
+ my $conf = $class->load_config($vmid);
- # 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});
- }
- }
+ die "you can't delete a snapshot if vm is a template\n"
+ if $class->is_template($conf);
- if ($remove_drive) {
- $class->__snapshot_delete_remove_drive($snap, $remove_drive);
- }
+ $snap = $conf->{snapshots}->{$snapname};
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
- if ($prepare) {
- $snap->{snapstate} = 'delete';
- } else {
- delete $conf->{snapshots}->{$snapname};
- delete $conf->{lock} if $drivehash;
- foreach my $volid (@$unused) {
- $class->add_unused_volume($conf, $volid);
- }
- }
+ $snap->{snapstate} = 'delete';
$class->write_config($vmid, $conf);
- };
-
- $class->lock_config($vmid, $updatefn);
+ });
# now remove vmstate file
if ($snap->{vmstate}) {
$class->__snapshot_delete_vmstate_file($snap, $force);
# save changes (remove vmstate from snapshot)
- $class->lock_config($vmid, $updatefn, 'vmstate') if !$force;
+ $class->lock_config($vmid, $remove_drive, 'vmstate') if !$force;
};
# now remove all volume snapshots
return if $snapname eq 'vzdump' && $vs ne 'rootfs' && !$volume->{backup};
if (!$drivehash || $drivehash->{$vs}) {
- eval { $class->__snapshot_delete_vol_snapshot($vmid, $vs, $volume, $snapname); };
+ eval { $class->__snapshot_delete_vol_snapshot($vmid, $vs, $volume, $snapname, $unused); };
if (my $err = $@) {
die $err if !$force;
warn $err;
}
}
- # save changes (remove mp from snapshot)
- $class->lock_config($vmid, $updatefn, $vs) if !$force;
- push @$unused, $volume->{volume};
+ # save changes (remove drive from snapshot)
+ $class->lock_config($vmid, $remove_drive, $vs) if !$force;
});
# now cleanup config
- $prepare = 0;
- $class->lock_config($vmid, $updatefn);
+ $class->lock_config($vmid, sub {
+ my $conf = $class->load_config($vmid);
+ $snap = $conf->{snapshots}->{$snapname};
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
+ # remove parent refs
+ &$unlink_parent($conf, $snap->{parent});
+ foreach my $sn (keys %{$conf->{snapshots}}) {
+ next if $sn eq $snapname;
+ &$unlink_parent($conf->{snapshots}->{$sn}, $snap->{parent});
+ }
+
+
+ delete $conf->{snapshots}->{$snapname};
+ delete $conf->{lock};
+ foreach my $volid (@$unused) {
+ $class->add_unused_volume($conf, $volid);
+ }
+
+ $class->write_config($vmid, $conf);
+ });
}
# Rolls back to a given snapshot.
my $forcemachine;
if (!$prepare) {
+ my $unused = $class->__snapshot_rollback_get_unused($conf, $snap);
+
+ foreach my $volid (@$unused) {
+ $class->add_unused_volume($conf, $volid);
+ }
+
my $has_machine_config = defined($conf->{machine});
# copy snapshot config to current config