[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