[pve-devel] [PATCH qemu-server 2/2] move pending changes related functions from QemuServer to QemuConfig
Oguz Bektas
o.bektas at proxmox.com
Wed Sep 4 18:00:02 CEST 2019
we now inherit these from AbstractConfig, so they need to be
defined/overwritten in QemuConfig instead of QemuServer.
Signed-off-by: Oguz Bektas <o.bektas at proxmox.com>
---
this patch requires my previous patch from pve-guest-common.
everything should work the same as before when both patches are applied.
PVE/API2/Qemu.pm | 20 +--
PVE/QemuConfig.pm | 233 ++++++++++++++++++++++++++
PVE/QemuServer.pm | 306 +----------------------------------
PVE/QemuServer/ImportDisk.pm | 4 +-
4 files changed, 250 insertions(+), 313 deletions(-)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index b30931d..2958d8a 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -885,7 +885,7 @@ __PACKAGE__->register_method({
next if ref($value); # just to be sure
$conf->{$opt} = $value;
}
- my $pending_delete_hash = PVE::QemuServer::split_flagged_list($conf->{pending}->{delete});
+ my $pending_delete_hash = PVE::QemuConfig->split_flagged_list($conf->{pending}->{delete});
foreach my $opt (keys %$pending_delete_hash) {
delete $conf->{$opt} if $conf->{$opt};
}
@@ -952,7 +952,7 @@ __PACKAGE__->register_method({
my $conf = PVE::QemuConfig->load_config($param->{vmid});
- my $pending_delete_hash = PVE::QemuServer::split_flagged_list($conf->{pending}->{delete});
+ my $pending_delete_hash = PVE::QemuConfig->split_flagged_list($conf->{pending}->{delete});
my $res = [];
@@ -1199,7 +1199,7 @@ my $update_vm_api = sub {
$rpcenv->check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']);
PVE::QemuServer::vmconfig_register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf->{pending}->{$opt}))
if defined($conf->{pending}->{$opt});
- PVE::QemuServer::vmconfig_delete_pending_option($conf, $opt, $force);
+ PVE::QemuConfig->vmconfig_delete_pending_option($conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf);
} elsif ($opt =~ m/^serial\d+$/) {
if ($conf->{$opt} eq 'socket') {
@@ -1207,7 +1207,7 @@ my $update_vm_api = sub {
} elsif ($authuser ne 'root at pam') {
die "only root can delete '$opt' config for real devices\n";
}
- PVE::QemuServer::vmconfig_delete_pending_option($conf, $opt, $force);
+ PVE::QemuConfig->vmconfig_delete_pending_option($conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf);
} elsif ($opt =~ m/^usb\d+$/) {
if ($conf->{$opt} =~ m/spice/) {
@@ -1215,10 +1215,10 @@ my $update_vm_api = sub {
} elsif ($authuser ne 'root at pam') {
die "only root can delete '$opt' config for real devices\n";
}
- PVE::QemuServer::vmconfig_delete_pending_option($conf, $opt, $force);
+ PVE::QemuConfig->vmconfig_delete_pending_option($conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf);
} else {
- PVE::QemuServer::vmconfig_delete_pending_option($conf, $opt, $force);
+ PVE::QemuConfig->vmconfig_delete_pending_option($conf, $opt, $force);
PVE::QemuConfig->write_config($vmid, $conf);
}
}
@@ -1259,13 +1259,13 @@ my $update_vm_api = sub {
} else {
$conf->{pending}->{$opt} = $param->{$opt};
}
- PVE::QemuServer::vmconfig_undelete_pending_option($conf, $opt);
+ PVE::QemuConfig->vmconfig_undelete_pending_option($conf, $opt);
PVE::QemuConfig->write_config($vmid, $conf);
}
# remove pending changes when nothing changed
$conf = PVE::QemuConfig->load_config($vmid); # update/reload
- my $changes = PVE::QemuServer::vmconfig_cleanup_pending($conf);
+ my $changes = PVE::QemuConfig->vmconfig_cleanup_pending($conf);
PVE::QemuConfig->write_config($vmid, $conf) if $changes;
return if !scalar(keys %{$conf->{pending}});
@@ -1278,10 +1278,10 @@ my $update_vm_api = sub {
if ($running) {
my $errors = {};
- PVE::QemuServer::vmconfig_hotplug_pending($vmid, $conf, $storecfg, $modified, $errors);
+ PVE::QemuConfig->vmconfig_hotplug_pending($vmid, $conf, $storecfg, $modified, $errors);
raise_param_exc($errors) if scalar(keys %$errors);
} else {
- PVE::QemuServer::vmconfig_apply_pending($vmid, $conf, $storecfg, $running);
+ PVE::QemuConfig->vmconfig_apply_pending($vmid, $conf, $storecfg, $running);
}
return;
diff --git a/PVE/QemuConfig.pm b/PVE/QemuConfig.pm
index 84d601a..ea3d7ba 100644
--- a/PVE/QemuConfig.pm
+++ b/PVE/QemuConfig.pm
@@ -397,6 +397,239 @@ sub __snapshot_foreach_volume {
PVE::QemuServer::foreach_drive($conf, $func);
}
+
+# hotplug changes in [PENDING]
+# $selection hash can be used to only apply specified options, for
+# example: { cores => 1 } (only apply changed 'cores')
+# $errors ref is used to return error messages
+sub vmconfig_hotplug_pending {
+ my ($class, $vmid, $conf, $storecfg, $selection, $errors) = @_;
+
+ my $defaults = PVE::QemuServer::load_defaults();
+ my ($arch, $machine_type) = PVE::QemuServer::get_basic_machine_info($conf, undef);
+
+ # commit values which do not have any impact on running VM first
+ # Note: those option cannot raise errors, we we do not care about
+ # $selection and always apply them.
+
+ my $add_error = sub {
+ my ($opt, $msg) = @_;
+ $errors->{$opt} = "hotplug problem - $msg";
+ };
+
+ my $changes = 0;
+ foreach my $opt (keys %{$conf->{pending}}) { # add/change
+ if ($PVE::QemuServer::fast_plug_option->{$opt}) {
+ $conf->{$opt} = $conf->{pending}->{$opt};
+ delete $conf->{pending}->{$opt};
+ $changes = 1;
+ }
+ }
+
+ if ($changes) {
+ $class->write_config($vmid, $conf);
+ $conf = $class->load_config($vmid); # update/reload
+ }
+
+ my $hotplug_features = PVE::QemuServer::parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
+
+ my $pending_delete_hash = $class->split_flagged_list($conf->{pending}->{delete});
+ while (my ($opt, $force) = each %$pending_delete_hash) {
+ next if $selection && !$selection->{$opt};
+ eval {
+ if ($opt eq 'hotplug') {
+ die "skip\n" if ($conf->{hotplug} =~ /memory/);
+ } elsif ($opt eq 'tablet') {
+ die "skip\n" if !$hotplug_features->{usb};
+ if ($defaults->{tablet}) {
+ PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
+ PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
+ if $arch eq 'aarch64';
+ } else {
+ PVE::QemuServer::vm_deviceunplug($vmid, $conf, 'tablet');
+ PVE::Qemuservervm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
+ }
+ } elsif ($opt =~ m/^usb\d+/) {
+ die "skip\n";
+ # since we cannot reliably hot unplug usb devices
+ # we are disabling it
+ die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
+ PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
+ } elsif ($opt eq 'vcpus') {
+ die "skip\n" if !$hotplug_features->{cpu};
+ PVE::Qemuserver::qemu_cpu_hotplug($vmid, $conf, undef);
+ } elsif ($opt eq 'balloon') {
+ # enable balloon device is not hotpluggable
+ die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
+ # here we reset the ballooning value to memory
+ my $balloon = $conf->{memory} || $defaults->{memory};
+ PVE::QemuServer::vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
+ } elsif ($PVE::QemuServer::fast_plug_option->{$opt}) {
+ # do nothing
+ } elsif ($opt =~ m/^net(\d+)$/) {
+ die "skip\n" if !$hotplug_features->{network};
+ PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
+ } elsif (PVE::QemuServer::is_valid_drivename($opt)) {
+ die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
+ PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt);
+ PVE::QemuServer::vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
+ } elsif ($opt =~ m/^memory$/) {
+ die "skip\n" if !$hotplug_features->{memory};
+ PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
+ } elsif ($opt eq 'cpuunits') {
+ PVE::QemuServer::cgroups_write("cpu", $vmid, "cpu.shares", $defaults->{cpuunits});
+ } elsif ($opt eq 'cpulimit') {
+ PVE::QemuServer::cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", -1);
+ } else {
+ die "skip\n";
+ }
+ };
+ if (my $err = $@) {
+ &$add_error($opt, $err) if $err ne "skip\n";
+ } else {
+ # save new config if hotplug was successful
+ delete $conf->{$opt};
+ $class->vmconfig_undelete_pending_option($conf, $opt);
+ $class->write_config($vmid, $conf);
+ $conf = $class->load_config($vmid); # update/reload
+ }
+ }
+
+ my $apply_pending_cloudinit;
+ $apply_pending_cloudinit = sub {
+ my ($key, $value) = @_;
+ $apply_pending_cloudinit = sub {}; # once is enough
+
+ my @cloudinit_opts = keys %$PVE::QemuServer::confdesc_cloudinit;
+ foreach my $opt (keys %{$conf->{pending}}) {
+ next if !grep { $_ eq $opt } @cloudinit_opts;
+ $conf->{$opt} = delete $conf->{pending}->{$opt};
+ }
+
+ my $new_conf = { %$conf };
+ $new_conf->{$key} = $value;
+ PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
+ };
+
+ foreach my $opt (keys %{$conf->{pending}}) {
+ next if $selection && !$selection->{$opt};
+ my $value = $conf->{pending}->{$opt};
+ eval {
+ if ($opt eq 'hotplug') {
+ die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
+ } elsif ($opt eq 'tablet') {
+ die "skip\n" if !$hotplug_features->{usb};
+ if ($value == 1) {
+ PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
+ PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
+ if $arch eq 'aarch64';
+ } elsif ($value == 0) {
+ PVE::QemuServer::vm_deviceunplug($vmid, $conf, 'tablet');
+ PVE::QemuServer::vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
+ }
+ } elsif ($opt =~ m/^usb\d+$/) {
+ die "skip\n";
+ # since we cannot reliably hot unplug usb devices
+ # we are disabling it
+ die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
+ my $d = eval { PVE::JSONSchema::parse_property_string($PVE::QemuServer::usbdesc->{format}, $value) };
+ die "skip\n" if !$d;
+ PVE::QemuServer::qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
+ } elsif ($opt eq 'vcpus') {
+ die "skip\n" if !$hotplug_features->{cpu};
+ PVE::QemuServer::qemu_cpu_hotplug($vmid, $conf, $value);
+ } elsif ($opt eq 'balloon') {
+ # enable/disable balloning device is not hotpluggable
+ my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
+ my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
+ die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
+
+ # allow manual ballooning if shares is set to zero
+ if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
+ my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
+ PVE::QemuServer::vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
+ }
+ } elsif ($opt =~ m/^net(\d+)$/) {
+ # some changes can be done without hotplug
+ PVE::QemuServer::vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
+ $vmid, $opt, $value, $arch, $machine_type);
+ } elsif (PVE::QemuServer::is_valid_drivename($opt)) {
+ # some changes can be done without hotplug
+ my $drive = PVE::QemuServer::parse_drive($opt, $value);
+ if (PVE::QemuServer::drive_is_cloudinit($drive)) {
+ &$apply_pending_cloudinit($opt, $value);
+ }
+ PVE::QemuServer::vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
+ $vmid, $opt, $value, 1, $arch, $machine_type);
+ } elsif ($opt =~ m/^memory$/) { #dimms
+ die "skip\n" if !$hotplug_features->{memory};
+ $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
+ } elsif ($opt eq 'cpuunits') {
+ PVE::QemuServer::cgroups_write("cpu", $vmid, "cpu.shares", $conf->{pending}->{$opt});
+ } elsif ($opt eq 'cpulimit') {
+ my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
+ PVE::QemuServer::cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", $cpulimit);
+ } else {
+ die "skip\n"; # skip non-hot-pluggable options
+ }
+ };
+ if (my $err = $@) {
+ &$add_error($opt, $err) if $err ne "skip\n";
+ } else {
+ # save new config if hotplug was successful
+ $conf->{$opt} = $value;
+ delete $conf->{pending}->{$opt};
+ $class->write_config($vmid, $conf);
+ $conf = $class->load_config($vmid); # update/reload
+ }
+ }
+}
+
+sub vmconfig_apply_pending {
+ my ($class, $vmid, $conf, $storecfg) = @_;
+
+ # cold plug
+
+ my $pending_delete_hash = $class->split_flagged_list($conf->{pending}->{delete});
+ while (my ($opt, $force) = each %$pending_delete_hash) {
+ die "internal error" if $opt =~ m/^unused/;
+ $conf = $class->load_config($vmid); # update/reload
+ if (!defined($conf->{$opt})) {
+ $class->vmconfig_undelete_pending_option($conf, $opt);
+ $class->write_config($vmid, $conf);
+ } elsif (PVE::QemuServer::is_valid_drivename($opt)) {
+ PVE::QemuServer::vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
+ $class->vmconfig_undelete_pending_option($conf, $opt);
+ delete $conf->{$opt};
+ $class->write_config($vmid, $conf);
+ } else {
+ $class->vmconfig_undelete_pending_option($conf, $opt);
+ delete $conf->{$opt};
+ $class->write_config($vmid, $conf);
+ }
+ }
+
+ $conf = $class->load_config($vmid); # update/reload
+
+ foreach my $opt (keys %{$conf->{pending}}) { # add/change
+ $conf = $class->load_config($vmid); # update/reload
+
+ if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) {
+ # skip if nothing changed
+ } elsif (PVE::QemuServer::is_valid_drivename($opt)) {
+ PVE::QemuServer::vmconfig_register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf->{$opt}))
+ if defined($conf->{$opt});
+ $conf->{$opt} = $conf->{pending}->{$opt};
+ } else {
+ $conf->{$opt} = $conf->{pending}->{$opt};
+ }
+
+ delete $conf->{pending}->{$opt};
+ $class->write_config($vmid, $conf);
+ }
+}
+
+
# END implemented abstract methods from PVE::AbstractConfig
1;
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 6e3b19e..7ff45f4 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -700,7 +700,7 @@ my $cicustom_fmt = {
};
PVE::JSONSchema::register_format('pve-qm-cicustom', $cicustom_fmt);
-my $confdesc_cloudinit = {
+our $confdesc_cloudinit = {
citype => {
optional => 1,
type => 'string',
@@ -1302,7 +1302,7 @@ EODESCR
},
};
-my $usbdesc = {
+our $usbdesc = {
optional => 1,
type => 'string', format => $usb_fmt,
description => "Configure an USB device (n is 0 to 4).",
@@ -2323,40 +2323,6 @@ sub vm_is_volid_owner {
return undef;
}
-sub split_flagged_list {
- my $text = shift || '';
- $text =~ s/[,;]/ /g;
- $text =~ s/^\s+//;
- return { map { /^(!?)(.*)$/ && ($2, $1) } ($text =~ /\S+/g) };
-}
-
-sub join_flagged_list {
- my ($how, $lst) = @_;
- join $how, map { $lst->{$_} . $_ } keys %$lst;
-}
-
-sub vmconfig_delete_pending_option {
- my ($conf, $key, $force) = @_;
-
- delete $conf->{pending}->{$key};
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- $pending_delete_hash->{$key} = $force ? '!' : '';
- $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
-}
-
-sub vmconfig_undelete_pending_option {
- my ($conf, $key) = @_;
-
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- delete $pending_delete_hash->{$key};
-
- if (%$pending_delete_hash) {
- $conf->{pending}->{delete} = join_flagged_list(',', $pending_delete_hash);
- } else {
- delete $conf->{pending}->{delete};
- }
-}
-
sub vmconfig_register_unused_drive {
my ($storecfg, $vmid, $conf, $drive) = @_;
@@ -2371,37 +2337,6 @@ sub vmconfig_register_unused_drive {
}
}
-sub vmconfig_cleanup_pending {
- my ($conf) = @_;
-
- # remove pending changes when nothing changed
- my $changes;
- foreach my $opt (keys %{$conf->{pending}}) {
- if (defined($conf->{$opt}) && ($conf->{pending}->{$opt} eq $conf->{$opt})) {
- $changes = 1;
- delete $conf->{pending}->{$opt};
- }
- }
-
- my $current_delete_hash = split_flagged_list($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} = join_flagged_list(',', $pending_delete_hash);
- } else {
- delete $conf->{pending}->{delete};
- }
-
- return $changes;
-}
-
# smbios: [manufacturer=str][,product=str][,version=str][,serial=str][,uuid=uuid][,sku=str][,family=str][,base64=bool]
my $smbios1_fmt = {
uuid => {
@@ -4839,7 +4774,7 @@ sub set_migration_caps {
vm_mon_cmd_nocheck($vmid, "migrate-set-capabilities", capabilities => $cap_ref);
}
-my $fast_plug_option = {
+our $fast_plug_option = {
'lock' => 1,
'name' => 1,
'onboot' => 1,
@@ -4851,193 +4786,6 @@ my $fast_plug_option = {
'hookscript' => 1,
};
-# hotplug changes in [PENDING]
-# $selection hash can be used to only apply specified options, for
-# example: { cores => 1 } (only apply changed 'cores')
-# $errors ref is used to return error messages
-sub vmconfig_hotplug_pending {
- my ($vmid, $conf, $storecfg, $selection, $errors) = @_;
-
- my $defaults = load_defaults();
- my ($arch, $machine_type) = get_basic_machine_info($conf, undef);
-
- # commit values which do not have any impact on running VM first
- # Note: those option cannot raise errors, we we do not care about
- # $selection and always apply them.
-
- my $add_error = sub {
- my ($opt, $msg) = @_;
- $errors->{$opt} = "hotplug problem - $msg";
- };
-
- my $changes = 0;
- foreach my $opt (keys %{$conf->{pending}}) { # add/change
- if ($fast_plug_option->{$opt}) {
- $conf->{$opt} = $conf->{pending}->{$opt};
- delete $conf->{pending}->{$opt};
- $changes = 1;
- }
- }
-
- if ($changes) {
- PVE::QemuConfig->write_config($vmid, $conf);
- $conf = PVE::QemuConfig->load_config($vmid); # update/reload
- }
-
- my $hotplug_features = parse_hotplug_features(defined($conf->{hotplug}) ? $conf->{hotplug} : '1');
-
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- while (my ($opt, $force) = each %$pending_delete_hash) {
- next if $selection && !$selection->{$opt};
- eval {
- if ($opt eq 'hotplug') {
- die "skip\n" if ($conf->{hotplug} =~ /memory/);
- } elsif ($opt eq 'tablet') {
- die "skip\n" if !$hotplug_features->{usb};
- if ($defaults->{tablet}) {
- vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
- vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
- if $arch eq 'aarch64';
- } else {
- vm_deviceunplug($vmid, $conf, 'tablet');
- vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
- }
- } elsif ($opt =~ m/^usb\d+/) {
- die "skip\n";
- # since we cannot reliably hot unplug usb devices
- # we are disabling it
- die "skip\n" if !$hotplug_features->{usb} || $conf->{$opt} =~ m/spice/i;
- vm_deviceunplug($vmid, $conf, $opt);
- } elsif ($opt eq 'vcpus') {
- die "skip\n" if !$hotplug_features->{cpu};
- qemu_cpu_hotplug($vmid, $conf, undef);
- } elsif ($opt eq 'balloon') {
- # enable balloon device is not hotpluggable
- die "skip\n" if defined($conf->{balloon}) && $conf->{balloon} == 0;
- # here we reset the ballooning value to memory
- my $balloon = $conf->{memory} || $defaults->{memory};
- vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
- } elsif ($fast_plug_option->{$opt}) {
- # do nothing
- } elsif ($opt =~ m/^net(\d+)$/) {
- die "skip\n" if !$hotplug_features->{network};
- vm_deviceunplug($vmid, $conf, $opt);
- } elsif (is_valid_drivename($opt)) {
- die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
- vm_deviceunplug($vmid, $conf, $opt);
- vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
- } elsif ($opt =~ m/^memory$/) {
- die "skip\n" if !$hotplug_features->{memory};
- PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt);
- } elsif ($opt eq 'cpuunits') {
- cgroups_write("cpu", $vmid, "cpu.shares", $defaults->{cpuunits});
- } elsif ($opt eq 'cpulimit') {
- cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", -1);
- } else {
- die "skip\n";
- }
- };
- if (my $err = $@) {
- &$add_error($opt, $err) if $err ne "skip\n";
- } else {
- # save new config if hotplug was successful
- delete $conf->{$opt};
- vmconfig_undelete_pending_option($conf, $opt);
- PVE::QemuConfig->write_config($vmid, $conf);
- $conf = PVE::QemuConfig->load_config($vmid); # update/reload
- }
- }
-
- my $apply_pending_cloudinit;
- $apply_pending_cloudinit = sub {
- my ($key, $value) = @_;
- $apply_pending_cloudinit = sub {}; # once is enough
-
- my @cloudinit_opts = keys %$confdesc_cloudinit;
- foreach my $opt (keys %{$conf->{pending}}) {
- next if !grep { $_ eq $opt } @cloudinit_opts;
- $conf->{$opt} = delete $conf->{pending}->{$opt};
- }
-
- my $new_conf = { %$conf };
- $new_conf->{$key} = $value;
- PVE::QemuServer::Cloudinit::generate_cloudinitconfig($new_conf, $vmid);
- };
-
- foreach my $opt (keys %{$conf->{pending}}) {
- next if $selection && !$selection->{$opt};
- my $value = $conf->{pending}->{$opt};
- eval {
- if ($opt eq 'hotplug') {
- die "skip\n" if ($value =~ /memory/) || ($value !~ /memory/ && $conf->{hotplug} =~ /memory/);
- } elsif ($opt eq 'tablet') {
- die "skip\n" if !$hotplug_features->{usb};
- if ($value == 1) {
- vm_deviceplug($storecfg, $conf, $vmid, 'tablet', $arch, $machine_type);
- vm_deviceplug($storecfg, $conf, $vmid, 'keyboard', $arch, $machine_type)
- if $arch eq 'aarch64';
- } elsif ($value == 0) {
- vm_deviceunplug($vmid, $conf, 'tablet');
- vm_deviceunplug($vmid, $conf, 'keyboard') if $arch eq 'aarch64';
- }
- } elsif ($opt =~ m/^usb\d+$/) {
- die "skip\n";
- # since we cannot reliably hot unplug usb devices
- # we are disabling it
- die "skip\n" if !$hotplug_features->{usb} || $value =~ m/spice/i;
- my $d = eval { PVE::JSONSchema::parse_property_string($usbdesc->{format}, $value) };
- die "skip\n" if !$d;
- qemu_usb_hotplug($storecfg, $conf, $vmid, $opt, $d, $arch, $machine_type);
- } elsif ($opt eq 'vcpus') {
- die "skip\n" if !$hotplug_features->{cpu};
- qemu_cpu_hotplug($vmid, $conf, $value);
- } elsif ($opt eq 'balloon') {
- # enable/disable balloning device is not hotpluggable
- my $old_balloon_enabled = !!(!defined($conf->{balloon}) || $conf->{balloon});
- my $new_balloon_enabled = !!(!defined($conf->{pending}->{balloon}) || $conf->{pending}->{balloon});
- die "skip\n" if $old_balloon_enabled != $new_balloon_enabled;
-
- # allow manual ballooning if shares is set to zero
- if ((defined($conf->{shares}) && ($conf->{shares} == 0))) {
- my $balloon = $conf->{pending}->{balloon} || $conf->{memory} || $defaults->{memory};
- vm_mon_cmd($vmid, "balloon", value => $balloon*1024*1024);
- }
- } elsif ($opt =~ m/^net(\d+)$/) {
- # some changes can be done without hotplug
- vmconfig_update_net($storecfg, $conf, $hotplug_features->{network},
- $vmid, $opt, $value, $arch, $machine_type);
- } elsif (is_valid_drivename($opt)) {
- # some changes can be done without hotplug
- my $drive = parse_drive($opt, $value);
- if (drive_is_cloudinit($drive)) {
- &$apply_pending_cloudinit($opt, $value);
- }
- vmconfig_update_disk($storecfg, $conf, $hotplug_features->{disk},
- $vmid, $opt, $value, 1, $arch, $machine_type);
- } elsif ($opt =~ m/^memory$/) { #dimms
- die "skip\n" if !$hotplug_features->{memory};
- $value = PVE::QemuServer::Memory::qemu_memory_hotplug($vmid, $conf, $defaults, $opt, $value);
- } elsif ($opt eq 'cpuunits') {
- cgroups_write("cpu", $vmid, "cpu.shares", $conf->{pending}->{$opt});
- } elsif ($opt eq 'cpulimit') {
- my $cpulimit = $conf->{pending}->{$opt} == 0 ? -1 : int($conf->{pending}->{$opt} * 100000);
- cgroups_write("cpu", $vmid, "cpu.cfs_quota_us", $cpulimit);
- } else {
- die "skip\n"; # skip non-hot-pluggable options
- }
- };
- if (my $err = $@) {
- &$add_error($opt, $err) if $err ne "skip\n";
- } else {
- # save new config if hotplug was successful
- $conf->{$opt} = $value;
- delete $conf->{pending}->{$opt};
- PVE::QemuConfig->write_config($vmid, $conf);
- $conf = PVE::QemuConfig->load_config($vmid); # update/reload
- }
- }
-}
-
sub try_deallocate_drive {
my ($storecfg, $vmid, $conf, $key, $drive, $rpcenv, $authuser, $force) = @_;
@@ -5077,50 +4825,6 @@ sub vmconfig_delete_or_detach_drive {
}
}
-sub vmconfig_apply_pending {
- my ($vmid, $conf, $storecfg) = @_;
-
- # cold plug
-
- my $pending_delete_hash = split_flagged_list($conf->{pending}->{delete});
- while (my ($opt, $force) = each %$pending_delete_hash) {
- die "internal error" if $opt =~ m/^unused/;
- $conf = PVE::QemuConfig->load_config($vmid); # update/reload
- if (!defined($conf->{$opt})) {
- vmconfig_undelete_pending_option($conf, $opt);
- PVE::QemuConfig->write_config($vmid, $conf);
- } elsif (is_valid_drivename($opt)) {
- vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
- vmconfig_undelete_pending_option($conf, $opt);
- delete $conf->{$opt};
- PVE::QemuConfig->write_config($vmid, $conf);
- } else {
- vmconfig_undelete_pending_option($conf, $opt);
- delete $conf->{$opt};
- PVE::QemuConfig->write_config($vmid, $conf);
- }
- }
-
- $conf = PVE::QemuConfig->load_config($vmid); # update/reload
-
- foreach my $opt (keys %{$conf->{pending}}) { # add/change
- $conf = PVE::QemuConfig->load_config($vmid); # update/reload
-
- if (defined($conf->{$opt}) && ($conf->{$opt} eq $conf->{pending}->{$opt})) {
- # skip if nothing changed
- } elsif (is_valid_drivename($opt)) {
- vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
- if defined($conf->{$opt});
- $conf->{$opt} = $conf->{pending}->{$opt};
- } else {
- $conf->{$opt} = $conf->{pending}->{$opt};
- }
-
- delete $conf->{pending}->{$opt};
- PVE::QemuConfig->write_config($vmid, $conf);
- }
-}
-
my $safe_num_ne = sub {
my ($a, $b) = @_;
@@ -5311,7 +5015,7 @@ sub vm_start {
die "VM $vmid already running\n" if check_running($vmid, undef, $migratedfrom);
if (!$statefile && scalar(keys %{$conf->{pending}})) {
- vmconfig_apply_pending($vmid, $conf, $storecfg);
+ PVE::QemuConfig->vmconfig_apply_pending($vmid, $conf, $storecfg);
$conf = PVE::QemuConfig->load_config($vmid); # update/reload
}
@@ -5729,7 +5433,7 @@ sub vm_stop_cleanup {
}
}
- vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes;
+ PVE::QemuConfig->vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes;
};
warn $@ if $@; # avoid errors - just warn
}
diff --git a/PVE/QemuServer/ImportDisk.pm b/PVE/QemuServer/ImportDisk.pm
index db7db63..dd0b16c 100755
--- a/PVE/QemuServer/ImportDisk.pm
+++ b/PVE/QemuServer/ImportDisk.pm
@@ -62,7 +62,7 @@ sub do_import {
my $running = PVE::QemuServer::check_running($vmid);
if ($running) {
my $errors = {};
- PVE::QemuServer::vmconfig_hotplug_pending($vmid, $vm_conf, $storecfg, $modified, $errors);
+ PVE::QemuConfig->vmconfig_hotplug_pending($vmid, $vm_conf, $storecfg, $modified, $errors);
if (scalar(keys %$errors)) {
foreach my $k (keys %$errors) {
warn "$k: $errors->{$k}\n" if $debug;
@@ -70,7 +70,7 @@ sub do_import {
}
}
} else {
- PVE::QemuServer::vmconfig_apply_pending($vmid, $vm_conf, $storecfg);
+ PVE::QemuConfig->vmconfig_apply_pending($vmid, $vm_conf, $storecfg);
}
} else {
--
2.20.1
More information about the pve-devel
mailing list