vmconfig_apply_pending: add error handling

wrap around code which can possibly fail in evals to handle them
gracefully, and log errors.

note: this results in a change of behavior in the API. since errors
are handled gracefully instead of "die"ing, when there is a pending
change which cannot be applied for some reason, it will get logged in
the tasklog but the vm will continue booting regardless. the
non-applied change will stay in the pending section of the
configuration.

Signed-off-by: Oguz Bektas <o.bektas@proxmox.com>
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Oguz Bektas 2020-01-07 16:55:18 +01:00 committed by Thomas Lamprecht
parent c96173968a
commit eb5e482ded
2 changed files with 45 additions and 22 deletions

View File

@ -1242,13 +1242,13 @@ my $update_vm_api = sub {
$conf = PVE::QemuConfig->load_config($vmid); # update/reload $conf = PVE::QemuConfig->load_config($vmid); # update/reload
my $errors = {};
if ($running) { if ($running) {
my $errors = {};
PVE::QemuServer::vmconfig_hotplug_pending($vmid, $conf, $storecfg, $modified, $errors); PVE::QemuServer::vmconfig_hotplug_pending($vmid, $conf, $storecfg, $modified, $errors);
raise_param_exc($errors) if scalar(keys %$errors);
} else { } else {
PVE::QemuServer::vmconfig_apply_pending($vmid, $conf, $storecfg, $running); PVE::QemuServer::vmconfig_apply_pending($vmid, $conf, $storecfg, $running, $errors);
} }
raise_param_exc($errors) if scalar(keys %$errors);
return; return;
}; };

View File

@ -4977,23 +4977,39 @@ sub vmconfig_delete_or_detach_drive {
sub vmconfig_apply_pending { sub vmconfig_apply_pending {
my ($vmid, $conf, $storecfg) = @_; my ($vmid, $conf, $storecfg, $errors) = @_;
my $add_apply_error = sub {
my ($opt, $msg) = @_;
my $err_msg = "unable to apply pending change $opt : $msg";
$errors->{$opt} = $err_msg;
warn $err_msg;
};
# cold plug # cold plug
my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete}); my $pending_delete_hash = PVE::QemuConfig->parse_pending_delete($conf->{pending}->{delete});
foreach my $opt (sort keys %$pending_delete_hash) { foreach my $opt (sort keys %$pending_delete_hash) {
die "internal error" if $opt =~ m/^unused/;
my $force = $pending_delete_hash->{$opt}->{force}; my $force = $pending_delete_hash->{$opt}->{force};
$conf = PVE::QemuConfig->load_config($vmid); # update/reload eval {
if (!defined($conf->{$opt})) { die "internal error" if $opt =~ m/^unused/;
PVE::QemuConfig->remove_from_pending_delete($conf, $opt); $conf = PVE::QemuConfig->load_config($vmid); # update/reload
PVE::QemuConfig->write_config($vmid, $conf); if (!defined($conf->{$opt})) {
} elsif (is_valid_drivename($opt)) { PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force); PVE::QemuConfig->write_config($vmid, $conf);
PVE::QemuConfig->remove_from_pending_delete($conf, $opt); } elsif (is_valid_drivename($opt)) {
delete $conf->{$opt}; vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf); PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
delete $conf->{$opt};
PVE::QemuConfig->write_config($vmid, $conf);
} else {
PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
delete $conf->{$opt};
PVE::QemuConfig->write_config($vmid, $conf);
}
};
if (my $err = $@) {
$add_apply_error->($opt, $err);
} else { } else {
PVE::QemuConfig->remove_from_pending_delete($conf, $opt); PVE::QemuConfig->remove_from_pending_delete($conf, $opt);
delete $conf->{$opt}; delete $conf->{$opt};
@ -5006,17 +5022,24 @@ sub vmconfig_apply_pending {
foreach my $opt (keys %{$conf->{pending}}) { # add/change foreach my $opt (keys %{$conf->{pending}}) { # add/change
$conf = PVE::QemuConfig->load_config($vmid); # update/reload $conf = PVE::QemuConfig->load_config($vmid); # update/reload
if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) { eval {
# skip if nothing changed if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) {
} elsif (is_valid_drivename($opt)) { # skip if nothing changed
vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt})) } elsif (is_valid_drivename($opt)) {
if defined($conf->{$opt}); vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
$conf->{$opt} = $conf->{pending}->{$opt}; if defined($conf->{$opt});
$conf->{$opt} = $conf->{pending}->{$opt};
} else {
$conf->{$opt} = $conf->{pending}->{$opt};
}
};
if (my $err = $@) {
$add_apply_error->($opt, $err);
} else { } else {
$conf->{$opt} = $conf->{pending}->{$opt}; $conf->{$opt} = delete $conf->{pending}->{$opt};
PVE::QemuConfig->cleanup_pending($conf);
} }
delete $conf->{pending}->{$opt};
PVE::QemuConfig->write_config($vmid, $conf); PVE::QemuConfig->write_config($vmid, $conf);
} }
} }