2012-08-20 17:06:21 +04:00
/*
2013-01-31 05:50:09 +04:00
* Copyright ( C ) 2011 - 2013 Red Hat , Inc .
2012-08-20 17:06:21 +04:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2012-09-21 02:30:55 +04:00
* License along with this library . If not , see
2012-08-20 17:06:21 +04:00
* < http : //www.gnu.org/licenses/>.
*
*/
# include <config.h>
# include <time.h>
2016-06-06 14:46:41 +03:00
# include "testutils.h"
2018-03-22 21:05:26 +03:00
# include "testutilsqemuschema.h"
2012-08-20 17:06:21 +04:00
# include "qemumonitortestutils.h"
2012-12-13 19:49:48 +04:00
# include "virthread.h"
2018-12-13 17:53:50 +03:00
# define LIBVIRT_QEMU_PROCESSPRIV_H_ALLOW
2013-07-26 16:22:10 +04:00
# include "qemu/qemu_processpriv.h"
2012-08-20 17:06:21 +04:00
# include "qemu/qemu_monitor.h"
2013-07-25 14:08:25 +04:00
# include "qemu/qemu_agent.h"
2018-03-22 21:05:26 +03:00
# include "qemu/qemu_qapi.h"
2012-08-20 17:06:21 +04:00
# include "rpc/virnetsocket.h"
2012-12-12 22:06:53 +04:00
# include "viralloc.h"
2012-12-12 21:59:27 +04:00
# include "virlog.h"
2012-12-13 22:21:53 +04:00
# include "virerror.h"
2013-04-03 14:36:23 +04:00
# include "virstring.h"
2012-08-20 17:06:21 +04:00
# define VIR_FROM_THIS VIR_FROM_NONE
2014-02-28 16:16:17 +04:00
VIR_LOG_INIT ( " tests.qemumonitortestutils " ) ;
2012-08-20 17:06:21 +04:00
struct _qemuMonitorTestItem {
2013-07-25 13:48:11 +04:00
qemuMonitorTestResponseCallback cb ;
void * opaque ;
virFreeCallback freecb ;
2012-08-20 17:06:21 +04:00
} ;
struct _qemuMonitorTest {
virMutex lock ;
virThread thread ;
bool json ;
bool quit ;
bool running ;
2013-10-02 20:20:18 +04:00
bool started ;
2012-08-20 17:06:21 +04:00
char * incoming ;
size_t incomingLength ;
size_t incomingCapacity ;
char * outgoing ;
size_t outgoingLength ;
size_t outgoingCapacity ;
virNetSocketPtr server ;
virNetSocketPtr client ;
qemuMonitorPtr mon ;
2013-07-25 14:08:25 +04:00
qemuAgentPtr agent ;
2012-08-20 17:06:21 +04:00
2012-11-12 18:33:55 +04:00
char * tmpdir ;
2012-08-20 17:06:21 +04:00
size_t nitems ;
qemuMonitorTestItemPtr * items ;
virDomainObjPtr vm ;
2018-03-22 21:05:26 +03:00
virHashTablePtr qapischema ;
2012-08-20 17:06:21 +04:00
} ;
2013-07-25 13:48:11 +04:00
static void
qemuMonitorTestItemFree ( qemuMonitorTestItemPtr item )
{
if ( ! item )
return ;
if ( item - > freecb )
( item - > freecb ) ( item - > opaque ) ;
VIR_FREE ( item ) ;
}
2012-08-20 17:06:21 +04:00
2013-07-18 18:17:31 +04:00
2012-08-20 17:06:21 +04:00
/*
2013-04-20 00:18:14 +04:00
* Appends data for a reply to the outgoing buffer
2012-08-20 17:06:21 +04:00
*/
2013-07-25 13:48:11 +04:00
int
2016-06-07 14:46:01 +03:00
qemuMonitorTestAddResponse ( qemuMonitorTestPtr test ,
const char * response )
2012-08-20 17:06:21 +04:00
{
size_t want = strlen ( response ) + 2 ;
size_t have = test - > outgoingCapacity - test - > outgoingLength ;
2013-07-29 16:29:15 +04:00
VIR_DEBUG ( " Adding response to monitor command: '%s " , response ) ;
2012-08-20 17:06:21 +04:00
if ( have < want ) {
size_t need = want - have ;
2013-07-04 14:20:21 +04:00
if ( VIR_EXPAND_N ( test - > outgoing , test - > outgoingCapacity , need ) < 0 )
2012-08-20 17:06:21 +04:00
return - 1 ;
}
want - = 2 ;
2013-07-25 13:02:00 +04:00
memcpy ( test - > outgoing + test - > outgoingLength , response , want ) ;
memcpy ( test - > outgoing + test - > outgoingLength + want , " \r \n " , 2 ) ;
2012-08-20 17:06:21 +04:00
test - > outgoingLength + = want + 2 ;
return 0 ;
}
2016-12-01 11:22:44 +03:00
static int
qemuMonitorTestAddErrorResponse ( qemuMonitorTestPtr test ,
const char * usermsg )
2012-08-20 17:06:21 +04:00
{
2016-12-01 11:22:44 +03:00
virBuffer buf = VIR_BUFFER_INITIALIZER ;
char * escapemsg = NULL ;
char * jsonmsg = NULL ;
const char * monmsg = NULL ;
char * tmp ;
int ret = - 1 ;
if ( ! usermsg )
usermsg = " unexpected command " ;
if ( test - > json | | test - > agent ) {
virBufferEscape ( & buf , ' \\ ' , " \" " , " %s " , usermsg ) ;
if ( virBufferCheckError ( & buf ) < 0 )
goto error ;
escapemsg = virBufferContentAndReset ( & buf ) ;
/* replace newline/carriage return with space */
tmp = escapemsg ;
while ( * tmp ) {
if ( * tmp = = ' \r ' | | * tmp = = ' \n ' )
* tmp = ' ' ;
tmp + + ;
}
/* format the JSON error message */
if ( virAsprintf ( & jsonmsg , " { \" error \" : "
" { \" desc \" : \" %s \" , "
" \" class \" : \" UnexpectedCommand \" } } " ,
escapemsg ) < 0 )
goto error ;
monmsg = jsonmsg ;
2012-08-20 17:06:21 +04:00
} else {
2016-12-01 11:22:44 +03:00
monmsg = usermsg ;
2012-08-20 17:06:21 +04:00
}
2016-12-01 11:22:44 +03:00
ret = qemuMonitorTestAddResponse ( test , monmsg ) ;
error :
VIR_FREE ( escapemsg ) ;
VIR_FREE ( jsonmsg ) ;
return ret ;
}
static int
qemuMonitorTestAddUnexpectedErrorResponse ( qemuMonitorTestPtr test ,
const char * command )
{
char * msg ;
int ret ;
if ( virAsprintf ( & msg , " unexpected command: '%s' " , command ) < 0 )
return - 1 ;
ret = qemuMonitorTestAddErrorResponse ( test , msg ) ;
VIR_FREE ( msg ) ;
return ret ;
}
int
qemuMonitorTestAddInvalidCommandResponse ( qemuMonitorTestPtr test ,
const char * expectedcommand ,
const char * actualcommand )
{
char * msg ;
int ret ;
if ( virAsprintf ( & msg , " expected command '%s' got '%s' " ,
expectedcommand , actualcommand ) < 0 )
return - 1 ;
ret = qemuMonitorTestAddErrorResponse ( test , msg ) ;
VIR_FREE ( msg ) ;
return ret ;
2012-08-20 17:06:21 +04:00
}
2013-07-29 16:29:15 +04:00
int ATTRIBUTE_FMT_PRINTF ( 2 , 3 )
qemuMonitorReportError ( qemuMonitorTestPtr test , const char * errmsg , . . . )
{
va_list msgargs ;
char * msg = NULL ;
char * jsonmsg = NULL ;
int ret = - 1 ;
va_start ( msgargs , errmsg ) ;
if ( virVasprintf ( & msg , errmsg , msgargs ) < 0 )
goto cleanup ;
if ( test - > agent | | test - > json ) {
tests: monitor: json: Fix error message when returning json in json
The qemu JSON monitor test allows to test also expected command
arguments. As the error from the monitor simulator is returned as a
simulated qemu error (in JSON) all other JSON contained in the error
message needs to be escaped. This will happen if the monitor command
under test receives a JSON array as an argument.
This will improve the error message from:
libvirt: error : internal error: cannot parse json { "error": { "desc":
"Invalid value of argument 'keys' of command 'send-key': expected 'ble'
got '[{"type":"number","data":43},{"type":"number","data":26},
{"type":"number","data":46},{"type":"number","data":32}]'",
"class": "UnexpectedCommand" } }: lexical error: invalid string in json text.
To:
libvirt: QEMU Driver error : internal error: unable to execute QEMU
command 'send-key': Invalid value of argument 'keys' of command
'send-key': expected 'ble' got '[{"type":"number","data":43},
{"type":"number","data":26},{"type":"number","data":46},
{"type":"number","data":32}]'
This improvement will not have any effect on tests executing as
expected, but it will help test development.
2014-06-03 19:12:48 +04:00
char * tmp = msg ;
msg = qemuMonitorEscapeArg ( tmp ) ;
VIR_FREE ( tmp ) ;
if ( ! msg )
goto cleanup ;
2013-07-29 16:29:15 +04:00
if ( virAsprintf ( & jsonmsg , " { \" error \" : "
" { \" desc \" : \" %s \" , "
" \" class \" : \" UnexpectedCommand \" } } " ,
msg ) < 0 )
goto cleanup ;
} else {
if ( virAsprintf ( & jsonmsg , " error: '%s' " , msg ) < 0 )
goto cleanup ;
}
2016-06-07 14:46:01 +03:00
ret = qemuMonitorTestAddResponse ( test , jsonmsg ) ;
2013-07-29 16:29:15 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-29 16:29:15 +04:00
va_end ( msgargs ) ;
VIR_FREE ( msg ) ;
VIR_FREE ( jsonmsg ) ;
return ret ;
}
2013-07-18 18:17:31 +04:00
static int
2013-07-25 13:48:11 +04:00
qemuMonitorTestProcessCommand ( qemuMonitorTestPtr test ,
const char * cmdstr )
2012-08-20 17:06:21 +04:00
{
2013-07-25 13:48:11 +04:00
int ret ;
2012-08-20 17:06:21 +04:00
2013-07-29 16:29:15 +04:00
VIR_DEBUG ( " Processing string from monitor handler: '%s " , cmdstr ) ;
2013-07-25 13:48:11 +04:00
if ( test - > nitems = = 0 ) {
2016-12-01 11:22:44 +03:00
return qemuMonitorTestAddUnexpectedErrorResponse ( test , cmdstr ) ;
2012-08-20 17:06:21 +04:00
} else {
2013-07-25 13:48:11 +04:00
qemuMonitorTestItemPtr item = test - > items [ 0 ] ;
ret = ( item - > cb ) ( test , item , cmdstr ) ;
qemuMonitorTestItemFree ( item ) ;
if ( VIR_DELETE_ELEMENT ( test - > items , 0 , test - > nitems ) < 0 )
return - 1 ;
2012-08-20 17:06:21 +04:00
}
return ret ;
}
2013-07-18 18:17:31 +04:00
2012-08-20 17:06:21 +04:00
/*
* Handles read / write of monitor data on the monitor server side
*/
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestIO ( virNetSocketPtr sock ,
int events ,
void * opaque )
2012-08-20 17:06:21 +04:00
{
qemuMonitorTestPtr test = opaque ;
bool err = false ;
virMutexLock ( & test - > lock ) ;
2013-02-23 01:56:21 +04:00
if ( test - > quit ) {
virMutexUnlock ( & test - > lock ) ;
return ;
}
2012-08-20 17:06:21 +04:00
if ( events & VIR_EVENT_HANDLE_WRITABLE ) {
ssize_t ret ;
if ( ( ret = virNetSocketWrite ( sock ,
test - > outgoing ,
test - > outgoingLength ) ) < 0 ) {
err = true ;
goto cleanup ;
}
memmove ( test - > outgoing ,
test - > outgoing + ret ,
test - > outgoingLength - ret ) ;
test - > outgoingLength - = ret ;
if ( ( test - > outgoingCapacity - test - > outgoingLength ) > 1024 )
VIR_SHRINK_N ( test - > outgoing , test - > outgoingCapacity , 1024 ) ;
}
if ( events & VIR_EVENT_HANDLE_READABLE ) {
ssize_t ret , used ;
char * t1 , * t2 ;
if ( ( test - > incomingCapacity - test - > incomingLength ) < 1024 ) {
if ( VIR_EXPAND_N ( test - > incoming , test - > incomingCapacity , 1024 ) < 0 ) {
err = true ;
goto cleanup ;
}
}
if ( ( ret = virNetSocketRead ( sock ,
test - > incoming + test - > incomingLength ,
( test - > incomingCapacity - test - > incomingLength ) - 1 ) ) < 0 ) {
err = true ;
goto cleanup ;
}
test - > incomingLength + = ret ;
test - > incoming [ test - > incomingLength ] = ' \0 ' ;
/* Look to see if we've got a complete line, and
* if so , handle that command
*/
t1 = test - > incoming ;
2015-03-10 15:10:20 +03:00
while ( ( t2 = strstr ( t1 , " \n " ) ) | |
( ! test - > json & & ( t2 = strstr ( t1 , " \r " ) ) ) ) {
2012-08-20 17:06:21 +04:00
* t2 = ' \0 ' ;
if ( qemuMonitorTestProcessCommand ( test , t1 ) < 0 ) {
err = true ;
goto cleanup ;
}
2013-07-25 14:03:29 +04:00
t1 = t2 + 1 ;
2012-08-20 17:06:21 +04:00
}
used = t1 - test - > incoming ;
memmove ( test - > incoming , t1 , test - > incomingLength - used ) ;
test - > incomingLength - = used ;
if ( ( test - > incomingCapacity - test - > incomingLength ) > 1024 ) {
VIR_SHRINK_N ( test - > incoming ,
test - > incomingCapacity ,
1024 ) ;
}
}
if ( events & ( VIR_EVENT_HANDLE_HANGUP |
VIR_EVENT_HANDLE_ERROR ) )
err = true ;
2014-03-25 10:53:44 +04:00
cleanup :
2012-08-20 17:06:21 +04:00
if ( err ) {
virNetSocketRemoveIOCallback ( sock ) ;
virNetSocketClose ( sock ) ;
virObjectUnref ( test - > client ) ;
test - > client = NULL ;
} else {
events = VIR_EVENT_HANDLE_READABLE ;
if ( test - > outgoingLength )
events | = VIR_EVENT_HANDLE_WRITABLE ;
virNetSocketUpdateIOCallback ( sock , events ) ;
}
virMutexUnlock ( & test - > lock ) ;
}
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestWorker ( void * opaque )
2012-08-20 17:06:21 +04:00
{
qemuMonitorTestPtr test = opaque ;
virMutexLock ( & test - > lock ) ;
while ( ! test - > quit ) {
virMutexUnlock ( & test - > lock ) ;
if ( virEventRunDefaultImpl ( ) < 0 ) {
2013-02-04 22:31:46 +04:00
virMutexLock ( & test - > lock ) ;
2012-08-20 17:06:21 +04:00
test - > quit = true ;
break ;
}
virMutexLock ( & test - > lock ) ;
}
test - > running = false ;
virMutexUnlock ( & test - > lock ) ;
return ;
}
2013-07-18 18:17:31 +04:00
2012-11-12 14:34:41 +04:00
static void
2013-07-18 18:17:31 +04:00
qemuMonitorTestFreeTimer ( int timer ATTRIBUTE_UNUSED ,
void * opaque ATTRIBUTE_UNUSED )
2012-11-12 14:34:41 +04:00
{
/* nothing to be done here */
}
2013-07-18 18:17:31 +04:00
void
qemuMonitorTestFree ( qemuMonitorTestPtr test )
2012-08-20 17:06:21 +04:00
{
size_t i ;
2012-11-12 14:34:41 +04:00
int timer = - 1 ;
2012-08-20 17:06:21 +04:00
if ( ! test )
return ;
virMutexLock ( & test - > lock ) ;
if ( test - > running ) {
test - > quit = true ;
2012-11-12 14:34:41 +04:00
/* HACK: Add a dummy timeout to break event loop */
timer = virEventAddTimeout ( 0 , qemuMonitorTestFreeTimer , NULL , NULL ) ;
2012-08-20 17:06:21 +04:00
}
virMutexUnlock ( & test - > lock ) ;
if ( test - > client ) {
virNetSocketRemoveIOCallback ( test - > client ) ;
virNetSocketClose ( test - > client ) ;
virObjectUnref ( test - > client ) ;
}
virObjectUnref ( test - > server ) ;
if ( test - > mon ) {
2013-01-10 01:00:32 +04:00
virObjectUnlock ( test - > mon ) ;
2012-08-20 17:06:21 +04:00
qemuMonitorClose ( test - > mon ) ;
}
2013-07-25 14:08:25 +04:00
if ( test - > agent ) {
virObjectUnlock ( test - > agent ) ;
qemuAgentClose ( test - > agent ) ;
}
2012-08-20 17:06:21 +04:00
virObjectUnref ( test - > vm ) ;
2013-10-02 20:20:18 +04:00
if ( test - > started )
2013-07-22 18:57:08 +04:00
virThreadJoin ( & test - > thread ) ;
2012-08-20 17:06:21 +04:00
2012-11-12 14:34:41 +04:00
if ( timer ! = - 1 )
virEventRemoveTimeout ( timer ) ;
2013-02-04 22:31:46 +04:00
VIR_FREE ( test - > incoming ) ;
VIR_FREE ( test - > outgoing ) ;
2013-05-21 11:53:48 +04:00
for ( i = 0 ; i < test - > nitems ; i + + )
2012-08-20 17:06:21 +04:00
qemuMonitorTestItemFree ( test - > items [ i ] ) ;
VIR_FREE ( test - > items ) ;
2012-11-12 18:33:55 +04:00
if ( test - > tmpdir & & rmdir ( test - > tmpdir ) < 0 )
VIR_WARN ( " Failed to remove tempdir: %s " , strerror ( errno ) ) ;
VIR_FREE ( test - > tmpdir ) ;
2012-08-20 17:06:21 +04:00
virMutexDestroy ( & test - > lock ) ;
VIR_FREE ( test ) ;
}
int
2013-07-25 13:48:11 +04:00
qemuMonitorTestAddHandler ( qemuMonitorTestPtr test ,
qemuMonitorTestResponseCallback cb ,
void * opaque ,
virFreeCallback freecb )
2012-08-20 17:06:21 +04:00
{
qemuMonitorTestItemPtr item ;
if ( VIR_ALLOC ( item ) < 0 )
2013-07-04 14:20:21 +04:00
goto error ;
2012-08-20 17:06:21 +04:00
2013-07-25 13:48:11 +04:00
item - > cb = cb ;
item - > freecb = freecb ;
item - > opaque = opaque ;
2012-08-20 17:06:21 +04:00
virMutexLock ( & test - > lock ) ;
2013-07-18 19:09:41 +04:00
if ( VIR_APPEND_ELEMENT ( test - > items , test - > nitems , item ) < 0 ) {
2012-08-20 17:06:21 +04:00
virMutexUnlock ( & test - > lock ) ;
2013-07-04 14:20:21 +04:00
goto error ;
2012-08-20 17:06:21 +04:00
}
virMutexUnlock ( & test - > lock ) ;
return 0 ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 13:48:11 +04:00
if ( freecb )
( freecb ) ( opaque ) ;
VIR_FREE ( item ) ;
2012-08-20 17:06:21 +04:00
return - 1 ;
}
2013-07-25 13:48:11 +04:00
void *
qemuMonitorTestItemGetPrivateData ( qemuMonitorTestItemPtr item )
{
return item ? item - > opaque : NULL ;
}
2013-07-25 17:42:38 +04:00
typedef struct _qemuMonitorTestCommandArgs qemuMonitorTestCommandArgs ;
typedef qemuMonitorTestCommandArgs * qemuMonitorTestCommandArgsPtr ;
struct _qemuMonitorTestCommandArgs {
char * argname ;
char * argval ;
2013-07-25 13:48:11 +04:00
} ;
2013-07-25 17:42:38 +04:00
struct qemuMonitorTestHandlerData {
char * command_name ;
2016-11-25 12:43:23 +03:00
char * cmderr ;
2013-07-25 17:42:38 +04:00
char * response ;
size_t nargs ;
qemuMonitorTestCommandArgsPtr args ;
2016-09-27 18:11:55 +03:00
char * expectArgs ;
2013-07-25 17:42:38 +04:00
} ;
static void
qemuMonitorTestHandlerDataFree ( void * opaque )
{
struct qemuMonitorTestHandlerData * data = opaque ;
size_t i ;
if ( ! data )
return ;
for ( i = 0 ; i < data - > nargs ; i + + ) {
VIR_FREE ( data - > args [ i ] . argname ) ;
VIR_FREE ( data - > args [ i ] . argval ) ;
}
VIR_FREE ( data - > command_name ) ;
2016-11-25 12:43:23 +03:00
VIR_FREE ( data - > cmderr ) ;
2013-07-25 17:42:38 +04:00
VIR_FREE ( data - > response ) ;
VIR_FREE ( data - > args ) ;
2016-09-27 18:11:55 +03:00
VIR_FREE ( data - > expectArgs ) ;
2013-07-25 17:42:38 +04:00
VIR_FREE ( data ) ;
}
2018-03-22 21:05:26 +03:00
/* Returns -1 on error, 0 if validation was successful/not necessary, 1 if
* the validation has failed , and the reply was properly constructed */
static int
qemuMonitorTestProcessCommandDefaultValidate ( qemuMonitorTestPtr test ,
const char * cmdname ,
virJSONValuePtr args )
{
virBuffer debug = VIR_BUFFER_INITIALIZER ;
virJSONValuePtr schemaroot ;
virJSONValuePtr emptyargs = NULL ;
char * schemapath = NULL ;
int ret = - 1 ;
if ( ! test - > qapischema | | ! test - > json | | test - > agent )
return 0 ;
/* 'device_add' needs to be skipped as it does not have fully defined schema */
if ( STREQ ( cmdname , " device_add " ) )
return 0 ;
if ( virAsprintf ( & schemapath , " %s/arg-type " , cmdname ) < 0 )
goto cleanup ;
2018-07-12 14:27:40 +03:00
if ( virQEMUQAPISchemaPathGet ( schemapath , test - > qapischema , & schemaroot ) < 0 | |
! schemaroot ) {
2018-03-22 21:05:26 +03:00
if ( qemuMonitorReportError ( test ,
" command '%s' not found in QAPI schema " ,
cmdname ) = = 0 )
ret = 1 ;
goto cleanup ;
}
if ( ! args ) {
if ( ! ( emptyargs = virJSONValueNewObject ( ) ) )
goto cleanup ;
args = emptyargs ;
}
if ( testQEMUSchemaValidate ( args , schemaroot , test - > qapischema , & debug ) < 0 ) {
char * debugmsg = virBufferContentAndReset ( & debug ) ;
if ( qemuMonitorReportError ( test ,
" failed to validate arguments of '%s' "
" against QAPI schema: %s " ,
cmdname , debugmsg ) = = 0 )
ret = 1 ;
VIR_FREE ( debugmsg ) ;
goto cleanup ;
}
ret = 0 ;
cleanup :
virBufferFreeAndReset ( & debug ) ;
virJSONValueFree ( emptyargs ) ;
VIR_FREE ( schemapath ) ;
return ret ;
}
2013-07-25 13:48:11 +04:00
static int
qemuMonitorTestProcessCommandDefault ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ,
const char * cmdstr )
{
2013-07-25 17:42:38 +04:00
struct qemuMonitorTestHandlerData * data = item - > opaque ;
2013-07-25 13:48:11 +04:00
virJSONValuePtr val = NULL ;
2018-03-22 21:05:26 +03:00
virJSONValuePtr cmdargs = NULL ;
2013-07-25 13:48:11 +04:00
char * cmdcopy = NULL ;
const char * cmdname ;
char * tmp ;
int ret = - 1 ;
2018-03-22 21:05:26 +03:00
int rc ;
2013-07-25 13:48:11 +04:00
2013-07-25 14:08:25 +04:00
if ( test - > agent | | test - > json ) {
2013-07-25 13:48:11 +04:00
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing command name in %s " , cmdstr ) ;
2013-07-25 13:48:11 +04:00
goto cleanup ;
}
2018-03-22 21:05:26 +03:00
cmdargs = virJSONValueObjectGet ( val , " arguments " ) ;
2013-07-25 13:48:11 +04:00
} else {
if ( VIR_STRDUP ( cmdcopy , cmdstr ) < 0 )
return - 1 ;
cmdname = cmdcopy ;
if ( ! ( tmp = strchr ( cmdcopy , ' ' ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test ,
" Cannot find command name in '%s' " ,
cmdstr ) ;
2013-07-25 13:48:11 +04:00
goto cleanup ;
}
* tmp = ' \0 ' ;
}
2018-03-22 21:05:26 +03:00
if ( ( rc = qemuMonitorTestProcessCommandDefaultValidate ( test , cmdname , cmdargs ) ) < 0 )
goto cleanup ;
if ( rc = = 1 ) {
ret = 0 ;
goto cleanup ;
}
2013-09-19 17:19:08 +04:00
if ( data - > command_name & & STRNEQ ( data - > command_name , cmdname ) )
2016-12-01 11:22:44 +03:00
ret = qemuMonitorTestAddInvalidCommandResponse ( test , data - > command_name ,
cmdname ) ;
2013-07-25 13:48:11 +04:00
else
2016-06-07 14:46:01 +03:00
ret = qemuMonitorTestAddResponse ( test , data - > response ) ;
2013-07-25 13:48:11 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-25 13:48:11 +04:00
VIR_FREE ( cmdcopy ) ;
virJSONValueFree ( val ) ;
return ret ;
}
int
qemuMonitorTestAddItem ( qemuMonitorTestPtr test ,
const char * command_name ,
const char * response )
{
2013-07-25 17:42:38 +04:00
struct qemuMonitorTestHandlerData * data ;
2013-07-25 13:48:11 +04:00
if ( VIR_ALLOC ( data ) < 0 )
return - 1 ;
2013-07-25 17:42:38 +04:00
if ( VIR_STRDUP ( data - > command_name , command_name ) < 0 | |
VIR_STRDUP ( data - > response , response ) < 0 ) {
qemuMonitorTestHandlerDataFree ( data ) ;
return - 1 ;
}
2013-07-25 13:48:11 +04:00
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessCommandDefault ,
2013-07-25 17:42:38 +04:00
data , qemuMonitorTestHandlerDataFree ) ;
2013-07-25 13:48:11 +04:00
}
2012-08-20 17:06:21 +04:00
2016-11-25 12:43:23 +03:00
static int
qemuMonitorTestProcessCommandVerbatim ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ,
const char * cmdstr )
{
struct qemuMonitorTestHandlerData * data = item - > opaque ;
char * reformatted = NULL ;
char * errmsg = NULL ;
int ret = - 1 ;
/* JSON strings will be reformatted to simplify checking */
if ( test - > json | | test - > agent ) {
if ( ! ( reformatted = virJSONStringReformat ( cmdstr , false ) ) )
return - 1 ;
cmdstr = reformatted ;
}
if ( STREQ ( data - > command_name , cmdstr ) ) {
ret = qemuMonitorTestAddResponse ( test , data - > response ) ;
} else {
if ( data - > cmderr ) {
if ( virAsprintf ( & errmsg , " %s: %s " , data - > cmderr , cmdstr ) < 0 )
goto cleanup ;
ret = qemuMonitorTestAddErrorResponse ( test , errmsg ) ;
} else {
ret = qemuMonitorTestAddInvalidCommandResponse ( test ,
data - > command_name ,
cmdstr ) ;
}
}
cleanup :
VIR_FREE ( errmsg ) ;
VIR_FREE ( reformatted ) ;
return ret ;
}
/**
* qemuMonitorTestAddItemVerbatim :
* @ test : monitor test object
* @ command : full expected command syntax
* @ cmderr : possible explanation of expected command ( may be NULL )
* @ response : full reply of @ command
*
* Adds a test command for the simulated monitor . The full syntax is checked
* as specified in @ command . For JSON monitor tests formatting / whitespace is
* ignored . If the command on the monitor is not as expected an error containing
* @ cmderr is returned . Otherwise @ response is put as - is on the monitor .
*
* Returns 0 when command was successfully added , - 1 on error .
*/
int
qemuMonitorTestAddItemVerbatim ( qemuMonitorTestPtr test ,
const char * command ,
const char * cmderr ,
const char * response )
{
struct qemuMonitorTestHandlerData * data ;
if ( VIR_ALLOC ( data ) < 0 )
return - 1 ;
if ( VIR_STRDUP ( data - > response , response ) < 0 | |
VIR_STRDUP ( data - > cmderr , cmderr ) < 0 )
goto error ;
if ( test - > json | | test - > agent )
data - > command_name = virJSONStringReformat ( command , false ) ;
else
ignore_value ( VIR_STRDUP ( data - > command_name , command ) ) ;
if ( ! data - > command_name )
goto error ;
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessCommandVerbatim ,
data , qemuMonitorTestHandlerDataFree ) ;
error :
qemuMonitorTestHandlerDataFree ( data ) ;
return - 1 ;
}
2013-07-25 14:08:25 +04:00
static int
qemuMonitorTestProcessGuestAgentSync ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ATTRIBUTE_UNUSED ,
const char * cmdstr )
{
virJSONValuePtr val = NULL ;
virJSONValuePtr args ;
unsigned long long id ;
const char * cmdname ;
char * retmsg = NULL ;
int ret = - 1 ;
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing guest-sync command name " ) ;
2013-07-25 14:08:25 +04:00
goto cleanup ;
}
if ( STRNEQ ( cmdname , " guest-sync " ) ) {
2016-12-01 11:22:44 +03:00
ret = qemuMonitorTestAddInvalidCommandResponse ( test , " guest-sync " , cmdname ) ;
2013-07-25 14:08:25 +04:00
goto cleanup ;
}
if ( ! ( args = virJSONValueObjectGet ( val , " arguments " ) ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing arguments for guest-sync " ) ;
2013-07-25 14:08:25 +04:00
goto cleanup ;
}
if ( virJSONValueObjectGetNumberUlong ( args , " id " , & id ) ) {
2013-07-29 16:29:15 +04:00
ret = qemuMonitorReportError ( test , " Missing id for guest sync " ) ;
2013-07-25 14:08:25 +04:00
goto cleanup ;
}
if ( virAsprintf ( & retmsg , " { \" return \" :%llu} " , id ) < 0 )
goto cleanup ;
2016-06-07 14:46:01 +03:00
ret = qemuMonitorTestAddResponse ( test , retmsg ) ;
2013-07-25 14:08:25 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-25 14:08:25 +04:00
virJSONValueFree ( val ) ;
VIR_FREE ( retmsg ) ;
return ret ;
}
int
qemuMonitorTestAddAgentSyncResponse ( qemuMonitorTestPtr test )
{
if ( ! test - > agent ) {
virReportError ( VIR_ERR_INTERNAL_ERROR , " %s " ,
" This test is not an agent test " ) ;
return - 1 ;
}
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessGuestAgentSync ,
NULL , NULL ) ;
}
2013-07-25 17:42:38 +04:00
static int
qemuMonitorTestProcessCommandWithArgs ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ,
const char * cmdstr )
{
struct qemuMonitorTestHandlerData * data = item - > opaque ;
virJSONValuePtr val = NULL ;
virJSONValuePtr args ;
virJSONValuePtr argobj ;
char * argstr = NULL ;
const char * cmdname ;
size_t i ;
int ret = - 1 ;
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
ret = qemuMonitorReportError ( test , " Missing command name in %s " , cmdstr ) ;
goto cleanup ;
}
2013-09-19 17:19:08 +04:00
if ( data - > command_name & &
STRNEQ ( data - > command_name , cmdname ) ) {
2016-12-01 11:22:44 +03:00
ret = qemuMonitorTestAddInvalidCommandResponse ( test , data - > command_name ,
cmdname ) ;
2013-07-25 17:42:38 +04:00
goto cleanup ;
}
if ( ! ( args = virJSONValueObjectGet ( val , " arguments " ) ) ) {
ret = qemuMonitorReportError ( test ,
" Missing arguments section for command '%s' " ,
2013-09-19 17:19:08 +04:00
NULLSTR ( data - > command_name ) ) ;
2013-07-25 17:42:38 +04:00
goto cleanup ;
}
/* validate the args */
for ( i = 0 ; i < data - > nargs ; i + + ) {
qemuMonitorTestCommandArgsPtr arg = & data - > args [ i ] ;
if ( ! ( argobj = virJSONValueObjectGet ( args , arg - > argname ) ) ) {
ret = qemuMonitorReportError ( test ,
" Missing argument '%s' for command '%s' " ,
2013-09-19 17:19:08 +04:00
arg - > argname ,
NULLSTR ( data - > command_name ) ) ;
2013-07-25 17:42:38 +04:00
goto cleanup ;
}
/* convert the argument to string */
if ( ! ( argstr = virJSONValueToString ( argobj , false ) ) )
goto cleanup ;
/* verify that the argument value is expected */
if ( STRNEQ ( argstr , arg - > argval ) ) {
ret = qemuMonitorReportError ( test ,
" Invalid value of argument '%s' "
" of command '%s': "
" expected '%s' got '%s' " ,
2013-09-19 17:19:08 +04:00
arg - > argname ,
NULLSTR ( data - > command_name ) ,
2013-07-25 17:42:38 +04:00
arg - > argval , argstr ) ;
goto cleanup ;
}
VIR_FREE ( argstr ) ;
}
/* arguments checked out, return the response */
2016-06-07 14:46:01 +03:00
ret = qemuMonitorTestAddResponse ( test , data - > response ) ;
2013-07-25 17:42:38 +04:00
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-25 17:42:38 +04:00
VIR_FREE ( argstr ) ;
virJSONValueFree ( val ) ;
return ret ;
}
2016-09-27 18:11:55 +03:00
2013-07-25 17:42:38 +04:00
/* this allows to add a responder that is able to check
* a ( shallow ) structure of arguments for a command */
int
qemuMonitorTestAddItemParams ( qemuMonitorTestPtr test ,
const char * cmdname ,
const char * response ,
. . . )
{
struct qemuMonitorTestHandlerData * data ;
const char * argname ;
const char * argval ;
va_list args ;
va_start ( args , response ) ;
if ( VIR_ALLOC ( data ) < 0 )
2013-08-01 18:33:23 +04:00
goto error ;
2013-07-25 17:42:38 +04:00
if ( VIR_STRDUP ( data - > command_name , cmdname ) < 0 | |
VIR_STRDUP ( data - > response , response ) < 0 )
goto error ;
while ( ( argname = va_arg ( args , char * ) ) ) {
size_t i ;
if ( ! ( argval = va_arg ( args , char * ) ) ) {
virReportError ( VIR_ERR_INTERNAL_ERROR ,
" Missing argument value for argument '%s' " ,
argname ) ;
goto error ;
}
i = data - > nargs ;
if ( VIR_EXPAND_N ( data - > args , data - > nargs , 1 ) )
goto error ;
if ( VIR_STRDUP ( data - > args [ i ] . argname , argname ) < 0 | |
VIR_STRDUP ( data - > args [ i ] . argval , argval ) < 0 )
goto error ;
}
va_end ( args ) ;
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessCommandWithArgs ,
data , qemuMonitorTestHandlerDataFree ) ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 17:42:38 +04:00
va_end ( args ) ;
qemuMonitorTestHandlerDataFree ( data ) ;
return - 1 ;
}
2016-09-27 18:11:55 +03:00
static int
qemuMonitorTestProcessCommandWithArgStr ( qemuMonitorTestPtr test ,
qemuMonitorTestItemPtr item ,
const char * cmdstr )
{
struct qemuMonitorTestHandlerData * data = item - > opaque ;
virJSONValuePtr val = NULL ;
virJSONValuePtr args ;
char * argstr = NULL ;
const char * cmdname ;
int ret = - 1 ;
if ( ! ( val = virJSONValueFromString ( cmdstr ) ) )
return - 1 ;
if ( ! ( cmdname = virJSONValueObjectGetString ( val , " execute " ) ) ) {
ret = qemuMonitorReportError ( test , " Missing command name in %s " , cmdstr ) ;
goto cleanup ;
}
if ( STRNEQ ( data - > command_name , cmdname ) ) {
2016-12-01 11:22:44 +03:00
ret = qemuMonitorTestAddInvalidCommandResponse ( test , data - > command_name ,
cmdname ) ;
2016-09-27 18:11:55 +03:00
goto cleanup ;
}
if ( ! ( args = virJSONValueObjectGet ( val , " arguments " ) ) ) {
ret = qemuMonitorReportError ( test ,
" Missing arguments section for command '%s' " ,
data - > command_name ) ;
goto cleanup ;
}
/* convert the arguments to string */
if ( ! ( argstr = virJSONValueToString ( args , false ) ) )
goto cleanup ;
/* verify that the argument value is expected */
if ( STRNEQ ( argstr , data - > expectArgs ) ) {
ret = qemuMonitorReportError ( test ,
" %s: expected arguments: '%s', got: '%s' " ,
data - > command_name ,
data - > expectArgs , argstr ) ;
goto cleanup ;
}
/* arguments checked out, return the response */
ret = qemuMonitorTestAddResponse ( test , data - > response ) ;
cleanup :
VIR_FREE ( argstr ) ;
virJSONValueFree ( val ) ;
return ret ;
}
/**
* qemuMonitorTestAddItemExpect :
*
* @ test : test monitor object
* @ cmdname : command name
* @ cmdargs : expected arguments of the command
* @ apostrophe : convert apostrophes ( ' ) in @ cmdargs to quotes ( " )
* @ response : simulated response of the command
*
* Simulates a qemu monitor command . Checks that the ' arguments ' of the qmp
* command are expected . If @ apostrophe is true apostrophes are converted to
* quotes for simplification of writing the strings into code .
*/
int
qemuMonitorTestAddItemExpect ( qemuMonitorTestPtr test ,
const char * cmdname ,
const char * cmdargs ,
bool apostrophe ,
const char * response )
{
struct qemuMonitorTestHandlerData * data ;
if ( VIR_ALLOC ( data ) < 0 )
goto error ;
if ( VIR_STRDUP ( data - > command_name , cmdname ) < 0 | |
VIR_STRDUP ( data - > response , response ) < 0 | |
VIR_STRDUP ( data - > expectArgs , cmdargs ) < 0 )
goto error ;
if ( apostrophe ) {
char * tmp = data - > expectArgs ;
while ( * tmp ! = ' \0 ' ) {
if ( * tmp = = ' \' ' )
* tmp = ' " ' ;
tmp + + ;
}
}
return qemuMonitorTestAddHandler ( test ,
qemuMonitorTestProcessCommandWithArgStr ,
data , qemuMonitorTestHandlerDataFree ) ;
error :
qemuMonitorTestHandlerDataFree ( data ) ;
return - 1 ;
}
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestEOFNotify ( qemuMonitorPtr mon ATTRIBUTE_UNUSED ,
2013-07-25 21:26:15 +04:00
virDomainObjPtr vm ATTRIBUTE_UNUSED ,
void * opaque ATTRIBUTE_UNUSED )
2012-08-20 17:06:21 +04:00
{
}
2013-07-18 18:17:31 +04:00
static void
qemuMonitorTestErrorNotify ( qemuMonitorPtr mon ATTRIBUTE_UNUSED ,
2013-07-25 21:26:15 +04:00
virDomainObjPtr vm ATTRIBUTE_UNUSED ,
void * opaque ATTRIBUTE_UNUSED )
2012-08-20 17:06:21 +04:00
{
}
2013-07-25 13:48:11 +04:00
static qemuMonitorCallbacks qemuMonitorTestCallbacks = {
2012-08-20 17:06:21 +04:00
. eofNotify = qemuMonitorTestEOFNotify ,
. errorNotify = qemuMonitorTestErrorNotify ,
2013-07-26 16:22:10 +04:00
. domainDeviceDeleted = qemuProcessHandleDeviceDeleted ,
2012-08-20 17:06:21 +04:00
} ;
2013-07-18 18:17:31 +04:00
2013-07-25 14:08:25 +04:00
static void
qemuMonitorTestAgentNotify ( qemuAgentPtr agent ATTRIBUTE_UNUSED ,
virDomainObjPtr vm ATTRIBUTE_UNUSED )
{
}
static qemuAgentCallbacks qemuMonitorTestAgentCallbacks = {
. eofNotify = qemuMonitorTestAgentNotify ,
. errorNotify = qemuMonitorTestAgentNotify ,
} ;
2013-07-22 18:59:22 +04:00
static qemuMonitorTestPtr
qemuMonitorCommonTestNew ( virDomainXMLOptionPtr xmlopt ,
2013-07-25 21:17:44 +04:00
virDomainObjPtr vm ,
2013-07-22 18:59:22 +04:00
virDomainChrSourceDefPtr src )
2012-08-20 17:06:21 +04:00
{
2012-10-29 12:28:15 +04:00
qemuMonitorTestPtr test = NULL ;
2012-11-12 18:33:55 +04:00
char * path = NULL ;
char * tmpdir_template = NULL ;
2012-08-20 17:06:21 +04:00
2012-11-12 18:33:55 +04:00
if ( VIR_ALLOC ( test ) < 0 )
2013-07-04 14:20:21 +04:00
goto error ;
2012-08-20 17:06:21 +04:00
if ( virMutexInit ( & test - > lock ) < 0 ) {
virReportError ( VIR_ERR_INTERNAL_ERROR , " %s " ,
" Cannot initialize mutex " ) ;
VIR_FREE ( test ) ;
return NULL ;
}
2013-05-03 16:52:21 +04:00
if ( VIR_STRDUP ( tmpdir_template , " /tmp/libvirt_XXXXXX " ) < 0 )
goto error ;
2012-11-12 18:33:55 +04:00
if ( ! ( test - > tmpdir = mkdtemp ( tmpdir_template ) ) ) {
virReportSystemError ( errno , " %s " ,
" Failed to create temporary directory " ) ;
goto error ;
}
tmpdir_template = NULL ;
if ( virAsprintf ( & path , " %s/qemumonitorjsontest.sock " , test - > tmpdir ) < 0 )
2013-07-04 14:20:21 +04:00
goto error ;
2012-11-12 18:33:55 +04:00
2013-07-25 21:17:44 +04:00
if ( vm ) {
2017-04-03 11:24:39 +03:00
test - > vm = virObjectRef ( vm ) ;
2013-07-25 21:17:44 +04:00
} else {
test - > vm = virDomainObjNew ( xmlopt ) ;
if ( ! test - > vm )
goto error ;
}
2012-08-20 17:06:21 +04:00
2013-10-09 15:13:45 +04:00
if ( virNetSocketNewListenUNIX ( path , 0700 , geteuid ( ) , getegid ( ) ,
2012-08-20 17:06:21 +04:00
& test - > server ) < 0 )
goto error ;
2013-07-22 18:59:22 +04:00
memset ( src , 0 , sizeof ( * src ) ) ;
src - > type = VIR_DOMAIN_CHR_TYPE_UNIX ;
src - > data . nix . path = ( char * ) path ;
src - > data . nix . listen = false ;
2013-09-25 18:28:55 +04:00
path = NULL ;
2012-08-20 17:06:21 +04:00
if ( virNetSocketListen ( test - > server , 1 ) < 0 )
goto error ;
2014-03-25 10:53:44 +04:00
cleanup :
2013-07-22 18:59:22 +04:00
return test ;
2014-03-25 10:53:44 +04:00
error :
2013-09-25 18:28:55 +04:00
VIR_FREE ( path ) ;
2013-07-22 18:59:22 +04:00
VIR_FREE ( tmpdir_template ) ;
qemuMonitorTestFree ( test ) ;
test = NULL ;
goto cleanup ;
}
static int
qemuMonitorCommonTestInit ( qemuMonitorTestPtr test )
{
2013-07-25 13:48:11 +04:00
int events = VIR_EVENT_HANDLE_READABLE ;
2013-07-22 18:59:22 +04:00
if ( ! test )
return - 1 ;
2012-08-20 17:06:21 +04:00
if ( virNetSocketAccept ( test - > server , & test - > client ) < 0 )
goto error ;
2013-07-22 18:59:22 +04:00
if ( ! test - > client )
2012-09-06 19:14:25 +04:00
goto error ;
2013-07-25 13:48:11 +04:00
if ( test - > outgoingLength > 0 )
events = VIR_EVENT_HANDLE_WRITABLE ;
2012-08-20 17:06:21 +04:00
if ( virNetSocketAddIOCallback ( test - > client ,
2013-07-25 13:48:11 +04:00
events ,
2012-08-20 17:06:21 +04:00
qemuMonitorTestIO ,
test ,
NULL ) < 0 )
goto error ;
virMutexLock ( & test - > lock ) ;
if ( virThreadCreate ( & test - > thread ,
true ,
qemuMonitorTestWorker ,
test ) < 0 ) {
virMutexUnlock ( & test - > lock ) ;
goto error ;
}
2013-10-02 20:20:18 +04:00
test - > started = test - > running = true ;
2012-08-20 17:06:21 +04:00
virMutexUnlock ( & test - > lock ) ;
2013-07-22 18:59:22 +04:00
return 0 ;
2014-03-25 10:53:44 +04:00
error :
2013-07-22 18:59:22 +04:00
return - 1 ;
}
# define QEMU_JSON_GREETING "{\"QMP\":"\
" { \" version \" : " \
" { \" qemu \" : " \
" { \" micro \" : 1, " \
" \" minor \" : 0, " \
" \" major \" : 1 " \
" }, " \
" \" package \" : \" (qemu-kvm-1.0.1) " \
" \" }, " \
" \" capabilities \" : [] " \
" } " \
" } "
/* We skip the normal handshake reply of "{\"execute\":\"qmp_capabilities\"}" */
# define QEMU_TEXT_GREETING "QEMU 1.0,1 monitor - type 'help' for more information"
qemuMonitorTestPtr
2013-07-25 21:17:44 +04:00
qemuMonitorTestNew ( bool json ,
virDomainXMLOptionPtr xmlopt ,
2013-07-25 21:28:51 +04:00
virDomainObjPtr vm ,
2013-09-19 15:56:30 +04:00
virQEMUDriverPtr driver ,
2018-03-22 21:05:26 +03:00
const char * greeting ,
virHashTablePtr schema )
2013-07-22 18:59:22 +04:00
{
qemuMonitorTestPtr test = NULL ;
virDomainChrSourceDef src ;
2013-09-25 18:30:27 +04:00
memset ( & src , 0 , sizeof ( src ) ) ;
2013-07-25 21:17:44 +04:00
if ( ! ( test = qemuMonitorCommonTestNew ( xmlopt , vm , & src ) ) )
2013-07-22 18:59:22 +04:00
goto error ;
test - > json = json ;
2018-03-22 21:05:26 +03:00
test - > qapischema = schema ;
2013-07-22 18:59:22 +04:00
if ( ! ( test - > mon = qemuMonitorOpen ( test - > vm ,
& src ,
json ,
2018-03-14 20:27:49 +03:00
true ,
2017-03-11 09:23:42 +03:00
0 ,
2013-07-25 21:26:15 +04:00
& qemuMonitorTestCallbacks ,
2013-07-25 21:28:51 +04:00
driver ) ) )
2013-07-22 18:59:22 +04:00
goto error ;
virObjectLock ( test - > mon ) ;
2013-09-19 15:56:30 +04:00
if ( ! greeting )
greeting = json ? QEMU_JSON_GREETING : QEMU_TEXT_GREETING ;
2016-06-07 14:46:01 +03:00
if ( qemuMonitorTestAddResponse ( test , greeting ) < 0 )
2013-07-22 18:59:22 +04:00
goto error ;
if ( qemuMonitorCommonTestInit ( test ) < 0 )
goto error ;
virDomainChrSourceDefClear ( & src ) ;
2012-08-20 17:06:21 +04:00
return test ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 14:08:25 +04:00
virDomainChrSourceDefClear ( & src ) ;
qemuMonitorTestFree ( test ) ;
return NULL ;
}
2016-06-06 14:46:41 +03:00
2016-11-30 11:58:12 +03:00
/**
* qemuMonitorTestNewFromFile :
* @ fileName : File name to load monitor replies from
* @ xmlopt : XML parser configuration object
* @ simple : see below
*
* Create a JSON test monitor simulator object and fill it with replies
* specified in @ fileName . The file contains JSON reply objects separated by
* empty lines . If @ simple is true a generic QMP greeting is automatically
* added as the first reply , otherwise the first entry in the file is used .
*
* Returns the monitor object on success ; NULL on error .
*/
2016-06-06 14:46:41 +03:00
qemuMonitorTestPtr
qemuMonitorTestNewFromFile ( const char * fileName ,
2016-06-06 17:55:05 +03:00
virDomainXMLOptionPtr xmlopt ,
bool simple )
2016-06-06 14:46:41 +03:00
{
qemuMonitorTestPtr test = NULL ;
char * json = NULL ;
char * tmp ;
char * singleReply ;
if ( virTestLoadFile ( fileName , & json ) < 0 )
goto cleanup ;
2016-06-06 17:55:05 +03:00
if ( simple & & ! ( test = qemuMonitorTestNewSimple ( true , xmlopt ) ) )
goto cleanup ;
2016-06-06 14:46:41 +03:00
/* Our JSON parser expects replies to be separated by a newline character.
* Hence we must preprocess the file a bit . */
tmp = singleReply = json ;
while ( ( tmp = strchr ( tmp , ' \n ' ) ) ) {
/* It is safe to touch tmp[1] since all strings ends with '\0'. */
bool eof = ! tmp [ 1 ] ;
if ( * ( tmp + 1 ) ! = ' \n ' ) {
* tmp = ' ' ;
tmp + + ;
} else {
/* Cut off a single reply. */
* ( tmp + 1 ) = ' \0 ' ;
if ( test ) {
if ( qemuMonitorTestAddItem ( test , NULL , singleReply ) < 0 )
goto error ;
} else {
/* Create new mocked monitor with our greeting */
2018-03-22 21:05:26 +03:00
if ( ! ( test = qemuMonitorTestNew ( true , xmlopt , NULL , NULL ,
singleReply , NULL ) ) )
2016-06-06 14:46:41 +03:00
goto error ;
}
if ( ! eof ) {
/* Move the @tmp and @singleReply. */
tmp + = 2 ;
singleReply = tmp ;
}
}
if ( eof )
break ;
}
if ( test & & qemuMonitorTestAddItem ( test , NULL , singleReply ) < 0 )
goto error ;
cleanup :
VIR_FREE ( json ) ;
return test ;
error :
qemuMonitorTestFree ( test ) ;
test = NULL ;
goto cleanup ;
}
2016-11-30 16:43:50 +03:00
static int
qemuMonitorTestFullAddItem ( qemuMonitorTestPtr test ,
const char * filename ,
const char * command ,
const char * response ,
size_t line )
{
char * cmderr ;
int ret ;
if ( virAsprintf ( & cmderr , " wrong expected command in %s:%zu: " ,
filename , line ) < 0 )
return - 1 ;
ret = qemuMonitorTestAddItemVerbatim ( test , command , cmderr , response ) ;
VIR_FREE ( cmderr ) ;
return ret ;
}
/**
* qemuMonitorTestNewFromFileFull :
* @ fileName : File name to load monitor replies from
* @ driver : qemu driver object
* @ vm : domain object ( may be null if it ' s not needed by the test )
*
* Create a JSON test monitor simulator object and fill it with expected command
* sequence and replies specified in @ fileName .
*
* The file contains a sequence of JSON commands and reply objects separated by
* empty lines . A command is followed by a reply . The QMP greeting is added
* automatically .
*
* Returns the monitor object on success ; NULL on error .
*/
qemuMonitorTestPtr
qemuMonitorTestNewFromFileFull ( const char * fileName ,
virQEMUDriverPtr driver ,
virDomainObjPtr vm )
{
qemuMonitorTestPtr ret = NULL ;
char * jsonstr = NULL ;
char * tmp ;
size_t line = 0 ;
char * command = NULL ;
char * response = NULL ;
size_t commandln = 0 ;
if ( virTestLoadFile ( fileName , & jsonstr ) < 0 )
return NULL ;
2018-03-22 21:05:26 +03:00
if ( ! ( ret = qemuMonitorTestNew ( true , driver - > xmlopt , vm , driver , NULL , NULL ) ) )
2016-11-30 16:43:50 +03:00
goto cleanup ;
tmp = jsonstr ;
command = tmp ;
while ( ( tmp = strchr ( tmp , ' \n ' ) ) ) {
line + + ;
2018-05-03 12:38:23 +03:00
/* eof */
if ( ! tmp [ 1 ] )
break ;
2018-05-03 12:43:34 +03:00
/* concatenate block which was broken up for readability */
2016-11-30 16:43:50 +03:00
if ( * ( tmp + 1 ) ! = ' \n ' ) {
* tmp = ' ' ;
tmp + + ;
2018-05-03 12:43:34 +03:00
continue ;
}
2016-11-30 16:43:50 +03:00
2018-05-03 12:43:34 +03:00
/* Cut off a single reply. */
* ( tmp + 1 ) = ' \0 ' ;
2016-11-30 16:43:50 +03:00
2018-05-03 12:43:34 +03:00
if ( response ) {
if ( qemuMonitorTestFullAddItem ( ret , fileName , command ,
response , commandln ) < 0 )
goto error ;
command = NULL ;
response = NULL ;
}
2016-11-30 16:43:50 +03:00
2018-05-03 12:43:34 +03:00
/* Move the @tmp and @singleReply. */
tmp + = 2 ;
if ( ! command ) {
commandln = line ;
command = tmp ;
} else {
response = tmp ;
2016-11-30 16:43:50 +03:00
}
}
if ( command ) {
if ( ! response ) {
virReportError ( VIR_ERR_INTERNAL_ERROR , " missing response for command "
" on line '%zu' in '%s' " , commandln , fileName ) ;
goto error ;
}
if ( qemuMonitorTestFullAddItem ( ret , fileName , command ,
response , commandln ) < 0 )
goto error ;
}
cleanup :
VIR_FREE ( jsonstr ) ;
return ret ;
error :
qemuMonitorTestFree ( ret ) ;
ret = NULL ;
goto cleanup ;
}
2013-07-25 14:08:25 +04:00
qemuMonitorTestPtr
qemuMonitorTestNewAgent ( virDomainXMLOptionPtr xmlopt )
{
qemuMonitorTestPtr test = NULL ;
virDomainChrSourceDef src ;
2013-09-25 18:30:27 +04:00
memset ( & src , 0 , sizeof ( src ) ) ;
2013-07-25 21:17:44 +04:00
if ( ! ( test = qemuMonitorCommonTestNew ( xmlopt , NULL , & src ) ) )
2013-07-25 14:08:25 +04:00
goto error ;
if ( ! ( test - > agent = qemuAgentOpen ( test - > vm ,
& src ,
& qemuMonitorTestAgentCallbacks ) ) )
goto error ;
virObjectLock ( test - > agent ) ;
if ( qemuMonitorCommonTestInit ( test ) < 0 )
goto error ;
virDomainChrSourceDefClear ( & src ) ;
return test ;
2014-03-25 10:53:44 +04:00
error :
2013-07-25 14:08:25 +04:00
virDomainChrSourceDefClear ( & src ) ;
2012-08-20 17:06:21 +04:00
qemuMonitorTestFree ( test ) ;
2013-07-22 18:59:22 +04:00
return NULL ;
2012-08-20 17:06:21 +04:00
}
2013-07-18 18:17:31 +04:00
qemuMonitorPtr
qemuMonitorTestGetMonitor ( qemuMonitorTestPtr test )
2012-08-20 17:06:21 +04:00
{
return test - > mon ;
}
2013-07-25 14:08:25 +04:00
qemuAgentPtr
qemuMonitorTestGetAgent ( qemuMonitorTestPtr test )
{
return test - > agent ;
}
2018-04-04 17:45:04 +03:00
virDomainObjPtr
qemuMonitorTestGetDomainObj ( qemuMonitorTestPtr test )
{
return test - > vm ;
}