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:
Peter Maydell 2015-05-12 09:01:51 +01:00
commit 704eb1c099
11 changed files with 191 additions and 56 deletions

View File

@ -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>

View File

@ -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();
}

View File

@ -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;

View File

@ -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
View File

@ -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));
}

View File

@ -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

View File

@ -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;

View File

@ -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
View 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,
};

View File

@ -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:
def __parse_value(self, val):
try:
return int(val)
except ValueError:
pass
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ]
"""
cmdargs = cmdline.split()
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} }
for arg in cmdargs[1:]:
opt = arg.split('=')
if val.lower() == 'true':
return True
if val.lower() == 'false':
return False
if val.startswith(('{', '[')):
# Try first as pure JSON:
try:
if(len(opt) > 2):
opt[1] = '='.join(opt[1:])
value = int(opt[1])
return json.loads(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
# 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()

View File

@ -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");
@ -1041,7 +1042,7 @@ static void keyword_literal(void)
g_assert(qbool_get_int(qbool) == 0);
QDECREF(qbool);
obj = qobject_from_jsonf("%i", true);
g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL);
@ -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;