Re: [pve-devel] [PATCH 2/2] now if the QMP command starts with guest-+ , it will bind dynamicly to the VMID.qga socket. To test the function vmtime is implemented which return the vm UNIX Time.
I thought about IP address and disk usage: guest-network-get-interfaces guest-get-fsinfo So maybe 1 call each minute is enough for this ? But seems guest-get-fsinfo does not return useful data :-/ Yes, I only display mountpoint. (could be useful for hot-unplug protection). Note that both network and fsinfo are not implemented under qga windows currently - Mail original - De: Dietmar Maurer diet...@proxmox.com À: Alexandre DERUMIER aderum...@odiso.com Cc: pve-devel@pve.proxmox.com, Wolfgang Link wolfg...@linksystems.org Envoyé: Mardi 25 Novembre 2014 08:09:39 Objet: RE: [PATCH 2/2] now if the QMP command starts with guest-+ , it will bind dynamicly to the VMID.qga socket. To test the function vmtime is implemented which return the vm UNIX Time. Maybe pvestatd can save if last qga command was successful (guest agent online) what happen here if the guest agent is down ? (for example, when the vm is going to shutdown, stop the guest agent service) We get a timeout, and we save that information so that other processes knows that guest agent is down. BTW, which qga command is used to get status ? I don't find anything related in http://git.qemu.org/?p=qemu.git;a=blob_plain;f=qga/qapi- schema.json;hb=HEAD I thought about IP address and disk usage: guest-network-get-interfaces guest-get-fsinfo But seems guest-get-fsinfo does not return useful data :-/ ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH 2/2] now if the QMP command starts with guest-+ , it will bind dynamicly to the VMID.qga socket. To test the function vmtime is implemented which return the vm UNIX Time.
I thought about IP address and disk usage: guest-network-get-interfaces guest-get-fsinfo So maybe 1 call each minute is enough for this ? yes But seems guest-get-fsinfo does not return useful data :-/ Yes, I only display mountpoint. (could be useful for hot-unplug protection). Note that both network and fsinfo are not implemented under qga windows currently OK, thanks for that info. ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] vm_deviceunplug question
This is because for virtio disk we remove the controller + drive, (1controller - 1 drive) and for scsi we only remove the drive (1controller - 256 drives) - Mail original - De: Dietmar Maurer diet...@proxmox.com À: Alexandre DERUMIER (aderum...@odiso.com) aderum...@odiso.com Cc: pve-devel@pve.proxmox.com Envoyé: Mardi 25 Novembre 2014 07:26:22 Objet: vm_deviceunplug question For example, for virtio we call qemu_devicedelverify(): if ($deviceid =~ m/^(virtio)(\d+)$/) { qemu_devicedel($vmid, $deviceid); return undef if !qemu_devicedelverify($vmid, $deviceid); return undef if !qemu_drivedel($vmid, $deviceid); But for lsi/scsi we simply do: if ($deviceid =~ m/^(scsi)(\d+)$/) { qemu_devicedel($vmid, $deviceid); return undef if !qemu_drivedel($vmid, $deviceid); Why don’t we call qemu_devicedelverify() here? ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] vm_deviceunplug question
This is because for virtio disk we remove the controller + drive, (1controller - 1 drive) and for scsi we only remove the drive (1controller - 256 drives) I cannot see that: 3001 if ($deviceid =~ m/^(scsi)(\d+)$/) { 3002 return undef if !qemu_devicedel($vmid, $deviceid); 3003 return undef if !qemu_drivedel($vmid, $deviceid); 3004 } so you also remove the device using qemu_devicedel()? What do I miss? ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 02/19] parse_vm_config: correctly handle $descr
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm |6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 96eb6ec..e020740 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1782,7 +1782,7 @@ sub parse_vm_config { my $res = { digest = Digest::SHA::sha1_hex($raw), snapshots = {}, - pending = {} + pending = {}, }; $filename =~ m|/qemu-server/(\d+)\.conf$| @@ -1798,10 +1798,12 @@ sub parse_vm_config { next if $line =~ m/^\s*$/; if ($line =~ m/^\[PENDING\]\s*$/i) { + $conf-{description} = $descr if $descr; + $descr = ''; $conf = $res-{pending} = {}; next; - }elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { + } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { my $snapname = $1; $conf-{description} = $descr if $descr; $descr = ''; -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 04/19] write_vm_config : write pending change
example: $conf-{pending}-{virtio1} $conf-{pending}-{delete} = net0,net1 [PENDING] virtio1: ... delete: net0,net1 Signed-off-by: Alexandre Derumier aderum...@odiso.com Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 32 +--- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 9b03e6a..f6b8ce2 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1826,10 +1826,12 @@ sub parse_vm_config { my $key = $1; my $value = $2; $conf-{$key} = $value; - } elsif (($section eq 'pending') ($line =~ m/^delete:\s*(.*\S)\s*$/)) { + } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) { my $value = $1; - foreach my $opt (split(/,/, $value)) { - $conf-{del}-{$opt} = 1; + if ($section eq 'pending') { + $conf-{delete} = $value; # we parse this later + } else { + warn vm $vmid - propertry 'delete' is only allowed in [PENDING]\n; } } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S+)\s*$/) { my $key = $1; @@ -1893,12 +1895,18 @@ sub write_vm_config { my $used_volids = {}; my $cleanup_config = sub { - my ($cref, $snapname) = @_; + my ($cref, $pending, $snapname) = @_; foreach my $key (keys %$cref) { next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' || - $key eq 'snapstate'; + $key eq 'snapstate' || $key eq 'pending'; my $value = $cref-{$key}; + if ($key eq 'delete') { + die propertry 'delete' is only allowed in [PENDING]\n + if !$pending; + # fixme: check syntax? + next; + } eval { $value = check_type($key, $value); }; die unable to parse value of '$key' - $@ if $@; @@ -1912,8 +1920,12 @@ sub write_vm_config { }; $cleanup_config($conf); + +$cleanup_config($conf-{pending}, 1); + foreach my $snapname (keys %{$conf-{snapshots}}) { - $cleanup_config($conf-{snapshots}-{$snapname}, $snapname); + die internal error if $snapname eq 'pending'; + $cleanup_config($conf-{snapshots}-{$snapname}, undef, $snapname); } # remove 'unusedX' settings if we re-add a volume @@ -1936,13 +1948,19 @@ sub write_vm_config { } foreach my $key (sort keys %$conf) { - next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots'; + next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' || $key eq 'snapshots'; $raw .= $key: $conf-{$key}\n; } return $raw; }; my $raw = $generate_raw_config($conf); + +if (scalar(keys %{$conf-{pending}})){ + $raw .= \n[PENDING]\n; + $raw .= $generate_raw_config($conf-{pending}); +} + foreach my $snapname (sort keys %{$conf-{snapshots}}) { $raw .= \n[$snapname]\n; $raw .= $generate_raw_config($conf-{snapshots}-{$snapname}); -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 03/19] parse_vm_config: only allow 'delete' inside [PENDING]
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 13 +++-- 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index e020740..9b03e6a 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1792,22 +1792,24 @@ sub parse_vm_config { my $conf = $res; my $descr = ''; +my $section = ''; my @lines = split(/\n/, $raw); foreach my $line (@lines) { next if $line =~ m/^\s*$/; if ($line =~ m/^\[PENDING\]\s*$/i) { + $section = 'pending'; $conf-{description} = $descr if $descr; $descr = ''; - $conf = $res-{pending} = {}; + $conf = $res-{$section} = {}; next; } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { - my $snapname = $1; + $section = $1; $conf-{description} = $descr if $descr; $descr = ''; - $conf = $res-{snapshots}-{$snapname} = {}; + $conf = $res-{snapshots}-{$section} = {}; next; } @@ -1824,9 +1826,8 @@ sub parse_vm_config { my $key = $1; my $value = $2; $conf-{$key} = $value; - } elsif ($line =~ m/^(delete):\s*(.*\S)\s*$/) { - my $key = $1; - my $value = $2; + } elsif (($section eq 'pending') ($line =~ m/^delete:\s*(.*\S)\s*$/)) { + my $value = $1; foreach my $opt (split(/,/, $value)) { $conf-{del}-{$opt} = 1; } -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 00/19] qemu-server: implement pending changes
Based on Alexandres patches (sent to pve-devel list): qemu-server : implement pending changes v2 pending changes hotplug v2 Changes in v5: - implement network hotplug - cleanup error handling in vm_deviceunplug and vm_deviceplug - bug fixes Changes in v4: - implement API/CLI to get pending changes - improve hotplug error handling (see cpu hotplug as example) - new method to cleanup setting in [PENDING] - code cleanups Changes in v3: - I tried to simplify things by always writing changes into pending section first. - do not parse 'delete' option parse_vm_config Todo: implement hotplug disk/net Alexandre Derumier (1): parse_vm_config : parse pending changes Dietmar Maurer (18): parse_vm_config: correctly handle $descr parse_vm_config: only allow 'delete' inside [PENDING] write_vm_config : write pending change update_vm_api: always write into pending section implement vmconfig_apply_pending for stopped VM vm_start: apply pending changes fix balloon consistency check (consider pending changes) implement trivial hotplug vmconfig_hotplug_pending: implement tablet hotplug vmconfig_cleanup_pending: new method to clenup setting in [PENDING] code cleanup, delete trailing white space vmconfig_hotplug_pending: improve hotplug error handling implement API/CLI to get pending changes rename qemu_bridgeadd to qemu_add_pci_bridge vmconfig_hotplug_pending: correctly skip values vmconfig_hotplug_pending : add update_net vm_deviceunplug: raise expection if something fail vm_deviceplug: always raise exception on error PVE/API2/Qemu.pm | 234 +++--- PVE/QemuServer.pm | 576 ++--- pve-bridge|8 +- qm| 27 +++ 4 files changed, 661 insertions(+), 184 deletions(-) -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 09/19] implement trivial hotplug
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 47 ++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 8524180..2a702d2 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3438,11 +3438,56 @@ sub set_migration_caps { vm_mon_cmd_nocheck($vmid, migrate-set-capabilities, capabilities = $cap_ref); } +sub vmconfig_hotplug_pending { +my ($vmid, $conf, $storecfg) = @_; + +my $defaults = PVE::QemuServer::load_defaults(); + +# commit values which do not have any impact on running VM first + +my $changes = 0; +foreach my $opt (keys %{$conf-{pending}}) { # add/change + if ($opt eq 'name' || $opt eq 'hotplug' || $opt eq 'onboot' || $opt eq 'shares') { + $conf-{$opt} = $conf-{pending}-{$opt}; + delete $conf-{pending}-{$opt}; + $changes = 1; + } +} + +if ($changes) { + update_config_nolock($vmid, $conf, 1); + $conf = load_config($vmid); # update/reload +} + +$changes = 0; + +# allow manual ballooning if shares is set to zero + +if (defined($conf-{pending}-{balloon}) defined($conf-{shares}) ($conf-{shares} == 0)) { + my $balloon = $conf-{pending}-{balloon} || $conf-{memory} || $defaults-{memory}; + vm_mon_cmd($vmid, balloon, value = $balloon*1024*1024); + $conf-{balloon} = $conf-{pending}-{balloon}; + delete $conf-{pending}-{balloon}; + $changes = 1; +} + +if ($changes) { + update_config_nolock($vmid, $conf, 1); + $conf = load_config($vmid); # update/reload +} + +return if !$conf-{hotplug}; + +# fixme: implement disk/network hotplug here + +} sub vmconfig_apply_pending { my ($vmid, $conf, $storecfg, $running) = @_; -die implement me - vm is running if $running; # fixme: if $conf-{hotplug}; +return vmconfig_hotplug_pending($vmid, $conf, $storecfg) if $running; + +# cold plug my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); foreach my $opt (@delete) { # delete -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 01/19] parse_vm_config : parse pending changes
From: Alexandre Derumier aderum...@odiso.com example: [PENDING] virtio1:... delete:net0,net1 $conf-{pending}-{virtio1} $conf-{pending}-{del}-{net0} $conf-{pending}-{del}-{net1} Signed-off-by: Alexandre Derumier aderum...@odiso.com Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 13 - 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index b5aafc3..96eb6ec 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1782,6 +1782,7 @@ sub parse_vm_config { my $res = { digest = Digest::SHA::sha1_hex($raw), snapshots = {}, + pending = {} }; $filename =~ m|/qemu-server/(\d+)\.conf$| @@ -1796,7 +1797,11 @@ sub parse_vm_config { foreach my $line (@lines) { next if $line =~ m/^\s*$/; - if ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { + if ($line =~ m/^\[PENDING\]\s*$/i) { + $conf = $res-{pending} = {}; + next; + + }elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { my $snapname = $1; $conf-{description} = $descr if $descr; $descr = ''; @@ -1817,6 +1822,12 @@ sub parse_vm_config { my $key = $1; my $value = $2; $conf-{$key} = $value; + } elsif ($line =~ m/^(delete):\s*(.*\S)\s*$/) { + my $key = $1; + my $value = $2; + foreach my $opt (split(/,/, $value)) { + $conf-{del}-{$opt} = 1; + } } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S+)\s*$/) { my $key = $1; my $value = $2; -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 08/19] fix balloon consistency check (consider pending changes)
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm |5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index d6a70c0..a8841c2 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -945,10 +945,9 @@ my $update_vm_api = sub { PVE::QemuServer::check_lock($conf) if !$skiplock; - # fixme: wrong place? howto handle pending changes? @delete ? if ($param-{memory} || defined($param-{balloon})) { - my $maxmem = $param-{memory} || $conf-{memory} || $defaults-{memory}; - my $balloon = defined($param-{balloon}) ? $param-{balloon} : $conf-{balloon}; + my $maxmem = $param-{memory} || $conf-{pending}-{memory} || $conf-{memory} || $defaults-{memory}; + my $balloon = defined($param-{balloon}) ? $param-{balloon} : $conf-{pending}-{balloon} || $conf-{balloon}; die balloon value too large (must be smaller than assigned memory)\n if $balloon $balloon $maxmem; -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 05/19] update_vm_api: always write into pending section
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 99 -- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 0787074..222c44f 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -951,6 +951,44 @@ my $update_vm_api = sub { $check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param); +my $pending_delete_option = sub { + my ($conf, $key) = @_; + + delete $conf-{pending}-{$key}; + my $pending_delete_hash = { $key = 1 }; + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; + } + $conf-{pending}-{delete} = join(',', keys %$pending_delete_hash); +}; + +my $pending_undelete_option = sub { + my ($conf, $key) = @_; + + my $pending_delete_hash = {}; + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; + } + delete $pending_delete_hash-{$key}; + + my @keylist = keys %$pending_delete_hash; + if (scalar(@keylist)) { + $conf-{pending}-{delete} = join(',', @keylist); + } else { + delete $conf-{pending}-{delete}; + } +}; + +my $register_unused_drive = sub { + my ($conf, $drive) = @_; + if (!PVE::QemuServer::drive_is_cdrom($drive)) { + my $volid = $drive-{file}; + if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); + } + } +}; + my $updatefn = sub { my $conf = PVE::QemuServer::load_config($vmid); @@ -960,6 +998,7 @@ my $update_vm_api = sub { PVE::QemuServer::check_lock($conf) if !$skiplock; + # fixme: wrong place? howto handle pending changes? @delete ? if ($param-{memory} || defined($param-{balloon})) { my $maxmem = $param-{memory} || $conf-{memory} || $defaults-{memory}; my $balloon = defined($param-{balloon}) ? $param-{balloon} : $conf-{balloon}; @@ -974,11 +1013,67 @@ my $update_vm_api = sub { print update VM $vmid: . join (' ', @paramarr) . \n; - foreach my $opt (@delete) { # delete + # write updates to pending section + + foreach my $opt (@delete) { $conf = PVE::QemuServer::load_config($vmid); # update/reload - $vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force); + if ($opt =~ m/^unused/) { + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); + my $drive = PVE::QemuServer::parse_drive($opt, $conf-{$opt}); + if (my $sid = $test_deallocate_drive($storecfg, $vmid, $opt, $drive, $force)) { + $rpcenv-check($authuser, /storage/$sid, ['Datastore.AllocateSpace']); + $delete_drive($conf, $storecfg, $vmid, $opt, $drive); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } + } elsif (PVE::QemuServer::valid_drivename($opt)) { + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); + $register_unused_drive($conf, PVE::QemuServer::parse_drive($opt, $conf-{pending}-{$opt})) + if defined($conf-{pending}-{$opt}); + $pending_delete_option($conf, $opt); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } else { + $pending_delete_option($conf, $opt); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } } + foreach my $opt (keys %$param) { # add/change + $conf = PVE::QemuServer::load_config($vmid); # update/reload + next if defined($conf-{pending}-{$opt}) ($param-{$opt} eq $conf-{pending}-{$opt}); # skip if nothing changed + + if (PVE::QemuServer::valid_drivename($opt)) { + my $drive = PVE::QemuServer::parse_drive($opt, $param-{$opt}); + if (PVE::QemuServer::drive_is_cdrom($drive)) { # CDROM + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.CDROM']); + } else { + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); + } + $register_unused_drive($conf, PVE::QemuServer::parse_drive($opt, $conf-{pending}-{$opt})) + if defined($conf-{pending}-{$opt}); + + $create_disks($rpcenv, $authuser, $conf-{pending}, $storecfg, $vmid, undef, {$opt = $param-{$opt}}); + } else { + $conf-{pending}-{$opt} = $param-{$opt}; + } +
[pve-devel] [PATCH v5 10/19] vmconfig_hotplug_pending: implement tablet hotplug
Remove special case: now, we only hotplug if 'hotplug' is enabled. Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 71 - 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 2a702d2..7219353 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -2974,7 +2974,6 @@ sub vm_devices_list { my ($vmid) = @_; my $res = vm_mon_cmd($vmid, 'query-pci'); - my $devices = {}; foreach my $pcibus (@$res) { foreach my $device (@{$pcibus-{devices}}) { @@ -2990,6 +2989,14 @@ sub vm_devices_list { } } +my $resmice = vm_mon_cmd($vmid, 'query-mice'); +foreach my $mice (@$resmice) { + if ($mice-{name} eq 'QEMU HID Tablet') { + $devices-{tablet} = 1; + last; + } +} + return $devices; } @@ -3000,16 +3007,16 @@ sub vm_deviceplug { my $q35 = machine_type_is_q35($conf); -if ($deviceid eq 'tablet') { - qemu_deviceadd($vmid, print_tabletdevice_full($conf)); - return 1; -} - return 1 if !$conf-{hotplug}; my $devices_list = vm_devices_list($vmid); return 1 if defined($devices_list-{$deviceid}); +if ($deviceid eq 'tablet') { + qemu_deviceadd($vmid, print_tabletdevice_full($conf)); + return 1; +} + qemu_bridgeadd($storecfg, $conf, $vmid, $deviceid); #add bridge if we need it for the device if ($deviceid =~ m/^(virtio)(\d+)$/) { @@ -3067,16 +3074,16 @@ sub vm_deviceunplug { return 1 if !check_running ($vmid); -if ($deviceid eq 'tablet') { - qemu_devicedel($vmid, $deviceid); - return 1; -} - return 1 if !$conf-{hotplug}; my $devices_list = vm_devices_list($vmid); return 1 if !defined($devices_list-{$deviceid}); +if ($deviceid eq 'tablet') { + qemu_devicedel($vmid, $deviceid); + return 1; +} + die can't unplug bootdisk if $conf-{bootdisk} $conf-{bootdisk} eq $deviceid; if ($deviceid =~ m/^(virtio)(\d+)$/) { @@ -3478,8 +3485,48 @@ sub vmconfig_hotplug_pending { return if !$conf-{hotplug}; -# fixme: implement disk/network hotplug here +my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); +foreach my $opt (@delete) { + if ($opt eq 'tablet') { + if ($defaults-{tablet}) { + PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt); + } else { + PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); + } + } else { + # skip non-hot-pluggable options + next; + } + + # save new config if hotplug was successful + delete $conf-{$opt}; + vmconfig_undelete_pending_option($conf, $opt); + update_config_nolock($vmid, $conf, 1); + + $conf = load_config($vmid); # update/reload +} +foreach my $opt (keys %{$conf-{pending}}) { + my $value = $conf-{pending}-{$opt}; + + if ($opt eq 'tablet') { + if ($value == 1) { + PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt); + } elsif ($value == 0) { + PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); + } + } else { + # skip non-hot-pluggable options + next; + } + + # save new config if hotplug was successful + $conf-{$opt} = $value; + delete $conf-{pending}-{$opt}; + update_config_nolock($vmid, $conf, 1); + + $conf = load_config($vmid); # update/reload +} } sub vmconfig_apply_pending { -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 13/19] vmconfig_hotplug_pending: improve hotplug error handling
Simplify code, and allow to partially apply pending changes using a new $selection parameter. Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 12 - PVE/QemuServer.pm | 149 ++--- 2 files changed, 95 insertions(+), 66 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 8c7c076..b116c24 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -961,7 +961,10 @@ my $update_vm_api = sub { # write updates to pending section + my $modified = {}; # record what $option we modify + foreach my $opt (@delete) { + $modified-{$opt} = 1; $conf = PVE::QemuServer::load_config($vmid); # update/reload if ($opt =~ m/^unused/) { $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); @@ -984,6 +987,7 @@ my $update_vm_api = sub { } foreach my $opt (keys %$param) { # add/change + $modified-{$opt} = 1; $conf = PVE::QemuServer::load_config($vmid); # update/reload next if defined($conf-{pending}-{$opt}) ($param-{$opt} eq $conf-{pending}-{$opt}); # skip if nothing changed @@ -1017,8 +1021,14 @@ my $update_vm_api = sub { # apply pending changes $conf = PVE::QemuServer::load_config($vmid); # update/reload - PVE::QemuServer::vmconfig_apply_pending($vmid, $conf, $storecfg, $running); + if ($running) { + my $errors = {}; + PVE::QemuServer::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); + } return; # TODO: remove old code below foreach my $opt (keys %$param) { # add/change diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index dd06f2f..391376e 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3266,22 +3266,25 @@ sub qemu_netdevdel { sub qemu_cpu_hotplug { my ($vmid, $conf, $cores) = @_; -die new cores config is not defined if !$cores; -die you can't add more cores than maxcpus - if $conf-{maxcpus} ($cores $conf-{maxcpus}); -return if !check_running($vmid); +my $sockets = $conf-{sockets} || 1; +die cpu hotplug only works with one socket\n + if $sockets 1; -my $currentcores = $conf-{cores} if $conf-{cores}; -die current cores is not defined if !$currentcores; -die maxcpus is not defined if !$conf-{maxcpus}; -raise_param_exc({ 'cores' = online cpu unplug is not yet possible }) - if($cores $currentcores); +die maxcpus is not defined\n + if !$conf-{maxcpus}; + +die you can't add more cores than maxcpus\n + if $cores $conf-{maxcpus}; + +my $currentcores = $conf-{cores} || 1; +die online cpu unplug is not yet possible\n + if $cores $currentcores; my $currentrunningcores = vm_mon_cmd($vmid, query-cpus); -raise_param_exc({ 'cores' = cores number if running vm is different than configuration }) - if scalar (@{$currentrunningcores}) != $currentcores; +die cores number if running vm is different than configuration\n + if scalar(@{$currentrunningcores}) != $currentcores; -for(my $i = $currentcores; $i $cores; $i++) { +for (my $i = $currentcores; $i $cores; $i++) { vm_mon_cmd($vmid, cpu-add, id = int($i)); } } @@ -3477,12 +3480,23 @@ sub set_migration_caps { vm_mon_cmd_nocheck($vmid, migrate-set-capabilities, capabilities = $cap_ref); } +# 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) = @_; +my ($vmid, $conf, $storecfg, $selection, $errors) = @_; my $defaults = load_defaults(); # 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 @@ -3498,73 +3512,78 @@ sub vmconfig_hotplug_pending { $conf = load_config($vmid); # update/reload } -$changes = 0; - -# allow manual ballooning if shares is set to zero - -if (defined($conf-{pending}-{balloon}) defined($conf-{shares}) ($conf-{shares} == 0)) { - my $balloon = $conf-{pending}-{balloon} || $conf-{memory} || $defaults-{memory}; - vm_mon_cmd($vmid, balloon, value = $balloon*1024*1024); - $conf-{balloon} = $conf-{pending}-{balloon}; -
[pve-devel] [PATCH v5 17/19] vmconfig_hotplug_pending : add update_net
Signed-off-by: Alexandre Derumier aderum...@odiso.com Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 44 ++- PVE/QemuServer.pm | 67 - pve-bridge|8 +-- 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 98a42fe..ea045c9 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -897,46 +897,6 @@ my $vmconfig_update_disk = sub { } }; -my $vmconfig_update_net = sub { -my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $value) = @_; - -if ($conf-{$opt} PVE::QemuServer::check_running($vmid)) { - my $oldnet = PVE::QemuServer::parse_net($conf-{$opt}); - my $newnet = PVE::QemuServer::parse_net($value); - - if($oldnet-{model} ne $newnet-{model}){ - #if model change, we try to hot-unplug -die error hot-unplug $opt for update if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); - }else{ - - if($newnet-{bridge} $oldnet-{bridge}){ - my $iface = tap.$vmid.i.$1 if $opt =~ m/net(\d+)/; - - if($newnet-{rate} ne $oldnet-{rate}){ - PVE::Network::tap_rate_limit($iface, $newnet-{rate}); - } - - if(($newnet-{bridge} ne $oldnet-{bridge}) || ($newnet-{tag} ne $oldnet-{tag}) || ($newnet-{firewall} ne $oldnet-{firewall})){ - PVE::Network::tap_unplug($iface); - PVE::Network::tap_plug($iface, $newnet-{bridge}, $newnet-{tag}, $newnet-{firewall}); - } - - }else{ - #if bridge/nat mode change, we try to hot-unplug - die error hot-unplug $opt for update if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); - } - } - -} -$conf-{$opt} = $value; -PVE::QemuServer::update_config_nolock($vmid, $conf, 1); -$conf = PVE::QemuServer::load_config($vmid); # update/reload - -my $net = PVE::QemuServer::parse_net($conf-{$opt}); - -die error hotplug $opt if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $net); -}; - # POST/PUT {vmid}/config implementation # # The original API used PUT (idempotent) an we assumed that all operations @@ -1128,8 +1088,8 @@ my $update_vm_api = sub { } elsif ($opt =~ m/^net(\d+)$/) { #nics - $vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid, - $opt, $param-{$opt}); + # $vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid, + # $opt, $param-{$opt}); } else { diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 6642928..c568dfd 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3032,6 +3032,7 @@ sub vm_devices_list { return $devices; } +# fixme: this should raise exceptions on error! sub vm_deviceplug { my ($storecfg, $conf, $vmid, $deviceid, $device) = @_; @@ -3073,7 +3074,7 @@ sub vm_deviceplug { return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device); return undef if !qemu_driveadd($storecfg, $vmid, $device); my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); -if(!qemu_deviceadd($vmid, $devicefull)) { +if(!qemu_deviceadd($vmid, $devicefull)) { # fixme: use qemu_deviceaddverify? qemu_drivedel($vmid, $deviceid); return undef; } @@ -3101,6 +3102,7 @@ sub vm_deviceplug { return 1; } +# fixme: this should raise exceptions on error! sub vm_deviceunplug { my ($vmid, $conf, $deviceid) = @_; @@ -3532,6 +3534,9 @@ sub vmconfig_hotplug_pending { } elsif ($opt eq 'cores') { die skip\n if !$hotplug; qemu_cpu_hotplug($vmid, $conf, 1); + } elsif ($opt =~ m/^net(\d+)$/) { + die skip\n if !$hotplug; + vm_deviceunplug($vmid, $conf, $opt); } else { die skip\n; } @@ -3566,6 +3571,9 @@ sub vmconfig_hotplug_pending { # allow manual ballooning if shares is set to zero 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, $vmid, $opt, $value); } else { die skip\n; # skip non-hot-pluggable options } @@ -3626,6 +3634,63 @@ sub vmconfig_apply_pending { } } +my $safe_num_ne = sub { +my ($a, $b) = @_; + +return 0 if !defined($a) !defined($b); +return 1 if !defined($a); +return 1 if !defined($b); + +return $a != $b; +}; + +my $safe_string_ne = sub
[pve-devel] [PATCH v5 11/19] vmconfig_cleanup_pending: new method to clenup setting in [PENDING]
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm |8 +--- PVE/QemuServer.pm | 32 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index a8841c2..8c7c076 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -1006,14 +1006,8 @@ my $update_vm_api = sub { } # remove pending changes when nothing changed - my $changes; $conf = PVE::QemuServer::load_config($vmid); # update/reload - foreach my $opt (keys %{$conf-{pending}}) { # add/change - if (defined($conf-{$opt}) ($conf-{pending}-{$opt} eq $conf-{$opt})) { - $changes = 1; - delete $conf-{pending}-{$opt}; - } - } + my $changes = PVE::QemuServer::vmconfig_cleanup_pending($conf); PVE::QemuServer::update_config_nolock($vmid, $conf, 1) if $changes; return if !scalar(keys %{$conf-{pending}}); diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 7219353..2644117 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1442,6 +1442,38 @@ 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}; + } +} + +# remove delete if option is not set +my $pending_delete_hash = {}; +foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + if (defined($conf-{$opt})) { + $pending_delete_hash-{$opt} = 1; + } else { + $changes = 1; + } +} + +my @keylist = keys %$pending_delete_hash; +if (scalar(@keylist)) { + $conf-{pending}-{delete} = join(',', @keylist); +} else { + delete $conf-{pending}-{delete}; +} + +return $changes; +} + my $valid_smbios1_options = { manufacturer = '\S+', product = '\S+', -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 06/19] implement vmconfig_apply_pending for stopped VM
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 191 ++ 1 file changed, 119 insertions(+), 72 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 222c44f..41d75d0 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -37,6 +37,73 @@ my $resolve_cdrom_alias = sub { } }; +my $vm_is_volid_owner = sub { +my ($storecfg, $vmid, $volid) =@_; + +if ($volid !~ m|^/|) { + my ($path, $owner); + eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); }; + if ($owner ($owner == $vmid)) { + return 1; + } +} + +return undef; +}; + +my $test_deallocate_drive = sub { +my ($storecfg, $vmid, $key, $drive, $force) = @_; + +if (!PVE::QemuServer::drive_is_cdrom($drive)) { + my $volid = $drive-{file}; + if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + if ($force || $key =~ m/^unused/) { + my $sid = PVE::Storage::parse_volume_id($volid); + return $sid; + } + } +} + +return undef; +}; + +my $pending_delete_option = sub { +my ($conf, $key) = @_; + +delete $conf-{pending}-{$key}; +my $pending_delete_hash = { $key = 1 }; +foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; +} +$conf-{pending}-{delete} = join(',', keys %$pending_delete_hash); +}; + +my $pending_undelete_option = sub { +my ($conf, $key) = @_; + +my $pending_delete_hash = {}; +foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; +} +delete $pending_delete_hash-{$key}; + +my @keylist = keys %$pending_delete_hash; +if (scalar(@keylist)) { + $conf-{pending}-{delete} = join(',', @keylist); +} else { + delete $conf-{pending}-{delete}; +} +}; + +my $register_unused_drive = sub { +my ($storecfg, $vmid, $conf, $drive) = @_; +if (!PVE::QemuServer::drive_is_cdrom($drive)) { + my $volid = $drive-{file}; + if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); + } +} +}; my $check_storage_access = sub { my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_; @@ -639,36 +706,6 @@ __PACKAGE__-register_method({ return $conf; }}); -my $vm_is_volid_owner = sub { -my ($storecfg, $vmid, $volid) =@_; - -if ($volid !~ m|^/|) { - my ($path, $owner); - eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); }; - if ($owner ($owner == $vmid)) { - return 1; - } -} - -return undef; -}; - -my $test_deallocate_drive = sub { -my ($storecfg, $vmid, $key, $drive, $force) = @_; - -if (!PVE::QemuServer::drive_is_cdrom($drive)) { - my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { - if ($force || $key =~ m/^unused/) { - my $sid = PVE::Storage::parse_volume_id($volid); - return $sid; - } - } -} - -return undef; -}; - my $delete_drive = sub { my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_; @@ -868,6 +905,48 @@ my $vmconfig_update_net = sub { die error hotplug $opt if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $net); }; +my $vmconfig_apply_pending = sub { +my ($vmid, $conf, $storecfg, $running) = @_; + +my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); +foreach my $opt (@delete) { # delete + die internal error if $opt =~ m/^unused/; + $conf = PVE::QemuServer::load_config($vmid); # update/reload + if (!defined($conf-{$opt})) { + $pending_undelete_option($conf, $opt); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } elsif (PVE::QemuServer::valid_drivename($opt)) { + $register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf-{$opt})); + $pending_undelete_option($conf, $opt); + delete $conf-{$opt}; + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } else { + $pending_undelete_option($conf, $opt); + delete $conf-{$opt}; + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } +} + +$conf = PVE::QemuServer::load_config($vmid); # update/reload + +foreach my $opt (keys %{$conf-{pending}}) { # add/change + $conf = PVE::QemuServer::load_config($vmid); # update/reload + + if (defined($conf-{$opt}) ($conf-{$opt} eq $conf-{pending}-{$opt})) { + # skip if nothing changed + } elsif (PVE::QemuServer::valid_drivename($opt)) { + $register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf-{$opt})) + if defined($conf-{$opt}); +
[pve-devel] [PATCH v5 14/19] implement API/CLI to get pending changes
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 86 +- qm | 27 + 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index b116c24..98a42fe 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -624,7 +624,7 @@ __PACKAGE__-register_method({ path = '{vmid}/config', method = 'GET', proxyto = 'node', -description = Get virtual machine configuration., +description = Get current virtual machine configuration. This does not include pending configuration changes (see 'pending' API)., permissions = { check = ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], }, @@ -650,10 +650,94 @@ __PACKAGE__-register_method({ my $conf = PVE::QemuServer::load_config($param-{vmid}); delete $conf-{snapshots}; + delete $conf-{pending}; return $conf; }}); +__PACKAGE__-register_method({ +name = 'vm_pending', +path = '{vmid}/pending', +method = 'GET', +proxyto = 'node', +description = Get virtual machine configuration, including pending changes., +permissions = { + check = ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], +}, +parameters = { + additionalProperties = 0, + properties = { + node = get_standard_option('pve-node'), + vmid = get_standard_option('pve-vmid'), + }, +}, +returns = { + type = array, + items = { + type = object, + properties = { + key = { + description = Configuration option name., + type = 'string', + }, + value = { + description = Current value., + type = 'string', + optional = 1, + }, + pending = { + description = Pending value., + type = 'string', + optional = 1, + }, + delete = { + description = Indicated a pending delete request., + type = 'boolean', + optional = 1, + }, + }, + }, +}, +code = sub { + my ($param) = @_; + + my $conf = PVE::QemuServer::load_config($param-{vmid}); + + my $pending_delete_hash = {}; + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; + } + + my $res = []; + + foreach my $opt (keys $conf) { + next if ref($conf-{$opt}); + my $item = { key = $opt }; + $item-{value} = $conf-{$opt} if defined($conf-{$opt}); + $item-{pending} = $conf-{pending}-{$opt} if defined($conf-{pending}-{$opt}); + $item-{delete} = 1 if $pending_delete_hash-{$opt}; + push @$res, $item; + } + + foreach my $opt (keys $conf-{pending}) { + next if $opt eq 'delete'; + next if ref($conf-{pending}-{$opt}); # just to be sure + next if $conf-{$opt}; + my $item = { key = $opt }; + $item-{pending} = $conf-{pending}-{$opt}; + push @$res, $item; + } + + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + next if $conf-{pending}-{$opt}; # just to be sure + next if $conf-{$opt}; + my $item = { key = $opt, delete = 1}; + push @$res, $item; + } + + return $res; +}}); + my $delete_drive = sub { my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_; diff --git a/qm b/qm index cea223e..249117c 100755 --- a/qm +++ b/qm @@ -437,6 +437,33 @@ my $cmddef = { } }], +pending = [ PVE::API2::Qemu, 'vm_pending', ['vmid'], + { node = $nodename }, sub { + my $data = shift; + foreach my $item (sort { $a-{key} cmp $b-{key}} @$data) { + my $k = $item-{key}; + next if $k eq 'digest'; + my $v = $item-{value}; + my $p = $item-{pending}; + if ($k eq 'description') { + $v = PVE::Tools::encode_text($v) if defined($v); + $p = PVE::Tools::encode_text($p) if defined($p); + } + if (defined($v)) { + if ($item-{delete}) { + print del $k: $v\n; + } elsif (defined($p)) { + print cur $k: $v\n; + print new $k: $p\n; + } else { + print cur $k: $v\n; + } + } elsif (defined($p)) { + print new $k: $p\n; +
[pve-devel] [PATCH v5 07/19] vm_start: apply pending changes
I move related helper methods into PVE::QemuServer. Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 111 - PVE/QemuServer.pm | 103 + 2 files changed, 111 insertions(+), 103 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 41d75d0..d6a70c0 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -37,26 +37,12 @@ my $resolve_cdrom_alias = sub { } }; -my $vm_is_volid_owner = sub { -my ($storecfg, $vmid, $volid) =@_; - -if ($volid !~ m|^/|) { - my ($path, $owner); - eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); }; - if ($owner ($owner == $vmid)) { - return 1; - } -} - -return undef; -}; - my $test_deallocate_drive = sub { my ($storecfg, $vmid, $key, $drive, $force) = @_; if (!PVE::QemuServer::drive_is_cdrom($drive)) { my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + if ( PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) { if ($force || $key =~ m/^unused/) { my $sid = PVE::Storage::parse_volume_id($volid); return $sid; @@ -67,44 +53,6 @@ my $test_deallocate_drive = sub { return undef; }; -my $pending_delete_option = sub { -my ($conf, $key) = @_; - -delete $conf-{pending}-{$key}; -my $pending_delete_hash = { $key = 1 }; -foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { - $pending_delete_hash-{$opt} = 1; -} -$conf-{pending}-{delete} = join(',', keys %$pending_delete_hash); -}; - -my $pending_undelete_option = sub { -my ($conf, $key) = @_; - -my $pending_delete_hash = {}; -foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { - $pending_delete_hash-{$opt} = 1; -} -delete $pending_delete_hash-{$key}; - -my @keylist = keys %$pending_delete_hash; -if (scalar(@keylist)) { - $conf-{pending}-{delete} = join(',', @keylist); -} else { - delete $conf-{pending}-{delete}; -} -}; - -my $register_unused_drive = sub { -my ($storecfg, $vmid, $conf, $drive) = @_; -if (!PVE::QemuServer::drive_is_cdrom($drive)) { - my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { - PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); - } -} -}; - my $check_storage_access = sub { my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_; @@ -712,7 +660,7 @@ my $delete_drive = sub { if (!PVE::QemuServer::drive_is_cdrom($drive)) { my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + if (PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) { if ($force || $key =~ m/^unused/) { eval { # check if the disk is really unused @@ -905,48 +853,6 @@ my $vmconfig_update_net = sub { die error hotplug $opt if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $net); }; -my $vmconfig_apply_pending = sub { -my ($vmid, $conf, $storecfg, $running) = @_; - -my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); -foreach my $opt (@delete) { # delete - die internal error if $opt =~ m/^unused/; - $conf = PVE::QemuServer::load_config($vmid); # update/reload - if (!defined($conf-{$opt})) { - $pending_undelete_option($conf, $opt); - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); - } elsif (PVE::QemuServer::valid_drivename($opt)) { - $register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf-{$opt})); - $pending_undelete_option($conf, $opt); - delete $conf-{$opt}; - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); - } else { - $pending_undelete_option($conf, $opt); - delete $conf-{$opt}; - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); - } -} - -$conf = PVE::QemuServer::load_config($vmid); # update/reload - -foreach my $opt (keys %{$conf-{pending}}) { # add/change - $conf = PVE::QemuServer::load_config($vmid); # update/reload - - if (defined($conf-{$opt}) ($conf-{$opt} eq $conf-{pending}-{$opt})) { - # skip if nothing changed - } elsif (PVE::QemuServer::valid_drivename($opt)) { - $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}; - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); -} -}; - # POST/PUT {vmid}/config implementation # # The original API
[pve-devel] [PATCH v5 19/19] vm_deviceplug: always raise exception on error
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 92 - 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 14989f8..243d069 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3032,73 +3032,80 @@ sub vm_devices_list { return $devices; } -# fixme: this should raise exceptions on error! sub vm_deviceplug { my ($storecfg, $conf, $vmid, $deviceid, $device) = @_; -return 1 if !check_running($vmid); +die internal error if !$conf-{hotplug}; my $q35 = machine_type_is_q35($conf); -return 1 if !$conf-{hotplug}; - my $devices_list = vm_devices_list($vmid); return 1 if defined($devices_list-{$deviceid}); +qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device + if ($deviceid eq 'tablet') { + qemu_deviceadd($vmid, print_tabletdevice_full($conf)); - return 1; -} -qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device +} elsif ($deviceid =~ m/^(virtio)(\d+)$/) { -if ($deviceid =~ m/^(virtio)(\d+)$/) { -return undef if !qemu_driveadd($storecfg, $vmid, $device); +qemu_driveadd($storecfg, $vmid, $device); my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); + qemu_deviceadd($vmid, $devicefull); -if(!qemu_deviceaddverify($vmid, $deviceid)) { + eval { qemu_deviceaddverify($vmid, $deviceid); }; + if (my $err = $@) { eval { qemu_drivedel($vmid, $deviceid); }; warn $@ if $@; - return undef; + die $err; } -} -if ($deviceid =~ m/^(scsihw)(\d+)$/) { +} elsif ($deviceid =~ m/^(scsihw)(\d+)$/) { + my $scsihw = defined($conf-{scsihw}) ? $conf-{scsihw} : lsi; my $pciaddr = print_pci_addr($deviceid); my $devicefull = $scsihw,id=$deviceid$pciaddr; + qemu_deviceadd($vmid, $devicefull); -return undef if(!qemu_deviceaddverify($vmid, $deviceid)); -} +qemu_deviceaddverify($vmid, $deviceid); -if ($deviceid =~ m/^(scsi)(\d+)$/) { -return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device); -return undef if !qemu_driveadd($storecfg, $vmid, $device); -my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); -if(!qemu_deviceadd($vmid, $devicefull)) { # fixme: use qemu_deviceaddverify? +} elsif ($deviceid =~ m/^(scsi)(\d+)$/) { + +qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device); +qemu_driveadd($storecfg, $vmid, $device); + + my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); + eval { qemu_deviceadd($vmid, $devicefull); }; + if (my $err = $@) { eval { qemu_drivedel($vmid, $deviceid); }; warn $@ if $@; - return undef; + die $err; } -} -if ($deviceid =~ m/^(net)(\d+)$/) { +} elsif ($deviceid =~ m/^(net)(\d+)$/) { + return undef if !qemu_netdevadd($vmid, $conf, $device, $deviceid); my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid); qemu_deviceadd($vmid, $netdevicefull); -if(!qemu_deviceaddverify($vmid, $deviceid)) { - qemu_netdevdel($vmid, $deviceid); - return undef; +eval { qemu_deviceaddverify($vmid, $deviceid); }; + if (my $err = $@) { + eval { qemu_netdevdel($vmid, $deviceid); }; + warn $@ if $@; + die $err; } -} +} elsif (!$q35 $deviceid =~ m/^(pci\.)(\d+)$/) { -if (!$q35 $deviceid =~ m/^(pci\.)(\d+)$/) { my $bridgeid = $2; my $pciaddr = print_pci_addr($deviceid); my $devicefull = pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr; + qemu_deviceadd($vmid, $devicefull); - return undef if !qemu_deviceaddverify($vmid, $deviceid); + qemu_deviceaddverify($vmid, $deviceid); + +} else { + die can't hotplug device '$deviceid'\n; } return 1; @@ -3154,26 +3161,24 @@ sub qemu_deviceadd { my %options = split(/[=,]/, $devicefull); vm_mon_cmd($vmid, device_add , %options); -return 1; } sub qemu_devicedel { -my($vmid, $deviceid) = @_; +my ($vmid, $deviceid) = @_; my $ret = vm_mon_cmd($vmid, device_del, id = $deviceid); } sub qemu_driveadd { -my($storecfg, $vmid, $device) = @_; +my ($storecfg, $vmid, $device) = @_; my $drive = print_drive_full($storecfg, $vmid, $device); my $ret = vm_human_monitor_command($vmid, drive_add auto $drive); + # If the command succeeds qemu prints: OK -if ($ret !~ m/OK/s) { -syslog(err, adding drive failed: $ret); -return undef; -} -return 1; +return 1 if $ret =~
[pve-devel] [PATCH v5 18/19] vm_deviceunplug: raise expection if something fail
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 81 +++-- 1 file changed, 42 insertions(+), 39 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index c568dfd..14989f8 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3057,8 +3057,9 @@ sub vm_deviceplug { my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); qemu_deviceadd($vmid, $devicefull); if(!qemu_deviceaddverify($vmid, $deviceid)) { - qemu_drivedel($vmid, $deviceid); - return undef; + eval { qemu_drivedel($vmid, $deviceid); }; + warn $@ if $@; + return undef; } } @@ -3075,8 +3076,9 @@ sub vm_deviceplug { return undef if !qemu_driveadd($storecfg, $vmid, $device); my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); if(!qemu_deviceadd($vmid, $devicefull)) { # fixme: use qemu_deviceaddverify? - qemu_drivedel($vmid, $deviceid); - return undef; + eval { qemu_drivedel($vmid, $deviceid); }; + warn $@ if $@; + return undef; } } @@ -3106,39 +3108,40 @@ sub vm_deviceplug { sub vm_deviceunplug { my ($vmid, $conf, $deviceid) = @_; -return 1 if !check_running ($vmid); - -return 1 if !$conf-{hotplug}; +die internal error if !$conf-{hotplug}; my $devices_list = vm_devices_list($vmid); return 1 if !defined($devices_list-{$deviceid}); +die can't unplug bootdisk if $conf-{bootdisk} $conf-{bootdisk} eq $deviceid; + if ($deviceid eq 'tablet') { + qemu_devicedel($vmid, $deviceid); - return 1; -} -die can't unplug bootdisk if $conf-{bootdisk} $conf-{bootdisk} eq $deviceid; +} elsif ($deviceid =~ m/^(virtio)(\d+)$/) { -if ($deviceid =~ m/^(virtio)(\d+)$/) { qemu_devicedel($vmid, $deviceid); -return undef if !qemu_devicedelverify($vmid, $deviceid); -return undef if !qemu_drivedel($vmid, $deviceid); -} - -if ($deviceid =~ m/^(lsi)(\d+)$/) { -return undef if !qemu_devicedel($vmid, $deviceid); -} +qemu_devicedelverify($vmid, $deviceid); +qemu_drivedel($vmid, $deviceid); + +} elsif ($deviceid =~ m/^(lsi)(\d+)$/) { + + qemu_devicedel($vmid, $deviceid); + +} elsif ($deviceid =~ m/^(scsi)(\d+)$/) { -if ($deviceid =~ m/^(scsi)(\d+)$/) { -return undef if !qemu_devicedel($vmid, $deviceid); -return undef if !qemu_drivedel($vmid, $deviceid); -} +qemu_devicedel($vmid, $deviceid); +qemu_drivedel($vmid, $deviceid); + +} elsif ($deviceid =~ m/^(net)(\d+)$/) { -if ($deviceid =~ m/^(net)(\d+)$/) { qemu_devicedel($vmid, $deviceid); -return undef if !qemu_devicedelverify($vmid, $deviceid); -return undef if !qemu_netdevdel($vmid, $deviceid); +qemu_devicedelverify($vmid, $deviceid); +qemu_netdevdel($vmid, $deviceid); + +} else { + die can't unplug device '$deviceid'\n; } return 1; @@ -3156,8 +3159,8 @@ sub qemu_deviceadd { sub qemu_devicedel { my($vmid, $deviceid) = @_; + my $ret = vm_mon_cmd($vmid, device_del, id = $deviceid); -return 1; } sub qemu_driveadd { @@ -3178,14 +3181,13 @@ sub qemu_drivedel { my $ret = vm_human_monitor_command($vmid, drive_del drive-$deviceid); $ret =~ s/^\s+//; -if ($ret =~ m/Device \'.*?\' not found/s) { -# NB: device not found errors mean the drive was auto-deleted and we ignore the error -} -elsif ($ret ne ) { - syslog(err, deleting drive $deviceid failed : $ret); - return undef; -} -return 1; + +return 1 if $ret eq ; + +# NB: device not found errors mean the drive was auto-deleted and we ignore the error +return 1 if $ret =~ m/Device \'.*?\' not found/s; + +die deleting drive $deviceid failed : $ret\n; } sub qemu_deviceaddverify { @@ -3202,16 +3204,18 @@ sub qemu_deviceaddverify { sub qemu_devicedelverify { -my ($vmid,$deviceid) = @_; +my ($vmid, $deviceid) = @_; + +# need to verify that the device is correctly removed as device_del +# is async and empty return is not reliable -#need to verify the device is correctly remove as device_del is async and empty return is not reliable for (my $i = 0; $i = 5; $i++) { my $devices_list = vm_devices_list($vmid); return 1 if !defined($devices_list-{$deviceid}); sleep 1; } -syslog(err, error on hot-unplugging device $deviceid); -return undef; + +die error on hot-unplugging device '$deviceid'\n; } sub qemu_findorcreatescsihw { @@ -3266,7 +3270,6 @@ sub qemu_netdevdel { my ($vmid, $deviceid) = @_; vm_mon_cmd($vmid, netdev_del, id = $deviceid); -return 1; } sub qemu_cpu_hotplug { -- 1.7.10.4
[pve-devel] [PATCH v5 15/19] rename qemu_bridgeadd to qemu_add_pci_bridge
To make it obvious that we add a PCI device, and not a network bridge. Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 14 +- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 391376e..a8828be 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3049,7 +3049,7 @@ sub vm_deviceplug { return 1; } -qemu_bridgeadd($storecfg, $conf, $vmid, $deviceid); #add bridge if we need it for the device +qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device if ($deviceid =~ m/^(virtio)(\d+)$/) { return undef if !qemu_driveadd($storecfg, $vmid, $device); @@ -3226,23 +3226,27 @@ sub qemu_findorcreatescsihw { return 1; } -sub qemu_bridgeadd { +sub qemu_add_pci_bridge { my ($storecfg, $conf, $vmid, $device) = @_; my $bridges = {}; -my $bridgeid = undef; + +my $bridgeid; + print_pci_addr($device, $bridges); while (my ($k, $v) = each %$bridges) { $bridgeid = $k; } -return if !$bridgeid || $bridgeid 1; +return if !defined($bridgeid) || $bridgeid 1; + my $bridge = pci.$bridgeid; my $devices_list = vm_devices_list($vmid); -if(!defined($devices_list-{$bridge})) { +if (!defined($devices_list-{$bridge})) { return undef if !vm_deviceplug($storecfg, $conf, $vmid, $bridge); } + return 1; } -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v5 16/19] vmconfig_hotplug_pending: correctly skip values
Do not use $skip variable (simply raise an exception) Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 26 +++--- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index a8828be..6642928 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3521,26 +3521,24 @@ sub vmconfig_hotplug_pending { my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); foreach my $opt (@delete) { next if $selection !$selection-{$opt}; - my $skip; eval { if ($opt eq 'tablet') { - return undef if !$hotplug; + die skip\n if !$hotplug; if ($defaults-{tablet}) { vm_deviceplug($storecfg, $conf, $vmid, $opt); } else { vm_deviceunplug($vmid, $conf, $opt); } } elsif ($opt eq 'cores') { - return undef if !$hotplug; + die skip\n if !$hotplug; qemu_cpu_hotplug($vmid, $conf, 1); } else { - $skip = 1; # skip non-hot-pluggable options - return undef; + die skip\n; } }; if (my $err = $@) { - $add_error($opt, $err); - } elsif (!$skip) { + $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); @@ -3552,31 +3550,29 @@ sub vmconfig_hotplug_pending { foreach my $opt (keys %{$conf-{pending}}) { next if $selection !$selection-{$opt}; my $value = $conf-{pending}-{$opt}; - my $skip; eval { if ($opt eq 'tablet') { - return undef if !$hotplug; + die skip\n if !$hotplug; if ($value == 1) { vm_deviceplug($storecfg, $conf, $vmid, $opt); } elsif ($value == 0) { vm_deviceunplug($vmid, $conf, $opt); } } elsif ($opt eq 'cores') { - return undef if !$hotplug; + die skip\n if !$hotplug; qemu_cpu_hotplug($vmid, $conf, $value); } elsif ($opt eq 'balloon') { - return undef if !(defined($conf-{shares}) ($conf-{shares} == 0)); + die skip\n if !(defined($conf-{shares}) ($conf-{shares} == 0)); # allow manual ballooning if shares is set to zero my $balloon = $conf-{pending}-{balloon} || $conf-{memory} || $defaults-{memory}; vm_mon_cmd($vmid, balloon, value = $balloon*1024*1024); } else { - $skip = 1; # skip non-hot-pluggable options - return undef; + die skip\n; # skip non-hot-pluggable options } }; if (my $err = $@) { - $add_error($opt, $err); - } elsif (!$skip) { + $add_error($opt, $err) if $err ne skip\n; + } else { # save new config if hotplug was successful $conf-{$opt} = $value; delete $conf-{pending}-{$opt}; -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH 2/2] now if the QMP command starts with guest-+ , it will bind dynamicly to the VMID.qga socket.
Signed-off-by: Wolfgang Link wolfg...@linksystems.org --- PVE/QMPClient.pm | 54 + PVE/QemuServer.pm | 16 +--- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/PVE/QMPClient.pm b/PVE/QMPClient.pm index 9674d00..b5684b6 100755 --- a/PVE/QMPClient.pm +++ b/PVE/QMPClient.pm @@ -20,7 +20,7 @@ use Data::Dumper; # Note: kvm can onyl handle 1 connection, so we close connections asap sub new { -my ($class, $eventcb, $qga) = @_; +my ($class, $eventcb) = @_; my $mux = new IO::Multiplex; @@ -34,7 +34,6 @@ sub new { }, $class; $self-{eventcb} = $eventcb if $eventcb; -$self-{qga} = $qga if $qga; $mux-set_callback_object($self); @@ -106,9 +105,18 @@ sub cmd { }; my $cmdid_seq = 0; +my $cmdid_seq_qga = 0; + my $next_cmdid = sub { -$cmdid_seq++; -return $$.0.$cmdid_seq; +my ($qga) = @_; + +if($qga){ + $cmdid_seq++; + return $$:$cmdid_seq; +} else { + $cmdid_seq_qga++; + return $$.0.$cmdid_seq_qga; +} }; my $close_connection = sub { @@ -124,9 +132,9 @@ my $close_connection = sub { }; my $open_connection = sub { -my ($self, $vmid, $timeout) = @_; +my ($self, $vmid, $timeout, $qga) = @_; -my $sname = PVE::QemuServer::qmp_socket($vmid, $self-{qga}); +my $sname = PVE::QemuServer::qmp_socket($vmid, $qga); $timeout = 1 if !$timeout; @@ -181,7 +189,8 @@ my $check_queue = sub { eval { my $cmd = $self-{current}-{$vmid} = shift @{$self-{queue}-{$vmid}}; - $cmd-{id} = $next_cmdid(); + + $cmd-{id} = $next_cmdid($cmd-{qga}); my $fd = -1; if ($cmd-{execute} eq 'add-fd' || $cmd-{execute} eq 'getfd') { @@ -191,7 +200,7 @@ my $check_queue = sub { my $qmpcmd = undef; - if($self-{qga}){ + if($self-{current}-{$vmid}-{qga}){ my $qmpcmdid =to_json({ execute = 'guest-sync', @@ -242,11 +251,15 @@ sub queue_execute { # open all necessary connections foreach my $vmid (keys %{$self-{queue}}) { next if !scalar(@{$self-{queue}-{$vmid}}); # no commands for the VM + + if ($self-{queue}-{$vmid}[0]-{execute} =~ /^guest\-+/){ + $self-{queue}-{$vmid}[0]-{qga} = 1; + } eval { - my $fh = $open_connection($self, $vmid, $timeout); + my $fh = $open_connection($self, $vmid, $timeout, $self-{queue}-{$vmid}[0]-{qga}); -if(!$self-{qga}){ +if(!$self-{queue}-{$vmid}[0]-{qga}){ my $cmd = { execute = 'qmp_capabilities', arguments = {} }; unshift @{$self-{queue}-{$vmid}}, $cmd; @@ -290,16 +303,17 @@ sub mux_close { # the descriptors. sub mux_input { my ($self, $mux, $fh, $input) = @_; - -if($self-{qga}){ - return if $$input !~ m/}\n(.+)}\n$/; -}else{ - return if $$input !~ m/}\r\n$/; -} -my $raw = $$input; +my $vmid = $self-{fhs_lookup}-{$fh}; +my $raw; +if ($self-{current}-{$vmid}-{qga}) { + return if $$input !~ s/^([^\n]+}\n[^\n]+})\n(.*)$/$2/so; + $raw = $1; +} else { + return if $$input !~ s/^([^\n]+})\r?\n(.*)$/$2/so; + $raw = $1; +} -my $vmid = $self-{fhs_lookup}-{$fh}; if (!$vmid) { warn internal error - unable to lookup vmid; return; @@ -308,7 +322,7 @@ sub mux_input { eval { my @jsons = split(\n, $raw); - if($self-{qga}){ + if($self-{current}-{$vmid}-{qga}){ die response is not complete if @jsons != 2 ; @@ -328,7 +342,7 @@ sub mux_input { $obj = from_json($jsons[1]); if (my $callback = $curcmd-{callback}) { - $callback($vmid, $obj); + $callback($vmid, $obj); } return; diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index d740564..ab02b93 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -2881,13 +2881,6 @@ sub qga_socket { return ${var_run_tmpdir}/$vmid.qga; } -sub vm_qga_cmd { -my ($vmid, $execute, %params) = @_; - -my $cmd = { execute = $execute, arguments = \%params }; -vm_qmp_command($vmid, $cmd, undef, 1); -} - sub pidfile_name { my ($vmid) = @_; return ${var_run_tmpdir}/$vmid.pid; @@ -3481,7 +3474,7 @@ sub vm_mon_cmd_nocheck { } sub vm_qmp_command { -my ($vmid, $cmd, $nocheck, $qga) = @_; +my ($vmid, $cmd, $nocheck) = @_; my $res; @@ -3493,12 +3486,13 @@ sub vm_qmp_command { eval { die VM $vmid not running\n if !check_running($vmid, $nocheck); - my $sname = qmp_socket($vmid, $qga); + my $qga = ($cmd-{execute} =~ /^guest\-+/)?1:0; + my $sname = qmp_socket($vmid,$qga); if (-e $sname) { - my $qmpclient = PVE::QMPClient-new(undef, $qga); + my $qmpclient = PVE::QMPClient-new();
[pve-devel] [PATCH 1/2] This are the condensed changes form http://pve.proxmox.com/pipermail/pve-devel/2013-March/006913.html is added to see the orgin work!
From: Alexandre Derumier aderum...@odiso.com This patch series add code to send command to quest guest agent. The procotol is qmp, so I have reuse as much as possible the current qmpclient the only big difference is that we can't pass an id to a request, so we must send a guest-sync command with an id before the real command command { execute: guest-sync, arguments: { id: 123456 } }{execute:guest-ping} result { return: 123456}\n{return: {}} Signed-off-by: Alexandre Derumier aderum...@odiso.com --- PVE/QMPClient.pm | 77 - PVE/QemuServer.pm | 20 +- 2 files changed, 78 insertions(+), 19 deletions(-) diff --git a/PVE/QMPClient.pm b/PVE/QMPClient.pm index 4e24419..9674d00 100755 --- a/PVE/QMPClient.pm +++ b/PVE/QMPClient.pm @@ -20,7 +20,7 @@ use Data::Dumper; # Note: kvm can onyl handle 1 connection, so we close connections asap sub new { -my ($class, $eventcb) = @_; +my ($class, $eventcb, $qga) = @_; my $mux = new IO::Multiplex; @@ -34,6 +34,7 @@ sub new { }, $class; $self-{eventcb} = $eventcb if $eventcb; +$self-{qga} = $qga if $qga; $mux-set_callback_object($self); @@ -107,7 +108,7 @@ sub cmd { my $cmdid_seq = 0; my $next_cmdid = sub { $cmdid_seq++; -return $$:$cmdid_seq; +return $$.0.$cmdid_seq; }; my $close_connection = sub { @@ -125,7 +126,7 @@ my $close_connection = sub { my $open_connection = sub { my ($self, $vmid, $timeout) = @_; -my $sname = PVE::QemuServer::qmp_socket($vmid); +my $sname = PVE::QemuServer::qmp_socket($vmid, $self-{qga}); $timeout = 1 if !$timeout; @@ -188,10 +189,27 @@ my $check_queue = sub { delete $cmd-{arguments}-{fd}; } - my $qmpcmd = to_json({ - execute = $cmd-{execute}, - arguments = $cmd-{arguments}, - id = $cmd-{id}}); + my $qmpcmd = undef; + + if($self-{qga}){ + + my $qmpcmdid =to_json({ + execute = 'guest-sync', + arguments = { id = int($cmd-{id})}}); + + $qmpcmd = to_json({ + execute = $cmd-{execute}, + arguments = $cmd-{arguments}}); + + $qmpcmd = $qmpcmdid.$qmpcmd; + + }else{ + + $qmpcmd = to_json({ + execute = $cmd-{execute}, + arguments = $cmd-{arguments}, + id = $cmd-{id}}); + } if ($fd = 0) { my $ret = PVE::IPCC::sendfd(fileno($fh), $fd, $qmpcmd); @@ -227,16 +245,19 @@ sub queue_execute { eval { my $fh = $open_connection($self, $vmid, $timeout); - my $cmd = { execute = 'qmp_capabilities', arguments = {} }; - unshift @{$self-{queue}-{$vmid}}, $cmd; + +if(!$self-{qga}){ +my $cmd = { execute = 'qmp_capabilities', arguments = {} }; +unshift @{$self-{queue}-{$vmid}}, $cmd; + $self-{mux}-set_timeout($fh, $timeout); }; if (my $err = $@) { warn $err; $self-{errors}-{$vmid} = $err; } + } } - my $running; for (;;) { @@ -269,10 +290,14 @@ sub mux_close { # the descriptors. sub mux_input { my ($self, $mux, $fh, $input) = @_; + +if($self-{qga}){ + return if $$input !~ m/}\n(.+)}\n$/; +}else{ + return if $$input !~ m/}\r\n$/; +} -return if $$input !~ s/^(.*})\r\n(.*)$/$2/so; - -my $raw = $1; +my $raw = $$input; my $vmid = $self-{fhs_lookup}-{$fh}; if (!$vmid) { @@ -283,6 +308,32 @@ sub mux_input { eval { my @jsons = split(\n, $raw); + if($self-{qga}){ + + die response is not complete if @jsons != 2 ; + + my $obj = from_json($jsons[0]); + my $cmdid = $obj-{return}; + die received responsed without command id\n if !$cmdid; + + my $curcmd = $self-{current}-{$vmid}; + die unable to lookup current command for VM $vmid\n if !$curcmd; + + delete $self-{current}-{$vmid}; + + if ($curcmd-{id} ne $cmdid) { + die got wrong command id '$cmdid' (expected $curcmd-{id})\n; + } + + $obj = from_json($jsons[1]); + + if (my $callback = $curcmd-{callback}) { + $callback($vmid, $obj); + } + + return; + } + foreach my $json (@jsons) { my $obj = from_json($json); next if defined($obj-{QMP}); # skip monitor greeting diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 2970598..d740564 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -2871,8 +2871,9 @@ sub spice_port { } sub qmp_socket { -my ($vmid) = @_; -return ${var_run_tmpdir}/$vmid.qmp; +my ($vmid, $qga) = @_; +my $sockettype = $qga ? 'qga' : 'qmp'; +
Re: [pve-devel] vm_deviceunplug question
I cannot see that: 3001 if ($deviceid =~ m/^(scsi)(\d+)$/) { 3002 return undef if !qemu_devicedel($vmid, $deviceid); 3003 return undef if !qemu_drivedel($vmid, $deviceid); 3004 } so you also remove the device using qemu_devicedel()? What do I miss? for virtio: -device virtio-blk-pci,drive=drive-virtio0,id=virtio0,bus=pci.0,addr=0xa -drive file=/var/lib/vz/images/115/vm-115-disk-34.raw,if=none,id=drive-virtio4,format=raw,aio=native,cache=none,detect-zeroes=on we remove virtio-blk-pci device and drive. (so we check that the pci device|controller is unplugged - need acpi hotplug) for scsi: -device virtio-scsi-pci,id=scsihw0,bus=pci.0,addr=0x5 -drive file=/var/lib/vz/images/115/vm-115-disk-34.raw,if=none,id=drive-scsi0 -device scsi-hd,bus=scsihw0.0,channel=0,scsi-id=0,lun=0,drive=drive-scsi0,id=scsi0 we remove scsi-hd device and drive. (this is not a pci device|controler,so we don't need to check if removal is ok). - Mail original - De: Dietmar Maurer diet...@proxmox.com À: Alexandre DERUMIER aderum...@odiso.com Cc: pve-devel@pve.proxmox.com Envoyé: Mardi 25 Novembre 2014 10:10:45 Objet: RE: vm_deviceunplug question This is because for virtio disk we remove the controller + drive, (1controller - 1 drive) and for scsi we only remove the drive (1controller - 256 drives) I cannot see that: 3001 if ($deviceid =~ m/^(scsi)(\d+)$/) { 3002 return undef if !qemu_devicedel($vmid, $deviceid); 3003 return undef if !qemu_drivedel($vmid, $deviceid); 3004 } so you also remove the device using qemu_devicedel()? What do I miss? ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] vm_deviceunplug question
for scsi: -device virtio-scsi-pci,id=scsihw0,bus=pci.0,addr=0x5 -drive file=/var/lib/vz/images/115/vm-115-disk-34.raw,if=none,id=drive-scsi0 -device scsi-hd,bus=scsihw0.0,channel=0,scsi-id=0,lun=0,drive=drive- scsi0,id=scsi0 we remove scsi-hd device and drive. (this is not a pci device|controler,so we don't need to check if removal is ok). Ah, OK - thank for the explanation. Please can you also review the latest hotplug changes, especially the error handling? I think we get better error messages this way. ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] vm_deviceunplug question
Please can you also review the latest hotplug changes, especially the error handling? I think we get better error messages this way. Yes, I wanted to fix that since a long time, thanks. About network hotplug, I have exception if I try to change the bridge for example, and conf-{hotplug} = 0. I think this is because be always call vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet); at the end of vmconfig_update_net. - Mail original - De: Dietmar Maurer diet...@proxmox.com À: Alexandre DERUMIER aderum...@odiso.com Cc: pve-devel@pve.proxmox.com Envoyé: Mardi 25 Novembre 2014 10:46:33 Objet: RE: vm_deviceunplug question for scsi: -device virtio-scsi-pci,id=scsihw0,bus=pci.0,addr=0x5 -drive file=/var/lib/vz/images/115/vm-115-disk-34.raw,if=none,id=drive-scsi0 -device scsi-hd,bus=scsihw0.0,channel=0,scsi-id=0,lun=0,drive=drive- scsi0,id=scsi0 we remove scsi-hd device and drive. (this is not a pci device|controler,so we don't need to check if removal is ok). Ah, OK - thank for the explanation. Please can you also review the latest hotplug changes, especially the error handling? I think we get better error messages this way. ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] vm_deviceunplug question
Yes, I wanted to fix that since a long time, thanks. OK About network hotplug, I have exception if I try to change the bridge for example, and conf-{hotplug} = 0. I think this is because be always call vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet); at the end of vmconfig_update_net. OK, will try to fix. ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 04/22] write_vm_config : write pending change
example: $conf-{pending}-{virtio1} $conf-{pending}-{delete} = net0,net1 [PENDING] virtio1: ... delete: net0,net1 Signed-off-by: Alexandre Derumier aderum...@odiso.com Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 32 +--- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 9b03e6a..f6b8ce2 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1826,10 +1826,12 @@ sub parse_vm_config { my $key = $1; my $value = $2; $conf-{$key} = $value; - } elsif (($section eq 'pending') ($line =~ m/^delete:\s*(.*\S)\s*$/)) { + } elsif ($line =~ m/^delete:\s*(.*\S)\s*$/) { my $value = $1; - foreach my $opt (split(/,/, $value)) { - $conf-{del}-{$opt} = 1; + if ($section eq 'pending') { + $conf-{delete} = $value; # we parse this later + } else { + warn vm $vmid - propertry 'delete' is only allowed in [PENDING]\n; } } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S+)\s*$/) { my $key = $1; @@ -1893,12 +1895,18 @@ sub write_vm_config { my $used_volids = {}; my $cleanup_config = sub { - my ($cref, $snapname) = @_; + my ($cref, $pending, $snapname) = @_; foreach my $key (keys %$cref) { next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots' || - $key eq 'snapstate'; + $key eq 'snapstate' || $key eq 'pending'; my $value = $cref-{$key}; + if ($key eq 'delete') { + die propertry 'delete' is only allowed in [PENDING]\n + if !$pending; + # fixme: check syntax? + next; + } eval { $value = check_type($key, $value); }; die unable to parse value of '$key' - $@ if $@; @@ -1912,8 +1920,12 @@ sub write_vm_config { }; $cleanup_config($conf); + +$cleanup_config($conf-{pending}, 1); + foreach my $snapname (keys %{$conf-{snapshots}}) { - $cleanup_config($conf-{snapshots}-{$snapname}, $snapname); + die internal error if $snapname eq 'pending'; + $cleanup_config($conf-{snapshots}-{$snapname}, undef, $snapname); } # remove 'unusedX' settings if we re-add a volume @@ -1936,13 +1948,19 @@ sub write_vm_config { } foreach my $key (sort keys %$conf) { - next if $key eq 'digest' || $key eq 'description' || $key eq 'snapshots'; + next if $key eq 'digest' || $key eq 'description' || $key eq 'pending' || $key eq 'snapshots'; $raw .= $key: $conf-{$key}\n; } return $raw; }; my $raw = $generate_raw_config($conf); + +if (scalar(keys %{$conf-{pending}})){ + $raw .= \n[PENDING]\n; + $raw .= $generate_raw_config($conf-{pending}); +} + foreach my $snapname (sort keys %{$conf-{snapshots}}) { $raw .= \n[$snapname]\n; $raw .= $generate_raw_config($conf-{snapshots}-{$snapname}); -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 11/22] vmconfig_cleanup_pending: new method to clenup setting in [PENDING]
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm |8 +--- PVE/QemuServer.pm | 32 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index a8841c2..8c7c076 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -1006,14 +1006,8 @@ my $update_vm_api = sub { } # remove pending changes when nothing changed - my $changes; $conf = PVE::QemuServer::load_config($vmid); # update/reload - foreach my $opt (keys %{$conf-{pending}}) { # add/change - if (defined($conf-{$opt}) ($conf-{pending}-{$opt} eq $conf-{$opt})) { - $changes = 1; - delete $conf-{pending}-{$opt}; - } - } + my $changes = PVE::QemuServer::vmconfig_cleanup_pending($conf); PVE::QemuServer::update_config_nolock($vmid, $conf, 1) if $changes; return if !scalar(keys %{$conf-{pending}}); diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 7219353..2644117 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1442,6 +1442,38 @@ 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}; + } +} + +# remove delete if option is not set +my $pending_delete_hash = {}; +foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + if (defined($conf-{$opt})) { + $pending_delete_hash-{$opt} = 1; + } else { + $changes = 1; + } +} + +my @keylist = keys %$pending_delete_hash; +if (scalar(@keylist)) { + $conf-{pending}-{delete} = join(',', @keylist); +} else { + delete $conf-{pending}-{delete}; +} + +return $changes; +} + my $valid_smbios1_options = { manufacturer = '\S+', product = '\S+', -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 02/22] parse_vm_config: correctly handle $descr
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm |6 -- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 96eb6ec..e020740 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1782,7 +1782,7 @@ sub parse_vm_config { my $res = { digest = Digest::SHA::sha1_hex($raw), snapshots = {}, - pending = {} + pending = {}, }; $filename =~ m|/qemu-server/(\d+)\.conf$| @@ -1798,10 +1798,12 @@ sub parse_vm_config { next if $line =~ m/^\s*$/; if ($line =~ m/^\[PENDING\]\s*$/i) { + $conf-{description} = $descr if $descr; + $descr = ''; $conf = $res-{pending} = {}; next; - }elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { + } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { my $snapname = $1; $conf-{description} = $descr if $descr; $descr = ''; -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 01/22] parse_vm_config : parse pending changes
From: Alexandre Derumier aderum...@odiso.com example: [PENDING] virtio1:... delete:net0,net1 $conf-{pending}-{virtio1} $conf-{pending}-{del}-{net0} $conf-{pending}-{del}-{net1} Signed-off-by: Alexandre Derumier aderum...@odiso.com Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 13 - 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index b5aafc3..96eb6ec 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1782,6 +1782,7 @@ sub parse_vm_config { my $res = { digest = Digest::SHA::sha1_hex($raw), snapshots = {}, + pending = {} }; $filename =~ m|/qemu-server/(\d+)\.conf$| @@ -1796,7 +1797,11 @@ sub parse_vm_config { foreach my $line (@lines) { next if $line =~ m/^\s*$/; - if ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { + if ($line =~ m/^\[PENDING\]\s*$/i) { + $conf = $res-{pending} = {}; + next; + + }elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { my $snapname = $1; $conf-{description} = $descr if $descr; $descr = ''; @@ -1817,6 +1822,12 @@ sub parse_vm_config { my $key = $1; my $value = $2; $conf-{$key} = $value; + } elsif ($line =~ m/^(delete):\s*(.*\S)\s*$/) { + my $key = $1; + my $value = $2; + foreach my $opt (split(/,/, $value)) { + $conf-{del}-{$opt} = 1; + } } elsif ($line =~ m/^([a-z][a-z_]*\d*):\s*(\S+)\s*$/) { my $key = $1; my $value = $2; -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 06/22] implement vmconfig_apply_pending for stopped VM
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 191 ++ 1 file changed, 119 insertions(+), 72 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 222c44f..41d75d0 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -37,6 +37,73 @@ my $resolve_cdrom_alias = sub { } }; +my $vm_is_volid_owner = sub { +my ($storecfg, $vmid, $volid) =@_; + +if ($volid !~ m|^/|) { + my ($path, $owner); + eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); }; + if ($owner ($owner == $vmid)) { + return 1; + } +} + +return undef; +}; + +my $test_deallocate_drive = sub { +my ($storecfg, $vmid, $key, $drive, $force) = @_; + +if (!PVE::QemuServer::drive_is_cdrom($drive)) { + my $volid = $drive-{file}; + if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + if ($force || $key =~ m/^unused/) { + my $sid = PVE::Storage::parse_volume_id($volid); + return $sid; + } + } +} + +return undef; +}; + +my $pending_delete_option = sub { +my ($conf, $key) = @_; + +delete $conf-{pending}-{$key}; +my $pending_delete_hash = { $key = 1 }; +foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; +} +$conf-{pending}-{delete} = join(',', keys %$pending_delete_hash); +}; + +my $pending_undelete_option = sub { +my ($conf, $key) = @_; + +my $pending_delete_hash = {}; +foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; +} +delete $pending_delete_hash-{$key}; + +my @keylist = keys %$pending_delete_hash; +if (scalar(@keylist)) { + $conf-{pending}-{delete} = join(',', @keylist); +} else { + delete $conf-{pending}-{delete}; +} +}; + +my $register_unused_drive = sub { +my ($storecfg, $vmid, $conf, $drive) = @_; +if (!PVE::QemuServer::drive_is_cdrom($drive)) { + my $volid = $drive-{file}; + if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); + } +} +}; my $check_storage_access = sub { my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_; @@ -639,36 +706,6 @@ __PACKAGE__-register_method({ return $conf; }}); -my $vm_is_volid_owner = sub { -my ($storecfg, $vmid, $volid) =@_; - -if ($volid !~ m|^/|) { - my ($path, $owner); - eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); }; - if ($owner ($owner == $vmid)) { - return 1; - } -} - -return undef; -}; - -my $test_deallocate_drive = sub { -my ($storecfg, $vmid, $key, $drive, $force) = @_; - -if (!PVE::QemuServer::drive_is_cdrom($drive)) { - my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { - if ($force || $key =~ m/^unused/) { - my $sid = PVE::Storage::parse_volume_id($volid); - return $sid; - } - } -} - -return undef; -}; - my $delete_drive = sub { my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_; @@ -868,6 +905,48 @@ my $vmconfig_update_net = sub { die error hotplug $opt if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $net); }; +my $vmconfig_apply_pending = sub { +my ($vmid, $conf, $storecfg, $running) = @_; + +my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); +foreach my $opt (@delete) { # delete + die internal error if $opt =~ m/^unused/; + $conf = PVE::QemuServer::load_config($vmid); # update/reload + if (!defined($conf-{$opt})) { + $pending_undelete_option($conf, $opt); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } elsif (PVE::QemuServer::valid_drivename($opt)) { + $register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf-{$opt})); + $pending_undelete_option($conf, $opt); + delete $conf-{$opt}; + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } else { + $pending_undelete_option($conf, $opt); + delete $conf-{$opt}; + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } +} + +$conf = PVE::QemuServer::load_config($vmid); # update/reload + +foreach my $opt (keys %{$conf-{pending}}) { # add/change + $conf = PVE::QemuServer::load_config($vmid); # update/reload + + if (defined($conf-{$opt}) ($conf-{$opt} eq $conf-{pending}-{$opt})) { + # skip if nothing changed + } elsif (PVE::QemuServer::valid_drivename($opt)) { + $register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf-{$opt})) + if defined($conf-{$opt}); +
[pve-devel] [PATCH v6 03/22] parse_vm_config: only allow 'delete' inside [PENDING]
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 13 +++-- 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index e020740..9b03e6a 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -1792,22 +1792,24 @@ sub parse_vm_config { my $conf = $res; my $descr = ''; +my $section = ''; my @lines = split(/\n/, $raw); foreach my $line (@lines) { next if $line =~ m/^\s*$/; if ($line =~ m/^\[PENDING\]\s*$/i) { + $section = 'pending'; $conf-{description} = $descr if $descr; $descr = ''; - $conf = $res-{pending} = {}; + $conf = $res-{$section} = {}; next; } elsif ($line =~ m/^\[([a-z][a-z0-9_\-]+)\]\s*$/i) { - my $snapname = $1; + $section = $1; $conf-{description} = $descr if $descr; $descr = ''; - $conf = $res-{snapshots}-{$snapname} = {}; + $conf = $res-{snapshots}-{$section} = {}; next; } @@ -1824,9 +1826,8 @@ sub parse_vm_config { my $key = $1; my $value = $2; $conf-{$key} = $value; - } elsif ($line =~ m/^(delete):\s*(.*\S)\s*$/) { - my $key = $1; - my $value = $2; + } elsif (($section eq 'pending') ($line =~ m/^delete:\s*(.*\S)\s*$/)) { + my $value = $1; foreach my $opt (split(/,/, $value)) { $conf-{del}-{$opt} = 1; } -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 00/22] qemu-server: implement pending changes
Based on Alexandres patches (sent to pve-devel list): qemu-server : implement pending changes v2 pending changes hotplug v2 Changes in v6: - implement disk hotplug - vmconfig_update_net: do not call vm_deviceplug() if hotplug == 0 - remove unused code Changes in v5: - implement network hotplug - cleanup error handling in vm_deviceunplug and vm_deviceplug - bug fixes Changes in v4: - implement API/CLI to get pending changes - improve hotplug error handling (see cpu hotplug as example) - new method to cleanup setting in [PENDING] - code cleanups Changes in v3: - I tried to simplify things by always writing changes into pending section first. - do not parse 'delete' option parse_vm_config TODO: test Alexandre Derumier (1): parse_vm_config : parse pending changes Dietmar Maurer (21): parse_vm_config: correctly handle $descr parse_vm_config: only allow 'delete' inside [PENDING] write_vm_config : write pending change update_vm_api: always write into pending section implement vmconfig_apply_pending for stopped VM vm_start: apply pending changes fix balloon consistency check (consider pending changes) implement trivial hotplug vmconfig_hotplug_pending: implement tablet hotplug vmconfig_cleanup_pending: new method to clenup setting in [PENDING] code cleanup, delete trailing white space vmconfig_hotplug_pending: improve hotplug error handling implement API/CLI to get pending changes rename qemu_bridgeadd to qemu_add_pci_bridge vmconfig_hotplug_pending: correctly skip values vmconfig_hotplug_pending : add update_net vm_deviceunplug: raise expection if something fail vm_deviceplug: always raise exception on error vmconfig_hotplug_pending : add update_disk vmconfig_update_net: do not call vm_deviceplug() if hotplug == 0 remove unused code PVE/API2/Qemu.pm | 424 ++--- PVE/QemuServer.pm | 674 - pve-bridge|8 +- qm| 27 +++ 4 files changed, 769 insertions(+), 364 deletions(-) -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 12/22] code cleanup, delete trailing white space
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 20 ++-- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 2644117..dd06f2f 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3480,7 +3480,7 @@ sub set_migration_caps { sub vmconfig_hotplug_pending { my ($vmid, $conf, $storecfg) = @_; -my $defaults = PVE::QemuServer::load_defaults(); +my $defaults = load_defaults(); # commit values which do not have any impact on running VM first @@ -3521,9 +3521,9 @@ sub vmconfig_hotplug_pending { foreach my $opt (@delete) { if ($opt eq 'tablet') { if ($defaults-{tablet}) { - PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt); + vm_deviceplug($storecfg, $conf, $vmid, $opt); } else { - PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); + vm_deviceunplug($vmid, $conf, $opt); } } else { # skip non-hot-pluggable options @@ -3543,9 +3543,9 @@ sub vmconfig_hotplug_pending { if ($opt eq 'tablet') { if ($value == 1) { - PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt); + vm_deviceplug($storecfg, $conf, $vmid, $opt); } elsif ($value == 0) { - PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); + vm_deviceunplug($vmid, $conf, $opt); } } else { # skip non-hot-pluggable options @@ -3691,15 +3691,15 @@ sub vm_start { if ($migratedfrom) { eval { - PVE::QemuServer::set_migration_caps($vmid); + set_migration_caps($vmid); }; warn $@ if $@; if ($spice_port) { print spice listens on port $spice_port\n; if ($spice_ticket) { - PVE::QemuServer::vm_mon_cmd_nocheck($vmid, set_password, protocol = 'spice', password = $spice_ticket); - PVE::QemuServer::vm_mon_cmd_nocheck($vmid, expire_password, protocol = 'spice', time = +30); + vm_mon_cmd_nocheck($vmid, set_password, protocol = 'spice', password = $spice_ticket); + vm_mon_cmd_nocheck($vmid, expire_password, protocol = 'spice', time = +30); } } @@ -4763,7 +4763,7 @@ sub restore_tar_archive { my $storecfg = cfs_read_file('storage.cfg'); # destroy existing data - keep empty config -my $vmcfgfn = PVE::QemuServer::config_file($vmid); +my $vmcfgfn = config_file($vmid); destroy_vm($storecfg, $vmid, 1) if -f $vmcfgfn; my $tocmd = /usr/lib/qemu-server/qmextract; @@ -5567,7 +5567,7 @@ sub get_current_qemu_machine { my ($vmid) = @_; my $cmd = { execute = 'query-machines', arguments = {} }; -my $res = PVE::QemuServer::vm_qmp_command($vmid, $cmd); +my $res = vm_qmp_command($vmid, $cmd); my ($current, $default); foreach my $e (@$res) { -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 05/22] update_vm_api: always write into pending section
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 99 -- 1 file changed, 97 insertions(+), 2 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 0787074..222c44f 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -951,6 +951,44 @@ my $update_vm_api = sub { $check_storage_access($rpcenv, $authuser, $storecfg, $vmid, $param); +my $pending_delete_option = sub { + my ($conf, $key) = @_; + + delete $conf-{pending}-{$key}; + my $pending_delete_hash = { $key = 1 }; + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; + } + $conf-{pending}-{delete} = join(',', keys %$pending_delete_hash); +}; + +my $pending_undelete_option = sub { + my ($conf, $key) = @_; + + my $pending_delete_hash = {}; + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; + } + delete $pending_delete_hash-{$key}; + + my @keylist = keys %$pending_delete_hash; + if (scalar(@keylist)) { + $conf-{pending}-{delete} = join(',', @keylist); + } else { + delete $conf-{pending}-{delete}; + } +}; + +my $register_unused_drive = sub { + my ($conf, $drive) = @_; + if (!PVE::QemuServer::drive_is_cdrom($drive)) { + my $volid = $drive-{file}; + if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); + } + } +}; + my $updatefn = sub { my $conf = PVE::QemuServer::load_config($vmid); @@ -960,6 +998,7 @@ my $update_vm_api = sub { PVE::QemuServer::check_lock($conf) if !$skiplock; + # fixme: wrong place? howto handle pending changes? @delete ? if ($param-{memory} || defined($param-{balloon})) { my $maxmem = $param-{memory} || $conf-{memory} || $defaults-{memory}; my $balloon = defined($param-{balloon}) ? $param-{balloon} : $conf-{balloon}; @@ -974,11 +1013,67 @@ my $update_vm_api = sub { print update VM $vmid: . join (' ', @paramarr) . \n; - foreach my $opt (@delete) { # delete + # write updates to pending section + + foreach my $opt (@delete) { $conf = PVE::QemuServer::load_config($vmid); # update/reload - $vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force); + if ($opt =~ m/^unused/) { + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); + my $drive = PVE::QemuServer::parse_drive($opt, $conf-{$opt}); + if (my $sid = $test_deallocate_drive($storecfg, $vmid, $opt, $drive, $force)) { + $rpcenv-check($authuser, /storage/$sid, ['Datastore.AllocateSpace']); + $delete_drive($conf, $storecfg, $vmid, $opt, $drive); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } + } elsif (PVE::QemuServer::valid_drivename($opt)) { + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); + $register_unused_drive($conf, PVE::QemuServer::parse_drive($opt, $conf-{pending}-{$opt})) + if defined($conf-{pending}-{$opt}); + $pending_delete_option($conf, $opt); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } else { + $pending_delete_option($conf, $opt); + PVE::QemuServer::update_config_nolock($vmid, $conf, 1); + } } + foreach my $opt (keys %$param) { # add/change + $conf = PVE::QemuServer::load_config($vmid); # update/reload + next if defined($conf-{pending}-{$opt}) ($param-{$opt} eq $conf-{pending}-{$opt}); # skip if nothing changed + + if (PVE::QemuServer::valid_drivename($opt)) { + my $drive = PVE::QemuServer::parse_drive($opt, $param-{$opt}); + if (PVE::QemuServer::drive_is_cdrom($drive)) { # CDROM + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.CDROM']); + } else { + $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); + } + $register_unused_drive($conf, PVE::QemuServer::parse_drive($opt, $conf-{pending}-{$opt})) + if defined($conf-{pending}-{$opt}); + + $create_disks($rpcenv, $authuser, $conf-{pending}, $storecfg, $vmid, undef, {$opt = $param-{$opt}}); + } else { + $conf-{pending}-{$opt} = $param-{$opt}; + } +
[pve-devel] [PATCH v6 21/22] vmconfig_update_net: do not call vm_deviceplug() if hotplug == 0
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm |8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 4a44ac8..24c77ef 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3699,10 +3699,16 @@ sub vmconfig_update_net { PVE::Network::tap_unplug($iface); PVE::Network::tap_plug($iface, $newnet-{bridge}, $newnet-{tag}, $newnet-{firewall}); } + + return 1; } } -vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet); +if ($conf-{hotplug}) { + vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet); +} else { + die skip\n; +} } sub vmconfig_update_disk { -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 20/22] vmconfig_hotplug_pending : add update_disk
Signed-off-by: Alexandre Derumier aderum...@odiso.com Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 94 ++--- PVE/QemuServer.pm | 92 +++ 2 files changed, 94 insertions(+), 92 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index ea045c9..064d7ad 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -807,96 +807,6 @@ my $vmconfig_delete_option = sub { PVE::QemuServer::update_config_nolock($vmid, $conf, 1); }; -my $safe_num_ne = sub { -my ($a, $b) = @_; - -return 0 if !defined($a) !defined($b); -return 1 if !defined($a); -return 1 if !defined($b); - -return $a != $b; -}; - -my $vmconfig_update_disk = sub { -my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $value, $force) = @_; - -my $drive = PVE::QemuServer::parse_drive($opt, $value); - -if (PVE::QemuServer::drive_is_cdrom($drive)) { #cdrom - $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.CDROM']); -} else { - $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); -} - -if ($conf-{$opt}) { - - if (my $old_drive = PVE::QemuServer::parse_drive($opt, $conf-{$opt})) { - - my $media = $drive-{media} || 'disk'; - my $oldmedia = $old_drive-{media} || 'disk'; - die unable to change media type\n if $media ne $oldmedia; - - if (!PVE::QemuServer::drive_is_cdrom($old_drive) - ($drive-{file} ne $old_drive-{file})) { # delete old disks - - $vmconfig_delete_option($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force); - $conf = PVE::QemuServer::load_config($vmid); # update/reload - } - -if($safe_num_ne($drive-{mbps}, $old_drive-{mbps}) || - $safe_num_ne($drive-{mbps_rd}, $old_drive-{mbps_rd}) || - $safe_num_ne($drive-{mbps_wr}, $old_drive-{mbps_wr}) || - $safe_num_ne($drive-{iops}, $old_drive-{iops}) || - $safe_num_ne($drive-{iops_rd}, $old_drive-{iops_rd}) || - $safe_num_ne($drive-{iops_wr}, $old_drive-{iops_wr}) || - $safe_num_ne($drive-{mbps_max}, $old_drive-{mbps_max}) || - $safe_num_ne($drive-{mbps_rd_max}, $old_drive-{mbps_rd_max}) || - $safe_num_ne($drive-{mbps_wr_max}, $old_drive-{mbps_wr_max}) || - $safe_num_ne($drive-{iops_max}, $old_drive-{iops_max}) || - $safe_num_ne($drive-{iops_rd_max}, $old_drive-{iops_rd_max}) || - $safe_num_ne($drive-{iops_wr_max}, $old_drive-{iops_wr_max})) { - PVE::QemuServer::qemu_block_set_io_throttle($vmid,drive-$opt, - ($drive-{mbps} || 0)*1024*1024, - ($drive-{mbps_rd} || 0)*1024*1024, - ($drive-{mbps_wr} || 0)*1024*1024, - $drive-{iops} || 0, - $drive-{iops_rd} || 0, - $drive-{iops_wr} || 0, - ($drive-{mbps_max} || 0)*1024*1024, - ($drive-{mbps_rd_max} || 0)*1024*1024, - ($drive-{mbps_wr_max} || 0)*1024*1024, - $drive-{iops_max} || 0, - $drive-{iops_rd_max} || 0, - $drive-{iops_wr_max} || 0) - if !PVE::QemuServer::drive_is_cdrom($drive); -} - } -} - -$create_disks($rpcenv, $authuser, $conf, $storecfg, $vmid, undef, {$opt = $value}); -PVE::QemuServer::update_config_nolock($vmid, $conf, 1); - -$conf = PVE::QemuServer::load_config($vmid); # update/reload -$drive = PVE::QemuServer::parse_drive($opt, $conf-{$opt}); - -if (PVE::QemuServer::drive_is_cdrom($drive)) { # cdrom - - if (PVE::QemuServer::check_running($vmid)) { - if ($drive-{file} eq 'none') { - PVE::QemuServer::vm_mon_cmd($vmid, eject,force = JSON::true,device = drive-$opt); - } else { - my $path = PVE::QemuServer::get_iso_path($storecfg, $vmid, $drive-{file}); - PVE::QemuServer::vm_mon_cmd($vmid, eject,force = JSON::true,device = drive-$opt); #force eject if locked - PVE::QemuServer::vm_mon_cmd($vmid, change,device = drive-$opt,target = $path) if $path; - } - } - -} else { # hotplug new disks - - die error hotplug $opt if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $drive); -
[pve-devel] [PATCH v6 22/22] remove unused code
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 138 -- 1 file changed, 29 insertions(+), 109 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 064d7ad..3d1a7e7 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -188,6 +188,34 @@ my $create_disks = sub { return $vollist; }; +my $delete_drive = sub { +my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_; + +if (!PVE::QemuServer::drive_is_cdrom($drive)) { + my $volid = $drive-{file}; + + if (PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) { + if ($force || $key =~ m/^unused/) { + eval { + # check if the disk is really unused + my $used_paths = PVE::QemuServer::get_used_paths($vmid, $storecfg, $conf, 1, $key); + my $path = PVE::Storage::path($storecfg, $volid); + + die unable to delete '$volid' - volume is still in use (snapshot?)\n + if $used_paths-{$path}; + + PVE::Storage::vdisk_free($storecfg, $volid); + }; + die $@ if $@; + } else { + PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); + } + } +} + +delete $conf-{$key}; +}; + my $check_vm_modify_config_perm = sub { my ($rpcenv, $authuser, $vmid, $pool, $key_list) = @_; @@ -738,75 +766,6 @@ __PACKAGE__-register_method({ return $res; }}); -my $delete_drive = sub { -my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_; - -if (!PVE::QemuServer::drive_is_cdrom($drive)) { - my $volid = $drive-{file}; - - if (PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) { - if ($force || $key =~ m/^unused/) { - eval { - # check if the disk is really unused - my $used_paths = PVE::QemuServer::get_used_paths($vmid, $storecfg, $conf, 1, $key); - my $path = PVE::Storage::path($storecfg, $volid); - - die unable to delete '$volid' - volume is still in use (snapshot?)\n - if $used_paths-{$path}; - - PVE::Storage::vdisk_free($storecfg, $volid); - }; - die $@ if $@; - } else { - PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); - } - } -} - -delete $conf-{$key}; -}; - -my $vmconfig_delete_option = sub { -my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $force) = @_; - -return if !defined($conf-{$opt}); - -my $isDisk = PVE::QemuServer::valid_drivename($opt)|| ($opt =~ m/^unused/); - -if ($isDisk) { - $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); - - my $drive = PVE::QemuServer::parse_drive($opt, $conf-{$opt}); - if (my $sid = $test_deallocate_drive($storecfg, $vmid, $opt, $drive, $force)) { - $rpcenv-check($authuser, /storage/$sid, ['Datastore.AllocateSpace']); - } -} - -my $unplugwarning = ; -if ($conf-{ostype} $conf-{ostype} eq 'l26') { - $unplugwarning = brverify that you have acpiphp pci_hotplug modules loaded in your guest VM; -} elsif ($conf-{ostype} $conf-{ostype} eq 'l24') { - $unplugwarning = brkernel 2.4 don't support hotplug, please disable hotplug in options; -} elsif (!$conf-{ostype} || ($conf-{ostype} $conf-{ostype} eq 'other')) { - $unplugwarning = brverify that your guest support acpi hotplug; -} - -if ($opt eq 'tablet') { - PVE::QemuServer::vm_deviceplug(undef, $conf, $vmid, $opt); -} else { -die error hot-unplug $opt $unplugwarning if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); -} - -if ($isDisk) { - my $drive = PVE::QemuServer::parse_drive($opt, $conf-{$opt}); - $delete_drive($conf, $storecfg, $vmid, $opt, $drive, $force); -} else { - delete $conf-{$opt}; -} - -PVE::QemuServer::update_config_nolock($vmid, $conf, 1); -}; - # POST/PUT {vmid}/config implementation # # The original API used PUT (idempotent) an we assumed that all operations @@ -983,47 +942,8 @@ my $update_vm_api = sub { } else { PVE::QemuServer::vmconfig_apply_pending($vmid, $conf, $storecfg, $running); } - return; # TODO: remove old code below - - foreach my $opt (keys %$param) { # add/change - - $conf = PVE::QemuServer::load_config($vmid); # update/reload - - next if $conf-{$opt} ($param-{$opt} eq $conf-{$opt}); # skip if nothing changed - if (PVE::QemuServer::valid_drivename($opt)) { - - #$vmconfig_update_disk($rpcenv, $authuser, $conf, $storecfg, $vmid, - # $opt, $param-{$opt}, $force); - - } elsif ($opt =~ m/^net(\d+)$/) { #nics - -
[pve-devel] [PATCH v6 19/22] vm_deviceplug: always raise exception on error
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 92 - 1 file changed, 49 insertions(+), 43 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 14989f8..243d069 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3032,73 +3032,80 @@ sub vm_devices_list { return $devices; } -# fixme: this should raise exceptions on error! sub vm_deviceplug { my ($storecfg, $conf, $vmid, $deviceid, $device) = @_; -return 1 if !check_running($vmid); +die internal error if !$conf-{hotplug}; my $q35 = machine_type_is_q35($conf); -return 1 if !$conf-{hotplug}; - my $devices_list = vm_devices_list($vmid); return 1 if defined($devices_list-{$deviceid}); +qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device + if ($deviceid eq 'tablet') { + qemu_deviceadd($vmid, print_tabletdevice_full($conf)); - return 1; -} -qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device +} elsif ($deviceid =~ m/^(virtio)(\d+)$/) { -if ($deviceid =~ m/^(virtio)(\d+)$/) { -return undef if !qemu_driveadd($storecfg, $vmid, $device); +qemu_driveadd($storecfg, $vmid, $device); my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); + qemu_deviceadd($vmid, $devicefull); -if(!qemu_deviceaddverify($vmid, $deviceid)) { + eval { qemu_deviceaddverify($vmid, $deviceid); }; + if (my $err = $@) { eval { qemu_drivedel($vmid, $deviceid); }; warn $@ if $@; - return undef; + die $err; } -} -if ($deviceid =~ m/^(scsihw)(\d+)$/) { +} elsif ($deviceid =~ m/^(scsihw)(\d+)$/) { + my $scsihw = defined($conf-{scsihw}) ? $conf-{scsihw} : lsi; my $pciaddr = print_pci_addr($deviceid); my $devicefull = $scsihw,id=$deviceid$pciaddr; + qemu_deviceadd($vmid, $devicefull); -return undef if(!qemu_deviceaddverify($vmid, $deviceid)); -} +qemu_deviceaddverify($vmid, $deviceid); -if ($deviceid =~ m/^(scsi)(\d+)$/) { -return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device); -return undef if !qemu_driveadd($storecfg, $vmid, $device); -my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); -if(!qemu_deviceadd($vmid, $devicefull)) { # fixme: use qemu_deviceaddverify? +} elsif ($deviceid =~ m/^(scsi)(\d+)$/) { + +qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device); +qemu_driveadd($storecfg, $vmid, $device); + + my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); + eval { qemu_deviceadd($vmid, $devicefull); }; + if (my $err = $@) { eval { qemu_drivedel($vmid, $deviceid); }; warn $@ if $@; - return undef; + die $err; } -} -if ($deviceid =~ m/^(net)(\d+)$/) { +} elsif ($deviceid =~ m/^(net)(\d+)$/) { + return undef if !qemu_netdevadd($vmid, $conf, $device, $deviceid); my $netdevicefull = print_netdevice_full($vmid, $conf, $device, $deviceid); qemu_deviceadd($vmid, $netdevicefull); -if(!qemu_deviceaddverify($vmid, $deviceid)) { - qemu_netdevdel($vmid, $deviceid); - return undef; +eval { qemu_deviceaddverify($vmid, $deviceid); }; + if (my $err = $@) { + eval { qemu_netdevdel($vmid, $deviceid); }; + warn $@ if $@; + die $err; } -} +} elsif (!$q35 $deviceid =~ m/^(pci\.)(\d+)$/) { -if (!$q35 $deviceid =~ m/^(pci\.)(\d+)$/) { my $bridgeid = $2; my $pciaddr = print_pci_addr($deviceid); my $devicefull = pci-bridge,id=pci.$bridgeid,chassis_nr=$bridgeid$pciaddr; + qemu_deviceadd($vmid, $devicefull); - return undef if !qemu_deviceaddverify($vmid, $deviceid); + qemu_deviceaddverify($vmid, $deviceid); + +} else { + die can't hotplug device '$deviceid'\n; } return 1; @@ -3154,26 +3161,24 @@ sub qemu_deviceadd { my %options = split(/[=,]/, $devicefull); vm_mon_cmd($vmid, device_add , %options); -return 1; } sub qemu_devicedel { -my($vmid, $deviceid) = @_; +my ($vmid, $deviceid) = @_; my $ret = vm_mon_cmd($vmid, device_del, id = $deviceid); } sub qemu_driveadd { -my($storecfg, $vmid, $device) = @_; +my ($storecfg, $vmid, $device) = @_; my $drive = print_drive_full($storecfg, $vmid, $device); my $ret = vm_human_monitor_command($vmid, drive_add auto $drive); + # If the command succeeds qemu prints: OK -if ($ret !~ m/OK/s) { -syslog(err, adding drive failed: $ret); -return undef; -} -return 1; +return 1 if $ret =~
[pve-devel] [PATCH v6 13/22] vmconfig_hotplug_pending: improve hotplug error handling
Simplify code, and allow to partially apply pending changes using a new $selection parameter. Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 12 - PVE/QemuServer.pm | 149 ++--- 2 files changed, 95 insertions(+), 66 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 8c7c076..b116c24 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -961,7 +961,10 @@ my $update_vm_api = sub { # write updates to pending section + my $modified = {}; # record what $option we modify + foreach my $opt (@delete) { + $modified-{$opt} = 1; $conf = PVE::QemuServer::load_config($vmid); # update/reload if ($opt =~ m/^unused/) { $rpcenv-check_vm_perm($authuser, $vmid, undef, ['VM.Config.Disk']); @@ -984,6 +987,7 @@ my $update_vm_api = sub { } foreach my $opt (keys %$param) { # add/change + $modified-{$opt} = 1; $conf = PVE::QemuServer::load_config($vmid); # update/reload next if defined($conf-{pending}-{$opt}) ($param-{$opt} eq $conf-{pending}-{$opt}); # skip if nothing changed @@ -1017,8 +1021,14 @@ my $update_vm_api = sub { # apply pending changes $conf = PVE::QemuServer::load_config($vmid); # update/reload - PVE::QemuServer::vmconfig_apply_pending($vmid, $conf, $storecfg, $running); + if ($running) { + my $errors = {}; + PVE::QemuServer::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); + } return; # TODO: remove old code below foreach my $opt (keys %$param) { # add/change diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index dd06f2f..391376e 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3266,22 +3266,25 @@ sub qemu_netdevdel { sub qemu_cpu_hotplug { my ($vmid, $conf, $cores) = @_; -die new cores config is not defined if !$cores; -die you can't add more cores than maxcpus - if $conf-{maxcpus} ($cores $conf-{maxcpus}); -return if !check_running($vmid); +my $sockets = $conf-{sockets} || 1; +die cpu hotplug only works with one socket\n + if $sockets 1; -my $currentcores = $conf-{cores} if $conf-{cores}; -die current cores is not defined if !$currentcores; -die maxcpus is not defined if !$conf-{maxcpus}; -raise_param_exc({ 'cores' = online cpu unplug is not yet possible }) - if($cores $currentcores); +die maxcpus is not defined\n + if !$conf-{maxcpus}; + +die you can't add more cores than maxcpus\n + if $cores $conf-{maxcpus}; + +my $currentcores = $conf-{cores} || 1; +die online cpu unplug is not yet possible\n + if $cores $currentcores; my $currentrunningcores = vm_mon_cmd($vmid, query-cpus); -raise_param_exc({ 'cores' = cores number if running vm is different than configuration }) - if scalar (@{$currentrunningcores}) != $currentcores; +die cores number if running vm is different than configuration\n + if scalar(@{$currentrunningcores}) != $currentcores; -for(my $i = $currentcores; $i $cores; $i++) { +for (my $i = $currentcores; $i $cores; $i++) { vm_mon_cmd($vmid, cpu-add, id = int($i)); } } @@ -3477,12 +3480,23 @@ sub set_migration_caps { vm_mon_cmd_nocheck($vmid, migrate-set-capabilities, capabilities = $cap_ref); } +# 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) = @_; +my ($vmid, $conf, $storecfg, $selection, $errors) = @_; my $defaults = load_defaults(); # 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 @@ -3498,73 +3512,78 @@ sub vmconfig_hotplug_pending { $conf = load_config($vmid); # update/reload } -$changes = 0; - -# allow manual ballooning if shares is set to zero - -if (defined($conf-{pending}-{balloon}) defined($conf-{shares}) ($conf-{shares} == 0)) { - my $balloon = $conf-{pending}-{balloon} || $conf-{memory} || $defaults-{memory}; - vm_mon_cmd($vmid, balloon, value = $balloon*1024*1024); - $conf-{balloon} = $conf-{pending}-{balloon}; -
[pve-devel] [PATCH v6 09/22] implement trivial hotplug
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 47 ++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 8524180..2a702d2 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3438,11 +3438,56 @@ sub set_migration_caps { vm_mon_cmd_nocheck($vmid, migrate-set-capabilities, capabilities = $cap_ref); } +sub vmconfig_hotplug_pending { +my ($vmid, $conf, $storecfg) = @_; + +my $defaults = PVE::QemuServer::load_defaults(); + +# commit values which do not have any impact on running VM first + +my $changes = 0; +foreach my $opt (keys %{$conf-{pending}}) { # add/change + if ($opt eq 'name' || $opt eq 'hotplug' || $opt eq 'onboot' || $opt eq 'shares') { + $conf-{$opt} = $conf-{pending}-{$opt}; + delete $conf-{pending}-{$opt}; + $changes = 1; + } +} + +if ($changes) { + update_config_nolock($vmid, $conf, 1); + $conf = load_config($vmid); # update/reload +} + +$changes = 0; + +# allow manual ballooning if shares is set to zero + +if (defined($conf-{pending}-{balloon}) defined($conf-{shares}) ($conf-{shares} == 0)) { + my $balloon = $conf-{pending}-{balloon} || $conf-{memory} || $defaults-{memory}; + vm_mon_cmd($vmid, balloon, value = $balloon*1024*1024); + $conf-{balloon} = $conf-{pending}-{balloon}; + delete $conf-{pending}-{balloon}; + $changes = 1; +} + +if ($changes) { + update_config_nolock($vmid, $conf, 1); + $conf = load_config($vmid); # update/reload +} + +return if !$conf-{hotplug}; + +# fixme: implement disk/network hotplug here + +} sub vmconfig_apply_pending { my ($vmid, $conf, $storecfg, $running) = @_; -die implement me - vm is running if $running; # fixme: if $conf-{hotplug}; +return vmconfig_hotplug_pending($vmid, $conf, $storecfg) if $running; + +# cold plug my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); foreach my $opt (@delete) { # delete -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 14/22] implement API/CLI to get pending changes
Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 86 +- qm | 27 + 2 files changed, 112 insertions(+), 1 deletion(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index b116c24..98a42fe 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -624,7 +624,7 @@ __PACKAGE__-register_method({ path = '{vmid}/config', method = 'GET', proxyto = 'node', -description = Get virtual machine configuration., +description = Get current virtual machine configuration. This does not include pending configuration changes (see 'pending' API)., permissions = { check = ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], }, @@ -650,10 +650,94 @@ __PACKAGE__-register_method({ my $conf = PVE::QemuServer::load_config($param-{vmid}); delete $conf-{snapshots}; + delete $conf-{pending}; return $conf; }}); +__PACKAGE__-register_method({ +name = 'vm_pending', +path = '{vmid}/pending', +method = 'GET', +proxyto = 'node', +description = Get virtual machine configuration, including pending changes., +permissions = { + check = ['perm', '/vms/{vmid}', [ 'VM.Audit' ]], +}, +parameters = { + additionalProperties = 0, + properties = { + node = get_standard_option('pve-node'), + vmid = get_standard_option('pve-vmid'), + }, +}, +returns = { + type = array, + items = { + type = object, + properties = { + key = { + description = Configuration option name., + type = 'string', + }, + value = { + description = Current value., + type = 'string', + optional = 1, + }, + pending = { + description = Pending value., + type = 'string', + optional = 1, + }, + delete = { + description = Indicated a pending delete request., + type = 'boolean', + optional = 1, + }, + }, + }, +}, +code = sub { + my ($param) = @_; + + my $conf = PVE::QemuServer::load_config($param-{vmid}); + + my $pending_delete_hash = {}; + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + $pending_delete_hash-{$opt} = 1; + } + + my $res = []; + + foreach my $opt (keys $conf) { + next if ref($conf-{$opt}); + my $item = { key = $opt }; + $item-{value} = $conf-{$opt} if defined($conf-{$opt}); + $item-{pending} = $conf-{pending}-{$opt} if defined($conf-{pending}-{$opt}); + $item-{delete} = 1 if $pending_delete_hash-{$opt}; + push @$res, $item; + } + + foreach my $opt (keys $conf-{pending}) { + next if $opt eq 'delete'; + next if ref($conf-{pending}-{$opt}); # just to be sure + next if $conf-{$opt}; + my $item = { key = $opt }; + $item-{pending} = $conf-{pending}-{$opt}; + push @$res, $item; + } + + foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { + next if $conf-{pending}-{$opt}; # just to be sure + next if $conf-{$opt}; + my $item = { key = $opt, delete = 1}; + push @$res, $item; + } + + return $res; +}}); + my $delete_drive = sub { my ($conf, $storecfg, $vmid, $key, $drive, $force) = @_; diff --git a/qm b/qm index cea223e..249117c 100755 --- a/qm +++ b/qm @@ -437,6 +437,33 @@ my $cmddef = { } }], +pending = [ PVE::API2::Qemu, 'vm_pending', ['vmid'], + { node = $nodename }, sub { + my $data = shift; + foreach my $item (sort { $a-{key} cmp $b-{key}} @$data) { + my $k = $item-{key}; + next if $k eq 'digest'; + my $v = $item-{value}; + my $p = $item-{pending}; + if ($k eq 'description') { + $v = PVE::Tools::encode_text($v) if defined($v); + $p = PVE::Tools::encode_text($p) if defined($p); + } + if (defined($v)) { + if ($item-{delete}) { + print del $k: $v\n; + } elsif (defined($p)) { + print cur $k: $v\n; + print new $k: $p\n; + } else { + print cur $k: $v\n; + } + } elsif (defined($p)) { + print new $k: $p\n; +
[pve-devel] [PATCH v6 15/22] rename qemu_bridgeadd to qemu_add_pci_bridge
To make it obvious that we add a PCI device, and not a network bridge. Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 14 +- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 391376e..a8828be 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3049,7 +3049,7 @@ sub vm_deviceplug { return 1; } -qemu_bridgeadd($storecfg, $conf, $vmid, $deviceid); #add bridge if we need it for the device +qemu_add_pci_bridge($storecfg, $conf, $vmid, $deviceid); # add PCI bridge if we need it for the device if ($deviceid =~ m/^(virtio)(\d+)$/) { return undef if !qemu_driveadd($storecfg, $vmid, $device); @@ -3226,23 +3226,27 @@ sub qemu_findorcreatescsihw { return 1; } -sub qemu_bridgeadd { +sub qemu_add_pci_bridge { my ($storecfg, $conf, $vmid, $device) = @_; my $bridges = {}; -my $bridgeid = undef; + +my $bridgeid; + print_pci_addr($device, $bridges); while (my ($k, $v) = each %$bridges) { $bridgeid = $k; } -return if !$bridgeid || $bridgeid 1; +return if !defined($bridgeid) || $bridgeid 1; + my $bridge = pci.$bridgeid; my $devices_list = vm_devices_list($vmid); -if(!defined($devices_list-{$bridge})) { +if (!defined($devices_list-{$bridge})) { return undef if !vm_deviceplug($storecfg, $conf, $vmid, $bridge); } + return 1; } -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 17/22] vmconfig_hotplug_pending : add update_net
Signed-off-by: Alexandre Derumier aderum...@odiso.com Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 44 ++- PVE/QemuServer.pm | 67 - pve-bridge|8 +-- 3 files changed, 74 insertions(+), 45 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 98a42fe..ea045c9 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -897,46 +897,6 @@ my $vmconfig_update_disk = sub { } }; -my $vmconfig_update_net = sub { -my ($rpcenv, $authuser, $conf, $storecfg, $vmid, $opt, $value) = @_; - -if ($conf-{$opt} PVE::QemuServer::check_running($vmid)) { - my $oldnet = PVE::QemuServer::parse_net($conf-{$opt}); - my $newnet = PVE::QemuServer::parse_net($value); - - if($oldnet-{model} ne $newnet-{model}){ - #if model change, we try to hot-unplug -die error hot-unplug $opt for update if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); - }else{ - - if($newnet-{bridge} $oldnet-{bridge}){ - my $iface = tap.$vmid.i.$1 if $opt =~ m/net(\d+)/; - - if($newnet-{rate} ne $oldnet-{rate}){ - PVE::Network::tap_rate_limit($iface, $newnet-{rate}); - } - - if(($newnet-{bridge} ne $oldnet-{bridge}) || ($newnet-{tag} ne $oldnet-{tag}) || ($newnet-{firewall} ne $oldnet-{firewall})){ - PVE::Network::tap_unplug($iface); - PVE::Network::tap_plug($iface, $newnet-{bridge}, $newnet-{tag}, $newnet-{firewall}); - } - - }else{ - #if bridge/nat mode change, we try to hot-unplug - die error hot-unplug $opt for update if !PVE::QemuServer::vm_deviceunplug($vmid, $conf, $opt); - } - } - -} -$conf-{$opt} = $value; -PVE::QemuServer::update_config_nolock($vmid, $conf, 1); -$conf = PVE::QemuServer::load_config($vmid); # update/reload - -my $net = PVE::QemuServer::parse_net($conf-{$opt}); - -die error hotplug $opt if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $net); -}; - # POST/PUT {vmid}/config implementation # # The original API used PUT (idempotent) an we assumed that all operations @@ -1128,8 +1088,8 @@ my $update_vm_api = sub { } elsif ($opt =~ m/^net(\d+)$/) { #nics - $vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid, - $opt, $param-{$opt}); + # $vmconfig_update_net($rpcenv, $authuser, $conf, $storecfg, $vmid, + # $opt, $param-{$opt}); } else { diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 6642928..c568dfd 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3032,6 +3032,7 @@ sub vm_devices_list { return $devices; } +# fixme: this should raise exceptions on error! sub vm_deviceplug { my ($storecfg, $conf, $vmid, $deviceid, $device) = @_; @@ -3073,7 +3074,7 @@ sub vm_deviceplug { return undef if !qemu_findorcreatescsihw($storecfg,$conf, $vmid, $device); return undef if !qemu_driveadd($storecfg, $vmid, $device); my $devicefull = print_drivedevice_full($storecfg, $conf, $vmid, $device); -if(!qemu_deviceadd($vmid, $devicefull)) { +if(!qemu_deviceadd($vmid, $devicefull)) { # fixme: use qemu_deviceaddverify? qemu_drivedel($vmid, $deviceid); return undef; } @@ -3101,6 +3102,7 @@ sub vm_deviceplug { return 1; } +# fixme: this should raise exceptions on error! sub vm_deviceunplug { my ($vmid, $conf, $deviceid) = @_; @@ -3532,6 +3534,9 @@ sub vmconfig_hotplug_pending { } elsif ($opt eq 'cores') { die skip\n if !$hotplug; qemu_cpu_hotplug($vmid, $conf, 1); + } elsif ($opt =~ m/^net(\d+)$/) { + die skip\n if !$hotplug; + vm_deviceunplug($vmid, $conf, $opt); } else { die skip\n; } @@ -3566,6 +3571,9 @@ sub vmconfig_hotplug_pending { # allow manual ballooning if shares is set to zero 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, $vmid, $opt, $value); } else { die skip\n; # skip non-hot-pluggable options } @@ -3626,6 +3634,63 @@ sub vmconfig_apply_pending { } } +my $safe_num_ne = sub { +my ($a, $b) = @_; + +return 0 if !defined($a) !defined($b); +return 1 if !defined($a); +return 1 if !defined($b); + +return $a != $b; +}; + +my $safe_string_ne = sub
[pve-devel] [PATCH v6 16/22] vmconfig_hotplug_pending: correctly skip values
Do not use $skip variable (simply raise an exception) Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 26 +++--- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index a8828be..6642928 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3521,26 +3521,24 @@ sub vmconfig_hotplug_pending { my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); foreach my $opt (@delete) { next if $selection !$selection-{$opt}; - my $skip; eval { if ($opt eq 'tablet') { - return undef if !$hotplug; + die skip\n if !$hotplug; if ($defaults-{tablet}) { vm_deviceplug($storecfg, $conf, $vmid, $opt); } else { vm_deviceunplug($vmid, $conf, $opt); } } elsif ($opt eq 'cores') { - return undef if !$hotplug; + die skip\n if !$hotplug; qemu_cpu_hotplug($vmid, $conf, 1); } else { - $skip = 1; # skip non-hot-pluggable options - return undef; + die skip\n; } }; if (my $err = $@) { - $add_error($opt, $err); - } elsif (!$skip) { + $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); @@ -3552,31 +3550,29 @@ sub vmconfig_hotplug_pending { foreach my $opt (keys %{$conf-{pending}}) { next if $selection !$selection-{$opt}; my $value = $conf-{pending}-{$opt}; - my $skip; eval { if ($opt eq 'tablet') { - return undef if !$hotplug; + die skip\n if !$hotplug; if ($value == 1) { vm_deviceplug($storecfg, $conf, $vmid, $opt); } elsif ($value == 0) { vm_deviceunplug($vmid, $conf, $opt); } } elsif ($opt eq 'cores') { - return undef if !$hotplug; + die skip\n if !$hotplug; qemu_cpu_hotplug($vmid, $conf, $value); } elsif ($opt eq 'balloon') { - return undef if !(defined($conf-{shares}) ($conf-{shares} == 0)); + die skip\n if !(defined($conf-{shares}) ($conf-{shares} == 0)); # allow manual ballooning if shares is set to zero my $balloon = $conf-{pending}-{balloon} || $conf-{memory} || $defaults-{memory}; vm_mon_cmd($vmid, balloon, value = $balloon*1024*1024); } else { - $skip = 1; # skip non-hot-pluggable options - return undef; + die skip\n; # skip non-hot-pluggable options } }; if (my $err = $@) { - $add_error($opt, $err); - } elsif (!$skip) { + $add_error($opt, $err) if $err ne skip\n; + } else { # save new config if hotplug was successful $conf-{$opt} = $value; delete $conf-{pending}-{$opt}; -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
[pve-devel] [PATCH v6 07/22] vm_start: apply pending changes
I move related helper methods into PVE::QemuServer. Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/API2/Qemu.pm | 111 - PVE/QemuServer.pm | 103 + 2 files changed, 111 insertions(+), 103 deletions(-) diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm index 41d75d0..d6a70c0 100644 --- a/PVE/API2/Qemu.pm +++ b/PVE/API2/Qemu.pm @@ -37,26 +37,12 @@ my $resolve_cdrom_alias = sub { } }; -my $vm_is_volid_owner = sub { -my ($storecfg, $vmid, $volid) =@_; - -if ($volid !~ m|^/|) { - my ($path, $owner); - eval { ($path, $owner) = PVE::Storage::path($storecfg, $volid); }; - if ($owner ($owner == $vmid)) { - return 1; - } -} - -return undef; -}; - my $test_deallocate_drive = sub { my ($storecfg, $vmid, $key, $drive, $force) = @_; if (!PVE::QemuServer::drive_is_cdrom($drive)) { my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + if ( PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) { if ($force || $key =~ m/^unused/) { my $sid = PVE::Storage::parse_volume_id($volid); return $sid; @@ -67,44 +53,6 @@ my $test_deallocate_drive = sub { return undef; }; -my $pending_delete_option = sub { -my ($conf, $key) = @_; - -delete $conf-{pending}-{$key}; -my $pending_delete_hash = { $key = 1 }; -foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { - $pending_delete_hash-{$opt} = 1; -} -$conf-{pending}-{delete} = join(',', keys %$pending_delete_hash); -}; - -my $pending_undelete_option = sub { -my ($conf, $key) = @_; - -my $pending_delete_hash = {}; -foreach my $opt (PVE::Tools::split_list($conf-{pending}-{delete})) { - $pending_delete_hash-{$opt} = 1; -} -delete $pending_delete_hash-{$key}; - -my @keylist = keys %$pending_delete_hash; -if (scalar(@keylist)) { - $conf-{pending}-{delete} = join(',', @keylist); -} else { - delete $conf-{pending}-{delete}; -} -}; - -my $register_unused_drive = sub { -my ($storecfg, $vmid, $conf, $drive) = @_; -if (!PVE::QemuServer::drive_is_cdrom($drive)) { - my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { - PVE::QemuServer::add_unused_volume($conf, $volid, $vmid); - } -} -}; - my $check_storage_access = sub { my ($rpcenv, $authuser, $storecfg, $vmid, $settings, $default_storage) = @_; @@ -712,7 +660,7 @@ my $delete_drive = sub { if (!PVE::QemuServer::drive_is_cdrom($drive)) { my $volid = $drive-{file}; - if ($vm_is_volid_owner($storecfg, $vmid, $volid)) { + if (PVE::QemuServer::vm_is_volid_owner($storecfg, $vmid, $volid)) { if ($force || $key =~ m/^unused/) { eval { # check if the disk is really unused @@ -905,48 +853,6 @@ my $vmconfig_update_net = sub { die error hotplug $opt if !PVE::QemuServer::vm_deviceplug($storecfg, $conf, $vmid, $opt, $net); }; -my $vmconfig_apply_pending = sub { -my ($vmid, $conf, $storecfg, $running) = @_; - -my @delete = PVE::Tools::split_list($conf-{pending}-{delete}); -foreach my $opt (@delete) { # delete - die internal error if $opt =~ m/^unused/; - $conf = PVE::QemuServer::load_config($vmid); # update/reload - if (!defined($conf-{$opt})) { - $pending_undelete_option($conf, $opt); - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); - } elsif (PVE::QemuServer::valid_drivename($opt)) { - $register_unused_drive($storecfg, $vmid, $conf, PVE::QemuServer::parse_drive($opt, $conf-{$opt})); - $pending_undelete_option($conf, $opt); - delete $conf-{$opt}; - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); - } else { - $pending_undelete_option($conf, $opt); - delete $conf-{$opt}; - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); - } -} - -$conf = PVE::QemuServer::load_config($vmid); # update/reload - -foreach my $opt (keys %{$conf-{pending}}) { # add/change - $conf = PVE::QemuServer::load_config($vmid); # update/reload - - if (defined($conf-{$opt}) ($conf-{$opt} eq $conf-{pending}-{$opt})) { - # skip if nothing changed - } elsif (PVE::QemuServer::valid_drivename($opt)) { - $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}; - PVE::QemuServer::update_config_nolock($vmid, $conf, 1); -} -}; - # POST/PUT {vmid}/config implementation # # The original API
[pve-devel] Qemu-Guest-Agent implementing problems
Hi at all, I have some problems by implementing the qga. There are 4 commands (guest-shutdown, guest-suspend-disk, guest-suspend-ram, guest-suspend-hybrid) which does NOT return a response on success. How should i handle this. Have anyone an Idea? greetings ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] [PATCH v6 21/22] vmconfig_update_net: do not call vm_deviceplug() if hotplug == 0
Ok, It's working fine now - Mail original - De: Dietmar Maurer diet...@proxmox.com À: pve-devel@pve.proxmox.com Envoyé: Mardi 25 Novembre 2014 12:24:28 Objet: [pve-devel] [PATCH v6 21/22] vmconfig_update_net: do not call vm_deviceplug() if hotplug == 0 Signed-off-by: Dietmar Maurer diet...@proxmox.com --- PVE/QemuServer.pm | 8 +++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm index 4a44ac8..24c77ef 100644 --- a/PVE/QemuServer.pm +++ b/PVE/QemuServer.pm @@ -3699,10 +3699,16 @@ sub vmconfig_update_net { PVE::Network::tap_unplug($iface); PVE::Network::tap_plug($iface, $newnet-{bridge}, $newnet-{tag}, $newnet-{firewall}); } + + return 1; } } - vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet); + if ($conf-{hotplug}) { + vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet); + } else { + die skip\n; + } } sub vmconfig_update_disk { -- 1.7.10.4 ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
Re: [pve-devel] Qemu-Guest-Agent implementing problems
I guess we need to use the 'mux_eof' callback for that case. See 'man IO::Multiplex' for details. There are 4 commands (guest-shutdown, guest-suspend-disk, guest-suspend- ram, guest-suspend-hybrid) which does NOT return a response on success. How should i handle this. Have anyone an Idea? ___ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel