PVE::Cluster::cfs_write_file($cfspath, $conf);
}
+# Pending changes related
+
+sub parse_pending_delete {
+ my ($class, $data) = @_;
+ $data ||= '';
+ $data =~ s/[,;]/ /g;
+ $data =~ s/^\s+//;
+ return { map { /^(!?)(.*)$/ && ($2, { 'force' => $1 eq '!' ? 1 : 0 } ) } ($data =~ /\S+/g) };
+}
+
+sub print_pending_delete {
+ my ($class, $delete_hash) = @_;
+ join ",", map { ( $delete_hash->{$_}->{force} ? '!' : '' ) . $_ } keys %$delete_hash;
+}
+
+sub add_to_pending_delete {
+ my ($class, $conf, $key, $force) = @_;
+
+ delete $conf->{pending}->{$key};
+ my $pending_delete_hash = $class->parse_pending_delete($conf->{pending}->{delete});
+ $pending_delete_hash->{$key}->{force} = $force;
+ $conf->{pending}->{delete} = $class->print_pending_delete($pending_delete_hash);
+}
+
+sub remove_from_pending_delete {
+ my ($class, $conf, $key) = @_;
+
+ my $pending_delete_hash = $class->parse_pending_delete($conf->{pending}->{delete});
+ delete $pending_delete_hash->{$key};
+
+ if (%$pending_delete_hash) {
+ $conf->{pending}->{delete} = $class->print_pending_delete($pending_delete_hash);
+ } else {
+ delete $conf->{pending}->{delete};
+ }
+}
+
+sub cleanup_pending {
+ my ($class, $conf) = @_;
+
+ # remove pending changes when nothing changed
+ my $changes;
+ foreach my $opt (keys %{$conf->{pending}}) {
+ next if $opt eq 'delete'; # just to be sure
+ if (defined($conf->{$opt}) && ($conf->{pending}->{$opt} eq $conf->{$opt})) {
+ $changes = 1;
+ delete $conf->{pending}->{$opt};
+ }
+ }
+
+ my $current_delete_hash = $class->parse_pending_delete($conf->{pending}->{delete});
+ my $pending_delete_hash = {};
+ while (my ($opt, $force) = each %$current_delete_hash) {
+ if (defined($conf->{$opt})) {
+ $pending_delete_hash->{$opt} = $force;
+ } else {
+ $changes = 1;
+ }
+ }
+
+ if (%$pending_delete_hash) {
+ $conf->{pending}->{delete} = $class->print_pending_delete($pending_delete_hash);
+ } else {
+ delete $conf->{pending}->{delete};
+ }
+
+ return $changes;
+}
+
# Lock config file using flock, run $code with @param, unlock config file.
# $timeout is the maximum time to aquire the flock
sub lock_config_full {
return $res;
}
+sub create_and_lock_config {
+ my ($class, $vmid, $allow_existing) = @_;
+
+ $class->lock_config_full($vmid, 5, sub {
+ PVE::Cluster::check_vmid_unused($vmid, $allow_existing);
+
+ my $conf = eval { $class->load_config($vmid) } || {};
+ $class->check_lock($conf);
+ $conf->{lock} = 'create';
+ $class->write_config($vmid, $conf);
+ });
+}
+
+# destroys configuration, only applyable for configs owned by the callers node.
+# dies if removal fails, e.g., when inquorate.
+sub destroy_config {
+ my ($class, $vmid) = @_;
+
+ my $config_fn = $class->config_file($vmid, $nodename);
+ unlink $config_fn or die "failed to remove config file: $!\n";
+}
+
# Lock config file using flock, run $code with @param, unlock config file.
# $timeout is the maximum time to aquire the flock
# $shared eq 1 creates a non-exclusive ("read") flock
# $cleanup: for cleanup - simply ignores volumes without replicate feature
# $norerr: never raise exceptions - return undef instead
sub get_replicatable_volumes {
- my ($class, $storecfg, $conf, $cleanup, $noerr) = @_;
+ my ($class, $storecfg, $vmid, $conf, $cleanup, $noerr) = @_;
die "implement me - abstract method\n";
}
die "abstract method - implement me\n";
}
+# called during rollback prepare, and between config rollback and starting guest
+# can change config, e.g. for vmgenid
+# $data is shared across calls and passed to vm_start
+sub __snapshot_rollback_hook {
+ my ($class, $vmid, $conf, $snap, $prepare, $data) = @_;
+
+ return;
+}
+
# Checks whether a volume snapshot is possible for this volume.
sub __snapshot_rollback_vol_possible {
my ($class, $volume, $snapname) = @_;
# Start the VM/CT after a rollback with restored vmstate.
sub __snapshot_rollback_vm_start {
- my ($class, $vmid, $vmstate, $forcemachine);
+ my ($class, $vmid, $vmstate, $data);
die "abstract method - implement me\n";
}
my $prepare = 1;
- my $snap;
my $unused = [];
+ my $conf = $class->load_config($vmid);
+ my $snap = $conf->{snapshots}->{$snapname};
+
+ die "snapshot '$snapname' does not exist\n" if !defined($snap);
+
$class->set_lock($vmid, 'snapshot-delete')
if (!$drivehash); # doesn't already have a 'snapshot' lock
my $repl_conf = PVE::ReplicationConfig->new();
if ($repl_conf->check_for_existing_jobs($vmid, 1)) {
# remove all replication snapshots
- my $volumes = $class->get_replicatable_volumes($storecfg, $conf, 1);
+ my $volumes = $class->get_replicatable_volumes($storecfg, $vmid, $conf, 1);
my $sorted_volids = [ sort keys %$volumes ];
# remove all local replication snapshots (jobid => undef)
$class->__snapshot_rollback_vol_possible($volume, $snapname);
});
+ my $data = {};
+
my $updatefn = sub {
$conf = $class->load_config($vmid);
} else {
die "got wrong lock\n" if !($conf->{lock} && $conf->{lock} eq 'rollback');
delete $conf->{lock};
- }
-
- # machine only relevant for Qemu
- 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
$conf = $class->__snapshot_apply_config($conf, $snap);
$conf->{parent} = $snapname;
-
- # Note: old code did not store 'machine', so we try to be smart
- # and guess the snapshot was generated with kvm 1.4 (pc-i440fx-1.4).
- $forcemachine = $conf->{machine} || 'pc-i440fx-1.4';
- # we remove the 'machine' configuration if not explicitly specified
- # in the original config.
- delete $conf->{machine} if $snap->{vmstate} && !$has_machine_config;
}
+ $class->__snapshot_rollback_hook($vmid, $conf, $snap, $prepare, $data);
+
$class->write_config($vmid, $conf);
if (!$prepare && $snap->{vmstate}) {
- $class->__snapshot_rollback_vm_start($vmid, $snap->{vmstate}, $forcemachine);
+ $class->__snapshot_rollback_vm_start($vmid, $snap->{vmstate}, $data);
}
};
$class->lock_config($vmid, $updatefn);
}
+# bash completion helper
+
+sub snapshot_list {
+ my ($class, $vmid) = @_;
+
+ my $snapshot = eval {
+ my $conf = $class->load_config($vmid);
+ my $snapshots = $conf->{snapshots} || [];
+ [ sort keys %$snapshots ]
+ } || [];
+
+ return $snapshot;
+}
+
1;