use PVE::Cluster;
use PVE::Storage;
+use PVE::GuestHelpers qw(typesafe_ne);
use PVE::ReplicationConfig;
use PVE::Replication;
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);
+ $conf->{pending} //= {};
+ my $pending = $conf->{pending};
+ my $pending_delete_hash = $class->parse_pending_delete($pending->{delete});
+
+ $pending_delete_hash->{$key} = { force => $force };
+
+ $pending->{delete} = $class->print_pending_delete($pending_delete_hash);
+
+ return $conf;
}
sub remove_from_pending_delete {
my ($class, $conf, $key) = @_;
- my $pending_delete_hash = $class->parse_pending_delete($conf->{pending}->{delete});
+ my $pending = $conf->{pending};
+ my $pending_delete_hash = $class->parse_pending_delete($pending->{delete});
+
+ return $conf if ! exists $pending_delete_hash->{$key};
+
delete $pending_delete_hash->{$key};
if (%$pending_delete_hash) {
- $conf->{pending}->{delete} = $class->print_pending_delete($pending_delete_hash);
+ $pending->{delete} = $class->print_pending_delete($pending_delete_hash);
} else {
- delete $conf->{pending}->{delete};
+ delete $pending->{delete};
}
+
+ return $conf;
}
sub cleanup_pending {
my ($class, $conf) = @_;
+ my $pending = $conf->{pending};
# 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})) {
+ if (defined($conf->{$opt}) && ($pending->{$opt} eq $conf->{$opt})) {
$changes = 1;
- delete $conf->{pending}->{$opt};
+ delete $pending->{$opt};
}
}
- my $current_delete_hash = $class->parse_pending_delete($conf->{pending}->{delete});
+ my $current_delete_hash = $class->parse_pending_delete($pending->{delete});
my $pending_delete_hash = {};
- while (my ($opt, $force) = each %$current_delete_hash) {
+ for my $opt (keys %$current_delete_hash) {
if (defined($conf->{$opt})) {
- $pending_delete_hash->{$opt} = $force;
+ $pending_delete_hash->{$opt} = $current_delete_hash->{$opt};
} else {
$changes = 1;
}
}
if (%$pending_delete_hash) {
- $conf->{pending}->{delete} = $class->print_pending_delete($pending_delete_hash);
+ $pending->{delete} = $class->print_pending_delete($pending_delete_hash);
} else {
- delete $conf->{pending}->{delete};
+ delete $pending->{delete};
}
return $changes;
}
+sub get_partial_fast_plug_option {
+ my ($class) = @_;
+
+ die "abstract method - implement me ";
+}
+
+sub partial_fast_plug {
+ my ($class, $conf, $opt) = @_;
+
+ my $partial_fast_plug_option = $class->get_partial_fast_plug_option();
+ return 0 if !$partial_fast_plug_option->{$opt};
+
+ my $format = $partial_fast_plug_option->{$opt}->{fmt};
+ my $fast_pluggable = $partial_fast_plug_option->{$opt}->{properties};
+
+ my $configured = {};
+ if (exists($conf->{$opt})) {
+ $configured = PVE::JSONSchema::parse_property_string($format, $conf->{$opt});
+ }
+ my $pending = PVE::JSONSchema::parse_property_string($format, $conf->{pending}->{$opt});
+
+ my $changes = 0;
+
+ # merge configured and pending opts to iterate
+ my @all_keys = keys %{{ %$pending, %$configured }};
+
+ foreach my $subopt (@all_keys) {
+ my $type = $format->{$subopt}->{type};
+ if (typesafe_ne($configured->{$subopt}, $pending->{$subopt}, $type)) {
+ if ($fast_pluggable->{$subopt}) {
+ $configured->{$subopt} = $pending->{$subopt};
+ $changes = 1
+ }
+ }
+ }
+
+ # if there're no keys in $configured (after merge) there shouldn't be anything to change
+ if (keys %$configured) {
+ $conf->{$opt} = PVE::JSONSchema::print_property_string($configured, $format);
+ }
+
+ return $changes;
+}
+
+
sub load_snapshot_config {
my ($class, $vmid, $snapname) = @_;
# Lock config file using flock, run $code with @param, unlock config file.
-# $timeout is the maximum time to aquire the flock
+# $timeout is the maximum time to acquire the flock
sub lock_config_full {
my ($class, $vmid, $timeout, $code, @param) = @_;
}
sub create_and_lock_config {
- my ($class, $vmid, $allow_existing) = @_;
+ my ($class, $vmid, $allow_existing, $lock) = @_;
$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';
+ $conf->{lock} = $lock // 'create';
$class->write_config($vmid, $conf);
});
}
-# destroys configuration, only applyable for configs owned by the callers node.
+# destroys configuration, only applicable for configs owned by the callers node.
# dies if removal fails, e.g., when inquorate.
sub destroy_config {
my ($class, $vmid) = @_;
}
# Lock config file using flock, run $code with @param, unlock config file.
-# $timeout is the maximum time to aquire the flock
+# $timeout is the maximum time to acquire the flock
# $shared eq 1 creates a non-exclusive ("read") flock
sub lock_config_mode {
my ($class, $vmid, $timeout, $shared, $code, @param) = @_;
return 1 if defined $conf->{template} && $conf->{template} == 1;
}
-# Checks whether $feature is availabe for the referenced volumes in $conf.
+# Checks whether $feature is available for the referenced volumes in $conf.
# Note: depending on the parameters, some volumes may be skipped!
sub has_feature {
my ($class, $feature, $conf, $storecfg, $snapname, $running, $backup_only) = @_;
die "implement me - abstract method\n";
}
+# Returns all the guests volumes which would be included in a vzdump job
+# Return Format (array-ref with hash-refs as elements):
+# [
+# {
+# key, key in the config, e.g. mp0, scsi0,...
+# included, boolean
+# reason, string
+# data volume object as returned from foreach_drive/foreach_mountpoint
+# },
+# ]
+sub get_backup_volumes {
+ my ($class, $conf) = @_;
+
+ die "implement me - abstract method\n";
+}
+
# Internal snapshots
# NOTE: Snapshot create/delete involves several non-atomic