2012-07-12 12:28:27 +02:00
package PVE::QMPClient ;
use strict ;
2013-10-01 13:14:49 +02:00
use warnings ;
2019-10-29 15:59:10 +01:00
2012-07-12 12:28:27 +02:00
use IO::Multiplex ;
use JSON ;
2019-10-29 15:59:10 +01:00
use POSIX qw( EINTR EAGAIN ) ;
2012-10-29 12:15:43 +01:00
use Scalar::Util qw( weaken ) ;
2019-10-29 15:59:10 +01:00
use Time::HiRes qw( usleep gettimeofday tv_interval ) ;
2012-08-27 13:41:24 +02:00
2019-10-29 15:59:10 +01:00
use PVE::IPCC ;
2019-11-19 12:23:44 +01:00
use PVE::QemuServer::Helpers ;
2012-07-12 12:28:27 +02:00
# Qemu Monitor Protocol (QMP) client.
#
# This implementation uses IO::Multiplex (libio-multiplex-perl) and
2014-11-27 11:56:52 +01:00
# allows you to issue qmp and qga commands to different VMs in parallel.
2012-07-12 12:28:27 +02:00
2014-11-27 11:56:52 +01:00
# Note: qemu can onyl handle 1 connection, so we close connections asap
2012-07-12 12:28:27 +02:00
sub new {
2014-11-26 11:11:39 +01:00
my ( $ class , $ eventcb ) = @ _ ;
2012-07-12 12:28:27 +02:00
my $ mux = new IO:: Multiplex ;
my $ self = bless {
mux = > $ mux ,
2014-11-27 11:56:52 +01:00
queue_lookup = > { } , # $fh => $queue_info
queue_info = > { } ,
2012-07-12 12:28:27 +02:00
} , $ class ;
$ self - > { eventcb } = $ eventcb if $ eventcb ;
$ mux - > set_callback_object ( $ self ) ;
2012-12-06 08:39:03 +01:00
# make sure perl doesn't believe this is a circular reference as we
2012-10-29 12:15:43 +01:00
# delete mux in DESTROY
weaken ( $ mux - > { _object } ) ;
2012-07-12 12:28:27 +02:00
return $ self ;
}
2014-11-27 11:56:52 +01:00
# Note: List of special QGA command. Those commands can close the connection
# without sending a response.
my $ qga_allow_close_cmds = {
'guest-shutdown' = > 1 ,
'guest-suspend-ram' = > 1 ,
'guest-suspend-disk' = > 1 ,
'guest-suspend-hybrid' = > 1 ,
} ;
my $ push_cmd_to_queue = sub {
my ( $ self , $ vmid , $ cmd ) = @ _ ;
my $ execute = $ cmd - > { execute } || die "no command name specified" ;
my $ qga = ( $ execute =~ /^guest\-+/ ) ? 1 : 0 ;
2019-09-17 16:47:25 +02:00
2019-11-19 12:23:44 +01:00
my $ sname = PVE::QemuServer::Helpers:: qmp_socket ( $ vmid , $ qga ) ;
2014-11-27 11:56:52 +01:00
2019-09-17 16:47:25 +02:00
$ self - > { queue_info } - > { $ sname } = { qga = > $ qga , vmid = > $ vmid , sname = > $ sname , cmds = > [] }
2014-11-27 11:56:52 +01:00
if ! $ self - > { queue_info } - > { $ sname } ;
push @ { $ self - > { queue_info } - > { $ sname } - > { cmds } } , $ cmd ;
return $ self - > { queue_info } - > { $ sname } ;
} ;
2012-12-06 08:39:03 +01:00
# add a single command to the queue for later execution
2012-07-12 12:28:27 +02:00
# with queue_execute()
sub queue_cmd {
my ( $ self , $ vmid , $ callback , $ execute , % params ) = @ _ ;
my $ cmd = { } ;
$ cmd - > { execute } = $ execute ;
$ cmd - > { arguments } = \ % params ;
$ cmd - > { callback } = $ callback ;
2014-11-27 11:56:52 +01:00
& $ push_cmd_to_queue ( $ self , $ vmid , $ cmd ) ;
return undef ;
2012-07-12 12:28:27 +02:00
}
# execute a single command
sub cmd {
2012-07-13 12:36:40 +02:00
my ( $ self , $ vmid , $ cmd , $ timeout ) = @ _ ;
2012-07-12 12:28:27 +02:00
my $ result ;
my $ callback = sub {
my ( $ vmid , $ resp ) = @ _ ;
$ result = $ resp - > { 'return' } ;
2018-02-20 09:43:44 +01:00
$ result = { error = > $ resp - > { 'error' } } if ! defined ( $ result ) && $ resp - > { 'error' } ;
2012-07-12 12:28:27 +02:00
} ;
2014-11-27 11:56:52 +01:00
die "no command specified" if ! ( $ cmd && $ cmd - > { execute } ) ;
2012-07-13 12:36:40 +02:00
2012-07-12 12:28:27 +02:00
$ cmd - > { callback } = $ callback ;
$ cmd - > { arguments } = { } if ! defined ( $ cmd - > { arguments } ) ;
2014-11-27 11:56:52 +01:00
my $ queue_info = & $ push_cmd_to_queue ( $ self , $ vmid , $ cmd ) ;
2012-07-12 12:28:27 +02:00
2012-07-13 12:36:40 +02:00
if ( ! $ timeout ) {
# hack: monitor sometime blocks
if ( $ cmd - > { execute } eq 'query-migrate' ) {
$ timeout = 60 * 60 ; # 1 hour
} elsif ( $ cmd - > { execute } =~ m/^(eject|change)/ ) {
$ timeout = 60 ; # note: cdrom mount command is slow
2016-11-23 11:40:41 +01:00
} elsif ( $ cmd - > { execute } eq 'guest-fsfreeze-freeze' ) {
# freeze syncs all guest FS, if we kill it it stays in an unfreezable
# locked state with high probability, so use an generous timeout
$ timeout = 60 * 60 ; # 1 hour
} elsif ( $ cmd - > { execute } eq 'guest-fsfreeze-thaw' ) {
# thaw has no possible long blocking actions, either it returns
# instantly or never (dead locked)
2014-12-02 13:03:55 +01:00
$ timeout = 10 ;
2012-09-24 10:43:19 +02:00
} elsif ( $ cmd - > { execute } eq 'savevm-start' ||
$ cmd - > { execute } eq 'savevm-end' ||
2012-12-06 08:39:03 +01:00
$ cmd - > { execute } eq 'query-backup' ||
2013-05-06 09:25:39 +02:00
$ cmd - > { execute } eq 'query-block-jobs' ||
2017-01-03 15:03:12 +01:00
$ cmd - > { execute } eq 'block-job-cancel' ||
$ cmd - > { execute } eq 'block-job-complete' ||
2013-03-01 10:57:15 +01:00
$ cmd - > { execute } eq 'backup-cancel' ||
2012-09-24 10:43:19 +02:00
$ cmd - > { execute } eq 'query-savevm' ||
2019-09-17 16:47:25 +02:00
$ cmd - > { execute } eq 'delete-drive-snapshot' ||
2014-11-26 11:11:40 +01:00
$ cmd - > { execute } eq 'guest-shutdown' ||
2018-09-24 10:44:22 +02:00
$ cmd - > { execute } eq 'blockdev-snapshot-internal-sync' ||
$ cmd - > { execute } eq 'blockdev-snapshot-delete-internal-sync' ||
2012-09-12 13:32:12 +02:00
$ cmd - > { execute } eq 'snapshot-drive' ) {
$ timeout = 10 * 60 ; # 10 mins ?
2012-08-27 13:13:36 +02:00
} else {
$ timeout = 3 ; # default
2012-07-13 12:36:40 +02:00
}
}
2014-11-28 10:32:40 +01:00
$ self - > queue_execute ( $ timeout , 2 ) ;
2014-11-27 11:56:52 +01:00
die "VM $vmid qmp command '$cmd->{execute}' failed - $queue_info->{error}"
if defined ( $ queue_info - > { error } ) ;
2012-07-12 12:28:27 +02:00
return $ result ;
} ;
my $ cmdid_seq = 0 ;
2014-11-26 11:11:39 +01:00
my $ cmdid_seq_qga = 0 ;
2014-11-27 11:56:52 +01:00
2012-07-12 12:28:27 +02:00
my $ next_cmdid = sub {
2014-11-26 11:11:39 +01:00
my ( $ qga ) = @ _ ;
if ( $ qga ) {
$ cmdid_seq_qga + + ;
return "$$" . "0" . $ cmdid_seq_qga ;
} else {
$ cmdid_seq + + ;
return "$$:$cmdid_seq" ;
}
2012-07-12 12:28:27 +02:00
} ;
2014-11-27 11:56:52 +01:00
my $ lookup_queue_info = sub {
my ( $ self , $ fh , $ noerr ) = @ _ ;
2012-12-06 08:39:03 +01:00
2019-09-17 16:47:25 +02:00
my $ queue_info = $ self - > { queue_lookup } - > { $ fh } ;
2014-11-27 11:56:52 +01:00
if ( ! $ queue_info ) {
warn "internal error - unable to lookup queue info" if ! $ noerr ;
return undef ;
}
return $ queue_info ;
} ;
2012-12-06 08:39:03 +01:00
2014-11-27 11:56:52 +01:00
my $ close_connection = sub {
my ( $ self , $ queue_info ) = @ _ ;
2012-07-12 12:28:27 +02:00
2014-11-27 11:56:52 +01:00
if ( my $ fh = delete $ queue_info - > { fh } ) {
delete $ self - > { queue_lookup } - > { $ fh } ;
2014-11-28 10:32:40 +01:00
$ self - > { mux } - > close ( $ fh ) ;
2019-09-17 16:47:25 +02:00
}
2012-07-12 12:28:27 +02:00
} ;
my $ open_connection = sub {
2014-11-27 11:56:52 +01:00
my ( $ self , $ queue_info , $ timeout ) = @ _ ;
die "duplicate call to open" if defined ( $ queue_info - > { fh } ) ;
my $ vmid = $ queue_info - > { vmid } ;
my $ qga = $ queue_info - > { qga } ;
2012-07-12 12:28:27 +02:00
2019-11-19 12:23:44 +01:00
my $ sname = PVE::QemuServer::Helpers:: qmp_socket ( $ vmid , $ qga ) ;
2012-07-12 12:28:27 +02:00
2012-09-25 09:27:24 +02:00
$ timeout = 1 if ! $ timeout ;
2012-08-27 13:41:24 +02:00
my $ fh ;
my $ starttime = [ gettimeofday ] ;
my $ count = 0 ;
2014-11-27 11:56:52 +01:00
my $ sotype = $ qga ? 'qga' : 'qmp' ;
2012-08-27 13:41:24 +02:00
for ( ; ; ) {
$ count + + ;
$ fh = IO::Socket::UNIX - > new ( Peer = > $ sname , Blocking = > 0 , Timeout = > 1 ) ;
last if $ fh ;
if ( $! != EINTR && $! != EAGAIN ) {
2014-11-27 11:56:52 +01:00
die "unable to connect to VM $vmid $sotype socket - $!\n" ;
2012-08-27 13:41:24 +02:00
}
my $ elapsed = tv_interval ( $ starttime , [ gettimeofday ] ) ;
2012-09-25 09:27:24 +02:00
if ( $ elapsed >= $ timeout ) {
2014-11-27 11:56:52 +01:00
die "unable to connect to VM $vmid $sotype socket - timeout after $count retries\n" ;
2012-08-27 13:41:24 +02:00
}
usleep ( 100000 ) ;
}
2012-07-12 12:28:27 +02:00
2014-11-27 11:56:52 +01:00
$ queue_info - > { fh } = $ fh ;
$ self - > { queue_lookup } - > { $ fh } = $ queue_info ;
2012-07-12 12:28:27 +02:00
$ self - > { mux } - > add ( $ fh ) ;
2014-11-27 11:56:52 +01:00
$ self - > { mux } - > set_timeout ( $ fh , $ timeout ) ;
2012-12-06 08:39:03 +01:00
2012-07-12 12:28:27 +02:00
return $ fh ;
} ;
my $ check_queue = sub {
my ( $ self ) = @ _ ;
my $ running = 0 ;
2012-12-06 08:39:03 +01:00
2014-11-27 11:56:52 +01:00
foreach my $ sname ( keys % { $ self - > { queue_info } } ) {
my $ queue_info = $ self - > { queue_info } - > { $ sname } ;
my $ fh = $ queue_info - > { fh } ;
2012-07-12 12:28:27 +02:00
next if ! $ fh ;
2014-11-27 11:56:52 +01:00
my $ qga = $ queue_info - > { qga } ;
if ( $ queue_info - > { error } ) {
& $ close_connection ( $ self , $ queue_info ) ;
2012-07-12 12:28:27 +02:00
next ;
}
2014-11-27 11:56:52 +01:00
if ( $ queue_info - > { current } ) { # command running, waiting for response
2012-07-12 12:28:27 +02:00
$ running + + ;
next ;
}
2014-11-27 11:56:52 +01:00
if ( ! scalar ( @ { $ queue_info - > { cmds } } ) ) { # no more commands
& $ close_connection ( $ self , $ queue_info ) ;
2012-07-12 12:28:27 +02:00
next ;
}
eval {
2014-11-27 11:56:52 +01:00
my $ cmd = $ queue_info - > { current } = shift @ { $ queue_info - > { cmds } } ;
$ cmd - > { id } = & $ next_cmdid ( $ qga ) ;
2012-07-12 12:28:27 +02:00
2012-12-06 08:39:03 +01:00
my $ fd = - 1 ;
2012-12-06 09:01:56 +01:00
if ( $ cmd - > { execute } eq 'add-fd' || $ cmd - > { execute } eq 'getfd' ) {
2012-12-06 08:39:03 +01:00
$ fd = $ cmd - > { arguments } - > { fd } ;
delete $ cmd - > { arguments } - > { fd } ;
}
2014-11-27 11:56:52 +01:00
my $ qmpcmd ;
2013-03-17 16:09:06 +01:00
2014-11-27 11:56:52 +01:00
if ( $ qga ) {
2013-03-17 16:09:06 +01:00
2019-09-17 16:47:25 +02:00
$ qmpcmd = to_json ( { execute = > 'guest-sync-delimited' ,
2020-03-09 14:32:44 +01:00
arguments = > { id = > int ( $ cmd - > { id } ) } } ) . "\n" .
to_json ( { execute = > $ cmd - > { execute } , arguments = > $ cmd - > { arguments } } ) . "\n" ;
2013-03-17 16:09:06 +01:00
2014-11-27 11:56:52 +01:00
} else {
2013-03-17 16:09:06 +01:00
$ qmpcmd = to_json ( {
execute = > $ cmd - > { execute } ,
arguments = > $ cmd - > { arguments } ,
id = > $ cmd - > { id } } ) ;
}
2012-07-12 12:28:27 +02:00
2012-12-06 08:39:03 +01:00
if ( $ fd >= 0 ) {
my $ ret = PVE::IPCC:: sendfd ( fileno ( $ fh ) , $ fd , $ qmpcmd ) ;
die "sendfd failed" if $ ret < 0 ;
} else {
$ self - > { mux } - > write ( $ fh , $ qmpcmd ) ;
}
2012-07-12 12:28:27 +02:00
} ;
if ( my $ err = $@ ) {
2014-11-27 11:56:52 +01:00
$ queue_info - > { error } = $ err ;
2012-07-12 12:28:27 +02:00
} else {
$ running + + ;
}
}
$ self - > { mux } - > endloop ( ) if ! $ running ;
return $ running ;
} ;
# execute all queued command
2014-11-28 10:32:40 +01:00
2012-07-12 12:28:27 +02:00
sub queue_execute {
2014-11-28 10:32:40 +01:00
my ( $ self , $ timeout , $ noerr ) = @ _ ;
2012-07-12 12:28:27 +02:00
$ timeout = 3 if ! $ timeout ;
# open all necessary connections
2014-11-27 11:56:52 +01:00
foreach my $ sname ( keys % { $ self - > { queue_info } } ) {
my $ queue_info = $ self - > { queue_info } - > { $ sname } ;
next if ! scalar ( @ { $ queue_info - > { cmds } } ) ; # no commands
2019-09-17 16:47:25 +02:00
2014-11-27 11:56:52 +01:00
$ queue_info - > { error } = undef ;
$ queue_info - > { current } = undef ;
2014-11-26 11:11:39 +01:00
2019-09-17 16:47:25 +02:00
eval {
2014-11-27 11:56:52 +01:00
& $ open_connection ( $ self , $ queue_info , $ timeout ) ;
2013-03-17 16:09:05 +01:00
2014-11-27 11:56:52 +01:00
if ( ! $ queue_info - > { qga } ) {
my $ cap_cmd = { execute = > 'qmp_capabilities' , arguments = > { } } ;
unshift @ { $ queue_info - > { cmds } } , $ cap_cmd ;
2013-03-17 16:09:05 +01:00
}
2012-07-12 12:28:27 +02:00
} ;
if ( my $ err = $@ ) {
2014-11-27 11:56:52 +01:00
$ queue_info - > { error } = $ err ;
2012-07-12 12:28:27 +02:00
}
}
my $ running ;
for ( ; ; ) {
$ running = & $ check_queue ( $ self ) ;
last if ! $ running ;
$ self - > { mux } - > loop ;
}
# make sure we close everything
2014-11-28 10:32:40 +01:00
my $ errors = '' ;
2014-11-27 11:56:52 +01:00
foreach my $ sname ( keys % { $ self - > { queue_info } } ) {
2014-11-28 10:32:40 +01:00
my $ queue_info = $ self - > { queue_info } - > { $ sname } ;
& $ close_connection ( $ self , $ queue_info ) ;
if ( $ queue_info - > { error } ) {
if ( $ noerr ) {
warn $ queue_info - > { error } if $ noerr < 2 ;
} else {
$ errors . = $ queue_info - > { error }
}
}
2012-07-12 12:28:27 +02:00
}
2014-11-27 11:56:52 +01:00
$ self - > { queue_info } = $ self - > { queue_lookup } = { } ;
2014-11-28 10:32:40 +01:00
die $ errors if $ errors ;
2012-07-12 12:28:27 +02:00
}
2013-04-18 10:34:44 +02:00
sub mux_close {
my ( $ self , $ mux , $ fh ) = @ _ ;
2019-09-17 16:47:25 +02:00
my $ queue_info = & $ lookup_queue_info ( $ self , $ fh , 1 ) ;
2014-11-27 11:56:52 +01:00
return if ! $ queue_info ;
2013-04-18 10:34:44 +02:00
2019-09-17 16:47:25 +02:00
$ queue_info - > { error } = "client closed connection\n"
2014-11-27 11:56:52 +01:00
if ! $ queue_info - > { error } ;
2013-04-18 10:34:44 +02:00
}
2014-11-28 10:32:40 +01:00
# mux_input is called when input is available on one of the descriptors.
2012-07-12 12:28:27 +02:00
sub mux_input {
my ( $ self , $ mux , $ fh , $ input ) = @ _ ;
2019-09-17 16:47:25 +02:00
my $ queue_info = & $ lookup_queue_info ( $ self , $ fh ) ;
2014-11-27 11:56:52 +01:00
return if ! $ queue_info ;
2019-09-17 16:47:25 +02:00
my $ sname = $ queue_info - > { sname } ;
my $ vmid = $ queue_info - > { vmid } ;
2014-11-27 11:56:52 +01:00
my $ qga = $ queue_info - > { qga } ;
2014-11-26 11:11:39 +01:00
2014-11-27 11:56:52 +01:00
my $ curcmd = $ queue_info - > { current } ;
die "unable to lookup current command for VM $vmid ($sname)\n" if ! $ curcmd ;
2019-09-17 16:47:25 +02:00
2013-03-17 16:09:07 +01:00
my $ raw ;
2014-11-27 11:56:52 +01:00
if ( $ qga ) {
2014-12-02 13:03:55 +01:00
return if $$ input !~ s/^.*\xff([^\n]+}\r?\n[^\n]+})\r?\n(.*)$/$2/so ;
2013-03-17 16:09:07 +01:00
$ raw = $ 1 ;
2014-11-26 11:11:39 +01:00
} else {
2014-11-28 10:42:12 +01:00
return if $$ input !~ s/^(.*})\r?\n(.*)$/$2/so ;
2013-03-17 16:09:07 +01:00
$ raw = $ 1 ;
}
2012-07-12 12:28:27 +02:00
eval {
my @ jsons = split ( "\n" , $ raw ) ;
2014-11-27 11:56:52 +01:00
if ( $ qga ) {
2013-03-17 16:09:07 +01:00
die "response is not complete" if @ jsons != 2 ;
my $ obj = from_json ( $ jsons [ 0 ] ) ;
2014-11-28 10:32:40 +01:00
2014-11-27 11:56:52 +01:00
my $ cmdid = $ obj - > { 'return' } ;
2013-03-17 16:09:07 +01:00
die "received responsed without command id\n" if ! $ cmdid ;
2014-12-02 13:03:55 +01:00
# skip results fro previous commands
return if $ cmdid < $ curcmd - > { id } ;
2019-09-17 16:47:25 +02:00
2013-03-17 16:09:07 +01:00
if ( $ curcmd - > { id } ne $ cmdid ) {
die "got wrong command id '$cmdid' (expected $curcmd->{id})\n" ;
}
2014-11-28 10:32:40 +01:00
delete $ queue_info - > { current } ;
2013-03-17 16:09:07 +01:00
$ obj = from_json ( $ jsons [ 1 ] ) ;
if ( my $ callback = $ curcmd - > { callback } ) {
& $ callback ( $ vmid , $ obj ) ;
}
return ;
}
2012-07-12 12:28:27 +02:00
foreach my $ json ( @ jsons ) {
my $ obj = from_json ( $ json ) ;
next if defined ( $ obj - > { QMP } ) ; # skip monitor greeting
if ( exists ( $ obj - > { error } - > { desc } ) ) {
my $ desc = $ obj - > { error } - > { desc } ;
chomp $ desc ;
die "$desc\n" if $ desc !~ m/Connection can not be completed immediately/ ;
next ;
}
if ( defined ( $ obj - > { event } ) ) {
if ( my $ eventcb = $ self - > { eventcb } ) {
& $ eventcb ( $ obj ) ;
}
next ;
}
my $ cmdid = $ obj - > { id } ;
die "received responsed without command id\n" if ! $ cmdid ;
if ( $ curcmd - > { id } ne $ cmdid ) {
die "got wrong command id '$cmdid' (expected $curcmd->{id})\n" ;
}
2014-11-28 10:32:40 +01:00
delete $ queue_info - > { current } ;
2012-07-12 12:28:27 +02:00
if ( my $ callback = $ curcmd - > { callback } ) {
& $ callback ( $ vmid , $ obj ) ;
}
}
} ;
if ( my $ err = $@ ) {
2014-11-27 11:56:52 +01:00
$ queue_info - > { error } = $ err ;
2012-07-12 12:28:27 +02:00
}
& $ check_queue ( $ self ) ;
}
# This gets called every second to update player info, etc...
sub mux_timeout {
my ( $ self , $ mux , $ fh ) = @ _ ;
2019-09-17 16:47:25 +02:00
if ( my $ queue_info = & $ lookup_queue_info ( $ self , $ fh ) ) {
2014-11-27 11:56:52 +01:00
$ queue_info - > { error } = "got timeout\n" ;
2014-11-28 10:32:40 +01:00
$ self - > { mux } - > inbuffer ( $ fh , '' ) ; # clear to avoid warnings
2012-07-12 12:28:27 +02:00
}
& $ check_queue ( $ self ) ;
}
2014-11-26 11:11:40 +01:00
sub mux_eof {
my ( $ self , $ mux , $ fh , $ input ) = @ _ ;
2014-11-28 10:32:40 +01:00
2014-11-27 11:56:52 +01:00
my $ queue_info = & $ lookup_queue_info ( $ self , $ fh ) ;
return if ! $ queue_info ;
2014-11-26 11:11:40 +01:00
2019-09-17 16:47:25 +02:00
my $ sname = $ queue_info - > { sname } ;
my $ vmid = $ queue_info - > { vmid } ;
2014-11-27 11:56:52 +01:00
my $ qga = $ queue_info - > { qga } ;
2019-09-17 16:47:25 +02:00
2014-11-27 11:56:52 +01:00
my $ curcmd = $ queue_info - > { current } ;
die "unable to lookup current command for VM $vmid ($sname)\n" if ! $ curcmd ;
2014-11-26 11:11:40 +01:00
2014-11-27 11:56:52 +01:00
if ( $ qga && $ qga_allow_close_cmds - > { $ curcmd - > { execute } } ) {
2014-11-26 11:11:40 +01:00
2014-12-02 13:03:55 +01:00
return if $$ input !~ s/^.*\xff([^\n]+})\r?\n(.*)$/$2/so ;
2014-11-26 11:11:40 +01:00
2014-11-28 10:32:40 +01:00
my $ raw = $ 1 ;
eval {
my $ obj = from_json ( $ raw ) ;
2014-11-26 11:11:40 +01:00
2014-11-28 10:32:40 +01:00
my $ cmdid = $ obj - > { 'return' } ;
die "received responsed without command id\n" if ! $ cmdid ;
2014-11-27 11:56:52 +01:00
2014-11-28 10:32:40 +01:00
delete $ queue_info - > { current } ;
2014-11-27 11:56:52 +01:00
2014-11-28 10:32:40 +01:00
if ( my $ callback = $ curcmd - > { callback } ) {
& $ callback ( $ vmid , undef ) ;
}
} ;
if ( my $ err = $@ ) {
$ queue_info - > { error } = $ err ;
}
2014-11-26 11:11:40 +01:00
2014-11-27 11:56:52 +01:00
& $ close_connection ( $ self , $ queue_info ) ;
2014-11-28 10:32:40 +01:00
if ( scalar ( @ { $ queue_info - > { cmds } } ) && ! $ queue_info - > { error } ) {
$ queue_info - > { error } = "Got EOF but command queue is not empty.\n" ;
}
2014-11-26 11:11:40 +01:00
}
}
2019-10-30 10:28:24 +01:00
sub DESTROY {
my ( $ self ) = @ _ ;
foreach my $ sname ( keys % { $ self - > { queue_info } } ) {
my $ queue_info = $ self - > { queue_info } - > { $ sname } ;
$ close_connection - > ( $ self , $ queue_info ) ;
}
}
2012-07-13 07:06:22 +02:00
1 ;