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'; + return "${var_run_tmpdir}/$vmid.$sockettype"; } sub qga_socket { @@ -2880,6 +2881,13 @@ 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"; @@ -3473,7 +3481,7 @@ sub vm_mon_cmd_nocheck { } sub vm_qmp_command { - my ($vmid, $cmd, $nocheck) = @_; + my ($vmid, $cmd, $nocheck, $qga) = @_; my $res; @@ -3485,12 +3493,12 @@ sub vm_qmp_command { eval { die "VM $vmid not running\n" if !check_running($vmid, $nocheck); - my $sname = qmp_socket($vmid); + my $sname = qmp_socket($vmid, $qga); if (-e $sname) { - my $qmpclient = PVE::QMPClient->new(); + my $qmpclient = PVE::QMPClient->new(undef, $qga); $res = $qmpclient->cmd($vmid, $cmd, $timeout); - } elsif (-e "${var_run_tmpdir}/$vmid.mon") { + } elsif (-e "${var_run_tmpdir}/$vmid.mon" && !$qga) { die "can't execute complex command on old monitor - stop/start your vm to fix the problem\n" if scalar(%{$cmd->{arguments}}); vm_monitor_command($vmid, $cmd->{execute}, $nocheck); -- 1.7.10.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel