we now inherit these from AbstractConfig, so they need to be defined/overwritten in QemuConfig instead of QemuServer.
Signed-off-by: Oguz Bektas <o.bek...@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@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@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 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com https://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel