QMP pull request
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVUKtnAAoJEDu+7JDiTtWnOAMP/27vMOKfD4Z+kIwHKRfmZjyb 4ACjEfhndM3i3oFuOz7AYoe5vuwYbIMw7H2yeXsxXf8+88PHX4yyQ/xG1KaX0Fg/ DyO2ndDL2acRfIn/eY7K+7E4HbNONFNiCsnspdFoq7ytxpVPpanc6nCQ5//YeFPo ZA/McBl8WIfhM5uTn56q14qCiGGcz0tbQ4THpSfALlBwPfxcYzpVEmO5VN9Smbef QJ4Fy9nusydia+1fsuzm3Kgm2m0+Y2+J3o/IJFE9RmQk2UK6xEe5Vjzi10biYxVW vIc2IiDt6nAj+kjsM0GPPkwAJBojbIg9m35/tvftef/5w/UWZoqovGmx5fEAF0h5 LVA3WwuadG67LHxAS2O9qaefwSU1IcZ5ti+1YAhdwwaWs3DyYzNZ5ly0l6yN6uwX Wieyme8WAZKMqwpUmxkIGlJa5x+pW1PQB3vyf9Cyjj2tWnI7HIIoncKZ4ks70YZm MxFUefUgDtztmcknm+u3t+bN/a9w45QHRAXxCNYvaGJNwwnBrM6MPMLB7DqELUSr tdfOgkcnKZsjNKLDyINDkp7Rdepz9yn1nYPRj3ImtDdq/Bceh9CCyGG3YGot2BUR VJj4U9ouyYHKCZO9gfNsvowJDHiw0swcpU0/hZhL71tbc9CSl0y3zGm5eK2BQ9Uc Xsy9M7Oo2ou0OoT/rYUw =MGRA -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/qmp-unstable/tags/for-upstream' into staging QMP pull request # gpg: Signature made Mon May 11 14:15:19 2015 BST using RSA key ID E24ED5A7 # gpg: Good signature from "Luiz Capitulino <lcapitulino@gmail.com>" * remotes/qmp-unstable/tags/for-upstream: scripts: qmp-shell: Add verbose flag scripts: qmp-shell: add transaction subshell scripts: qmp-shell: Expand support for QMP expressions scripts: qmp-shell: refactor helpers MAINTAINERS: New maintainer for QMP and QAPI json-parser: Accept 'null' in QMP qobject: Add a special null QObject qobject: Clean up around qtype_code QJSON: Use OBJECT_CHECK Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
704eb1c099
18
MAINTAINERS
18
MAINTAINERS
@ -926,20 +926,19 @@ K: srat|SRAT
|
||||
T: git git://github.com/ehabkost/qemu.git numa
|
||||
|
||||
QAPI
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
M: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||
S: Maintained
|
||||
S: Supported
|
||||
F: qapi/
|
||||
F: tests/qapi-schema/
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
QAPI Schema
|
||||
M: Eric Blake <eblake@redhat.com>
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
S: Supported
|
||||
F: qapi-schema.json
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
QObject
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
@ -964,13 +963,14 @@ X: qom/cpu.c
|
||||
F: tests/qom-test.c
|
||||
|
||||
QMP
|
||||
M: Luiz Capitulino <lcapitulino@redhat.com>
|
||||
S: Maintained
|
||||
M: Markus Armbruster <armbru@redhat.com>
|
||||
S: Supported
|
||||
F: qmp.c
|
||||
F: monitor.c
|
||||
F: qmp-commands.hx
|
||||
F: QMP/
|
||||
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp
|
||||
F: docs/qmp/
|
||||
F: scripts/qmp/
|
||||
T: git git://repo.or.cz/qemu/armbru.git qapi-next
|
||||
|
||||
SLIRP
|
||||
M: Jan Kiszka <jan.kiszka@siemens.com>
|
||||
|
@ -523,9 +523,6 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
|
||||
QDECREF(value);
|
||||
break;
|
||||
}
|
||||
case QTYPE_NONE:
|
||||
break;
|
||||
case QTYPE_MAX:
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
@ -226,7 +226,7 @@ struct Property {
|
||||
PropertyInfo *info;
|
||||
int offset;
|
||||
uint8_t bitnr;
|
||||
uint8_t qtype;
|
||||
qtype_code qtype;
|
||||
int64_t defval;
|
||||
int arrayoffset;
|
||||
PropertyInfo *arrayinfo;
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Based on ideas by Avi Kivity <avi@redhat.com>
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat Inc.
|
||||
* Copyright (C) 2009, 2015 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Luiz Capitulino <lcapitulino@redhat.com>
|
||||
@ -36,7 +36,8 @@
|
||||
#include <assert.h>
|
||||
|
||||
typedef enum {
|
||||
QTYPE_NONE,
|
||||
QTYPE_NONE, /* sentinel value, no QObject has this type code */
|
||||
QTYPE_QNULL,
|
||||
QTYPE_QINT,
|
||||
QTYPE_QSTRING,
|
||||
QTYPE_QDICT,
|
||||
@ -110,4 +111,12 @@ static inline qtype_code qobject_type(const QObject *obj)
|
||||
return obj->type->code;
|
||||
}
|
||||
|
||||
extern QObject qnull_;
|
||||
|
||||
static inline QObject *qnull(void)
|
||||
{
|
||||
qobject_incref(&qnull_);
|
||||
return &qnull_;
|
||||
}
|
||||
|
||||
#endif /* QOBJECT_H */
|
||||
|
10
qjson.c
10
qjson.c
@ -24,6 +24,8 @@ struct QJSON {
|
||||
bool omit_comma;
|
||||
};
|
||||
|
||||
#define QJSON(obj) OBJECT_CHECK(QJSON, (obj), TYPE_QJSON)
|
||||
|
||||
static void json_emit_element(QJSON *json, const char *name)
|
||||
{
|
||||
/* Check whether we need to print a , before an element */
|
||||
@ -87,7 +89,7 @@ const char *qjson_get_str(QJSON *json)
|
||||
|
||||
QJSON *qjson_new(void)
|
||||
{
|
||||
QJSON *json = (QJSON *)object_new(TYPE_QJSON);
|
||||
QJSON *json = QJSON(object_new(TYPE_QJSON));
|
||||
return json;
|
||||
}
|
||||
|
||||
@ -98,8 +100,7 @@ void qjson_finish(QJSON *json)
|
||||
|
||||
static void qjson_initfn(Object *obj)
|
||||
{
|
||||
QJSON *json = (QJSON *)object_dynamic_cast(obj, TYPE_QJSON);
|
||||
assert(json);
|
||||
QJSON *json = QJSON(obj);
|
||||
|
||||
json->str = qstring_from_str("{ ");
|
||||
json->omit_comma = true;
|
||||
@ -107,9 +108,8 @@ static void qjson_initfn(Object *obj)
|
||||
|
||||
static void qjson_finalizefn(Object *obj)
|
||||
{
|
||||
QJSON *json = (QJSON *)object_dynamic_cast(obj, TYPE_QJSON);
|
||||
QJSON *json = QJSON(obj);
|
||||
|
||||
assert(json);
|
||||
qobject_decref(QOBJECT(json->str));
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
util-obj-y = qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
|
||||
util-obj-y = qnull.o qint.o qstring.o qdict.o qlist.o qfloat.o qbool.o
|
||||
util-obj-y += qjson.o json-lexer.o json-streamer.o json-parser.o
|
||||
util-obj-y += qerror.o
|
||||
|
@ -561,6 +561,8 @@ static QObject *parse_keyword(JSONParserContext *ctxt)
|
||||
ret = QOBJECT(qbool_from_int(true));
|
||||
} else if (token_is_keyword(token, "false")) {
|
||||
ret = QOBJECT(qbool_from_int(false));
|
||||
} else if (token_is_keyword(token, "null")) {
|
||||
ret = qnull();
|
||||
} else {
|
||||
parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token));
|
||||
goto out;
|
||||
|
@ -127,6 +127,9 @@ static void to_json_list_iter(QObject *obj, void *opaque)
|
||||
static void to_json(const QObject *obj, QString *str, int pretty, int indent)
|
||||
{
|
||||
switch (qobject_type(obj)) {
|
||||
case QTYPE_QNULL:
|
||||
qstring_append(str, "null");
|
||||
break;
|
||||
case QTYPE_QINT: {
|
||||
QInt *val = qobject_to_qint(obj);
|
||||
char buffer[1024];
|
||||
@ -260,9 +263,8 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
|
||||
}
|
||||
case QTYPE_QERROR:
|
||||
/* XXX: should QError be emitted? */
|
||||
case QTYPE_NONE:
|
||||
break;
|
||||
case QTYPE_MAX:
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
29
qobject/qnull.c
Normal file
29
qobject/qnull.c
Normal file
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* QNull
|
||||
*
|
||||
* Copyright (C) 2015 Red Hat, Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Markus Armbruster <armbru@redhat.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU LGPL, version 2.1
|
||||
* or later. See the COPYING.LIB file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qapi/qmp/qobject.h"
|
||||
|
||||
static void qnull_destroy_obj(QObject *obj)
|
||||
{
|
||||
assert(0);
|
||||
}
|
||||
|
||||
static const QType qnull_type = {
|
||||
.code = QTYPE_QNULL,
|
||||
.destroy = qnull_destroy_obj,
|
||||
};
|
||||
|
||||
QObject qnull_ = {
|
||||
.type = &qnull_type,
|
||||
.refcnt = 1,
|
||||
};
|
@ -32,6 +32,7 @@
|
||||
|
||||
import qmp
|
||||
import json
|
||||
import ast
|
||||
import readline
|
||||
import sys
|
||||
import pprint
|
||||
@ -51,6 +52,19 @@ class QMPShellError(Exception):
|
||||
class QMPShellBadPort(QMPShellError):
|
||||
pass
|
||||
|
||||
class FuzzyJSON(ast.NodeTransformer):
|
||||
'''This extension of ast.NodeTransformer filters literal "true/false/null"
|
||||
values in an AST and replaces them by proper "True/False/None" values that
|
||||
Python can properly evaluate.'''
|
||||
def visit_Name(self, node):
|
||||
if node.id == 'true':
|
||||
node.id = 'True'
|
||||
if node.id == 'false':
|
||||
node.id = 'False'
|
||||
if node.id == 'null':
|
||||
node.id = 'None'
|
||||
return node
|
||||
|
||||
# TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
|
||||
# _execute_cmd()). Let's design a better one.
|
||||
class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
@ -59,6 +73,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
self._greeting = None
|
||||
self._completer = None
|
||||
self._pp = pp
|
||||
self._transmode = False
|
||||
self._actions = list()
|
||||
|
||||
def __get_address(self, arg):
|
||||
"""
|
||||
@ -88,32 +104,40 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
# clearing everything as it doesn't seem to matter
|
||||
readline.set_completer_delims('')
|
||||
|
||||
def __build_cmd(self, cmdline):
|
||||
"""
|
||||
Build a QMP input object from a user provided command-line in the
|
||||
following format:
|
||||
|
||||
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
|
||||
"""
|
||||
cmdargs = cmdline.split()
|
||||
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
|
||||
for arg in cmdargs[1:]:
|
||||
opt = arg.split('=')
|
||||
def __parse_value(self, val):
|
||||
try:
|
||||
if(len(opt) > 2):
|
||||
opt[1] = '='.join(opt[1:])
|
||||
value = int(opt[1])
|
||||
return int(val)
|
||||
except ValueError:
|
||||
if opt[1] == 'true':
|
||||
value = True
|
||||
elif opt[1] == 'false':
|
||||
value = False
|
||||
elif opt[1].startswith('{'):
|
||||
value = json.loads(opt[1])
|
||||
else:
|
||||
value = opt[1]
|
||||
optpath = opt[0].split('.')
|
||||
parent = qmpcmd['arguments']
|
||||
pass
|
||||
|
||||
if val.lower() == 'true':
|
||||
return True
|
||||
if val.lower() == 'false':
|
||||
return False
|
||||
if val.startswith(('{', '[')):
|
||||
# Try first as pure JSON:
|
||||
try:
|
||||
return json.loads(val)
|
||||
except ValueError:
|
||||
pass
|
||||
# Try once again as FuzzyJSON:
|
||||
try:
|
||||
st = ast.parse(val, mode='eval')
|
||||
return ast.literal_eval(FuzzyJSON().visit(st))
|
||||
except SyntaxError:
|
||||
pass
|
||||
except ValueError:
|
||||
pass
|
||||
return val
|
||||
|
||||
def __cli_expr(self, tokens, parent):
|
||||
for arg in tokens:
|
||||
(key, _, val) = arg.partition('=')
|
||||
if not val:
|
||||
raise QMPShellError("Expected a key=value pair, got '%s'" % arg)
|
||||
|
||||
value = self.__parse_value(val)
|
||||
optpath = key.split('.')
|
||||
curpath = []
|
||||
for p in optpath[:-1]:
|
||||
curpath.append(p)
|
||||
@ -126,10 +150,58 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
if type(parent[optpath[-1]]) is dict:
|
||||
raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath))
|
||||
else:
|
||||
raise QMPShellError('Cannot set "%s" multiple times' % opt[0])
|
||||
raise QMPShellError('Cannot set "%s" multiple times' % key)
|
||||
parent[optpath[-1]] = value
|
||||
|
||||
def __build_cmd(self, cmdline):
|
||||
"""
|
||||
Build a QMP input object from a user provided command-line in the
|
||||
following format:
|
||||
|
||||
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
|
||||
"""
|
||||
cmdargs = cmdline.split()
|
||||
|
||||
# Transactional CLI entry/exit:
|
||||
if cmdargs[0] == 'transaction(':
|
||||
self._transmode = True
|
||||
cmdargs.pop(0)
|
||||
elif cmdargs[0] == ')' and self._transmode:
|
||||
self._transmode = False
|
||||
if len(cmdargs) > 1:
|
||||
raise QMPShellError("Unexpected input after close of Transaction sub-shell")
|
||||
qmpcmd = { 'execute': 'transaction',
|
||||
'arguments': { 'actions': self._actions } }
|
||||
self._actions = list()
|
||||
return qmpcmd
|
||||
|
||||
# Nothing to process?
|
||||
if not cmdargs:
|
||||
return None
|
||||
|
||||
# Parse and then cache this Transactional Action
|
||||
if self._transmode:
|
||||
finalize = False
|
||||
action = { 'type': cmdargs[0], 'data': {} }
|
||||
if cmdargs[-1] == ')':
|
||||
cmdargs.pop(-1)
|
||||
finalize = True
|
||||
self.__cli_expr(cmdargs[1:], action['data'])
|
||||
self._actions.append(action)
|
||||
return self.__build_cmd(')') if finalize else None
|
||||
|
||||
# Standard command: parse and return it to be executed.
|
||||
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
|
||||
self.__cli_expr(cmdargs[1:], qmpcmd['arguments'])
|
||||
return qmpcmd
|
||||
|
||||
def _print(self, qmp):
|
||||
jsobj = json.dumps(qmp)
|
||||
if self._pp is not None:
|
||||
self._pp.pprint(jsobj)
|
||||
else:
|
||||
print str(jsobj)
|
||||
|
||||
def _execute_cmd(self, cmdline):
|
||||
try:
|
||||
qmpcmd = self.__build_cmd(cmdline)
|
||||
@ -138,15 +210,16 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
print 'command format: <command-name> ',
|
||||
print '[arg-name1=arg1] ... [arg-nameN=argN]'
|
||||
return True
|
||||
# For transaction mode, we may have just cached the action:
|
||||
if qmpcmd is None:
|
||||
return True
|
||||
if self._verbose:
|
||||
self._print(qmpcmd)
|
||||
resp = self.cmd_obj(qmpcmd)
|
||||
if resp is None:
|
||||
print 'Disconnected'
|
||||
return False
|
||||
|
||||
if self._pp is not None:
|
||||
self._pp.pprint(resp)
|
||||
else:
|
||||
print resp
|
||||
self._print(resp)
|
||||
return True
|
||||
|
||||
def connect(self):
|
||||
@ -158,6 +231,11 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
version = self._greeting['QMP']['version']['qemu']
|
||||
print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro'])
|
||||
|
||||
def get_prompt(self):
|
||||
if self._transmode:
|
||||
return "TRANS> "
|
||||
return "(QEMU) "
|
||||
|
||||
def read_exec_command(self, prompt):
|
||||
"""
|
||||
Read and execute a command.
|
||||
@ -177,6 +255,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
|
||||
else:
|
||||
return self._execute_cmd(cmdline)
|
||||
|
||||
def set_verbosity(self, verbose):
|
||||
self._verbose = verbose
|
||||
|
||||
class HMPShell(QMPShell):
|
||||
def __init__(self, address):
|
||||
QMPShell.__init__(self, address)
|
||||
@ -254,7 +335,7 @@ def die(msg):
|
||||
def fail_cmdline(option=None):
|
||||
if option:
|
||||
sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % option)
|
||||
sys.stderr.write('qemu-shell [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n')
|
||||
sys.stderr.write('qemu-shell [ -v ] [ -p ] [ -H ] < UNIX socket path> | < TCP address:port >\n')
|
||||
sys.exit(1)
|
||||
|
||||
def main():
|
||||
@ -262,6 +343,7 @@ def main():
|
||||
qemu = None
|
||||
hmp = False
|
||||
pp = None
|
||||
verbose = False
|
||||
|
||||
try:
|
||||
for arg in sys.argv[1:]:
|
||||
@ -273,6 +355,8 @@ def main():
|
||||
if pp is not None:
|
||||
fail_cmdline(arg)
|
||||
pp = pprint.PrettyPrinter(indent=4)
|
||||
elif arg == "-v":
|
||||
verbose = True
|
||||
else:
|
||||
if qemu is not None:
|
||||
fail_cmdline(arg)
|
||||
@ -297,7 +381,8 @@ def main():
|
||||
die('Could not connect to %s' % addr)
|
||||
|
||||
qemu.show_banner()
|
||||
while qemu.read_exec_command('(QEMU) '):
|
||||
qemu.set_verbosity(verbose)
|
||||
while qemu.read_exec_command(qemu.get_prompt()):
|
||||
pass
|
||||
qemu.close()
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright IBM, Corp. 2009
|
||||
* Copyright (c) 2013 Red Hat Inc.
|
||||
* Copyright (c) 2013, 2015 Red Hat Inc.
|
||||
*
|
||||
* Authors:
|
||||
* Anthony Liguori <aliguori@us.ibm.com>
|
||||
@ -1005,6 +1005,7 @@ static void keyword_literal(void)
|
||||
{
|
||||
QObject *obj;
|
||||
QBool *qbool;
|
||||
QObject *null;
|
||||
QString *str;
|
||||
|
||||
obj = qobject_from_json("true");
|
||||
@ -1050,6 +1051,16 @@ static void keyword_literal(void)
|
||||
g_assert(qbool_get_int(qbool) != 0);
|
||||
|
||||
QDECREF(qbool);
|
||||
|
||||
obj = qobject_from_json("null");
|
||||
g_assert(obj != NULL);
|
||||
g_assert(qobject_type(obj) == QTYPE_QNULL);
|
||||
|
||||
null = qnull();
|
||||
g_assert(null == obj);
|
||||
|
||||
qobject_decref(obj);
|
||||
qobject_decref(null);
|
||||
}
|
||||
|
||||
typedef struct LiteralQDictEntry LiteralQDictEntry;
|
||||
|
Loading…
Reference in New Issue
Block a user