提交 704eb1c0 编写于 作者: P Peter Maydell

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: NPeter Maydell <peter.maydell@linaro.org>
...@@ -926,20 +926,19 @@ K: srat|SRAT ...@@ -926,20 +926,19 @@ K: srat|SRAT
T: git git://github.com/ehabkost/qemu.git numa T: git git://github.com/ehabkost/qemu.git numa
QAPI QAPI
M: Luiz Capitulino <lcapitulino@redhat.com> M: Markus Armbruster <armbru@redhat.com>
M: Michael Roth <mdroth@linux.vnet.ibm.com> M: Michael Roth <mdroth@linux.vnet.ibm.com>
S: Maintained S: Supported
F: qapi/ F: qapi/
F: tests/qapi-schema/ 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 QAPI Schema
M: Eric Blake <eblake@redhat.com> M: Eric Blake <eblake@redhat.com>
M: Luiz Capitulino <lcapitulino@redhat.com>
M: Markus Armbruster <armbru@redhat.com> M: Markus Armbruster <armbru@redhat.com>
S: Supported S: Supported
F: qapi-schema.json 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 QObject
M: Luiz Capitulino <lcapitulino@redhat.com> M: Luiz Capitulino <lcapitulino@redhat.com>
...@@ -964,13 +963,14 @@ X: qom/cpu.c ...@@ -964,13 +963,14 @@ X: qom/cpu.c
F: tests/qom-test.c F: tests/qom-test.c
QMP QMP
M: Luiz Capitulino <lcapitulino@redhat.com> M: Markus Armbruster <armbru@redhat.com>
S: Maintained S: Supported
F: qmp.c F: qmp.c
F: monitor.c F: monitor.c
F: qmp-commands.hx F: qmp-commands.hx
F: QMP/ F: docs/qmp/
T: git git://repo.or.cz/qemu/qmp-unstable.git queue/qmp F: scripts/qmp/
T: git git://repo.or.cz/qemu/armbru.git qapi-next
SLIRP SLIRP
M: Jan Kiszka <jan.kiszka@siemens.com> M: Jan Kiszka <jan.kiszka@siemens.com>
......
...@@ -523,9 +523,6 @@ static void dump_qobject(fprintf_function func_fprintf, void *f, ...@@ -523,9 +523,6 @@ static void dump_qobject(fprintf_function func_fprintf, void *f,
QDECREF(value); QDECREF(value);
break; break;
} }
case QTYPE_NONE:
break;
case QTYPE_MAX:
default: default:
abort(); abort();
} }
......
...@@ -226,7 +226,7 @@ struct Property { ...@@ -226,7 +226,7 @@ struct Property {
PropertyInfo *info; PropertyInfo *info;
int offset; int offset;
uint8_t bitnr; uint8_t bitnr;
uint8_t qtype; qtype_code qtype;
int64_t defval; int64_t defval;
int arrayoffset; int arrayoffset;
PropertyInfo *arrayinfo; PropertyInfo *arrayinfo;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Based on ideas by Avi Kivity <avi@redhat.com> * Based on ideas by Avi Kivity <avi@redhat.com>
* *
* Copyright (C) 2009 Red Hat Inc. * Copyright (C) 2009, 2015 Red Hat Inc.
* *
* Authors: * Authors:
* Luiz Capitulino <lcapitulino@redhat.com> * Luiz Capitulino <lcapitulino@redhat.com>
...@@ -36,7 +36,8 @@ ...@@ -36,7 +36,8 @@
#include <assert.h> #include <assert.h>
typedef enum { typedef enum {
QTYPE_NONE, QTYPE_NONE, /* sentinel value, no QObject has this type code */
QTYPE_QNULL,
QTYPE_QINT, QTYPE_QINT,
QTYPE_QSTRING, QTYPE_QSTRING,
QTYPE_QDICT, QTYPE_QDICT,
...@@ -110,4 +111,12 @@ static inline qtype_code qobject_type(const QObject *obj) ...@@ -110,4 +111,12 @@ static inline qtype_code qobject_type(const QObject *obj)
return obj->type->code; return obj->type->code;
} }
extern QObject qnull_;
static inline QObject *qnull(void)
{
qobject_incref(&qnull_);
return &qnull_;
}
#endif /* QOBJECT_H */ #endif /* QOBJECT_H */
...@@ -24,6 +24,8 @@ struct QJSON { ...@@ -24,6 +24,8 @@ struct QJSON {
bool omit_comma; bool omit_comma;
}; };
#define QJSON(obj) OBJECT_CHECK(QJSON, (obj), TYPE_QJSON)
static void json_emit_element(QJSON *json, const char *name) static void json_emit_element(QJSON *json, const char *name)
{ {
/* Check whether we need to print a , before an element */ /* Check whether we need to print a , before an element */
...@@ -87,7 +89,7 @@ const char *qjson_get_str(QJSON *json) ...@@ -87,7 +89,7 @@ const char *qjson_get_str(QJSON *json)
QJSON *qjson_new(void) QJSON *qjson_new(void)
{ {
QJSON *json = (QJSON *)object_new(TYPE_QJSON); QJSON *json = QJSON(object_new(TYPE_QJSON));
return json; return json;
} }
...@@ -98,8 +100,7 @@ void qjson_finish(QJSON *json) ...@@ -98,8 +100,7 @@ void qjson_finish(QJSON *json)
static void qjson_initfn(Object *obj) static void qjson_initfn(Object *obj)
{ {
QJSON *json = (QJSON *)object_dynamic_cast(obj, TYPE_QJSON); QJSON *json = QJSON(obj);
assert(json);
json->str = qstring_from_str("{ "); json->str = qstring_from_str("{ ");
json->omit_comma = true; json->omit_comma = true;
...@@ -107,9 +108,8 @@ static void qjson_initfn(Object *obj) ...@@ -107,9 +108,8 @@ static void qjson_initfn(Object *obj)
static void qjson_finalizefn(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)); qobject_decref(QOBJECT(json->str));
} }
......
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 += qjson.o json-lexer.o json-streamer.o json-parser.o
util-obj-y += qerror.o util-obj-y += qerror.o
...@@ -561,6 +561,8 @@ static QObject *parse_keyword(JSONParserContext *ctxt) ...@@ -561,6 +561,8 @@ static QObject *parse_keyword(JSONParserContext *ctxt)
ret = QOBJECT(qbool_from_int(true)); ret = QOBJECT(qbool_from_int(true));
} else if (token_is_keyword(token, "false")) { } else if (token_is_keyword(token, "false")) {
ret = QOBJECT(qbool_from_int(false)); ret = QOBJECT(qbool_from_int(false));
} else if (token_is_keyword(token, "null")) {
ret = qnull();
} else { } else {
parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token)); parse_error(ctxt, token, "invalid keyword `%s'", token_get_value(token));
goto out; goto out;
......
...@@ -127,6 +127,9 @@ static void to_json_list_iter(QObject *obj, void *opaque) ...@@ -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) static void to_json(const QObject *obj, QString *str, int pretty, int indent)
{ {
switch (qobject_type(obj)) { switch (qobject_type(obj)) {
case QTYPE_QNULL:
qstring_append(str, "null");
break;
case QTYPE_QINT: { case QTYPE_QINT: {
QInt *val = qobject_to_qint(obj); QInt *val = qobject_to_qint(obj);
char buffer[1024]; char buffer[1024];
...@@ -260,9 +263,8 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent) ...@@ -260,9 +263,8 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
} }
case QTYPE_QERROR: case QTYPE_QERROR:
/* XXX: should QError be emitted? */ /* XXX: should QError be emitted? */
case QTYPE_NONE:
break; break;
case QTYPE_MAX: default:
abort(); abort();
} }
} }
......
/*
* 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 @@ ...@@ -32,6 +32,7 @@
import qmp import qmp
import json import json
import ast
import readline import readline
import sys import sys
import pprint import pprint
...@@ -51,6 +52,19 @@ class QMPShellError(Exception): ...@@ -51,6 +52,19 @@ class QMPShellError(Exception):
class QMPShellBadPort(QMPShellError): class QMPShellBadPort(QMPShellError):
pass 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 # TODO: QMPShell's interface is a bit ugly (eg. _fill_completion() and
# _execute_cmd()). Let's design a better one. # _execute_cmd()). Let's design a better one.
class QMPShell(qmp.QEMUMonitorProtocol): class QMPShell(qmp.QEMUMonitorProtocol):
...@@ -59,6 +73,8 @@ class QMPShell(qmp.QEMUMonitorProtocol): ...@@ -59,6 +73,8 @@ class QMPShell(qmp.QEMUMonitorProtocol):
self._greeting = None self._greeting = None
self._completer = None self._completer = None
self._pp = pp self._pp = pp
self._transmode = False
self._actions = list()
def __get_address(self, arg): def __get_address(self, arg):
""" """
...@@ -88,32 +104,40 @@ class QMPShell(qmp.QEMUMonitorProtocol): ...@@ -88,32 +104,40 @@ class QMPShell(qmp.QEMUMonitorProtocol):
# clearing everything as it doesn't seem to matter # clearing everything as it doesn't seem to matter
readline.set_completer_delims('') readline.set_completer_delims('')
def __build_cmd(self, cmdline): def __parse_value(self, val):
""" try:
Build a QMP input object from a user provided command-line in the return int(val)
following format: except ValueError:
pass
< command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] if val.lower() == 'true':
""" return True
cmdargs = cmdline.split() if val.lower() == 'false':
qmpcmd = { 'execute': cmdargs[0], 'arguments': {} } return False
for arg in cmdargs[1:]: if val.startswith(('{', '[')):
opt = arg.split('=') # Try first as pure JSON:
try: try:
if(len(opt) > 2): return json.loads(val)
opt[1] = '='.join(opt[1:])
value = int(opt[1])
except ValueError: except ValueError:
if opt[1] == 'true': pass
value = True # Try once again as FuzzyJSON:
elif opt[1] == 'false': try:
value = False st = ast.parse(val, mode='eval')
elif opt[1].startswith('{'): return ast.literal_eval(FuzzyJSON().visit(st))
value = json.loads(opt[1]) except SyntaxError:
else: pass
value = opt[1] except ValueError:
optpath = opt[0].split('.') pass
parent = qmpcmd['arguments'] 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 = [] curpath = []
for p in optpath[:-1]: for p in optpath[:-1]:
curpath.append(p) curpath.append(p)
...@@ -126,10 +150,58 @@ class QMPShell(qmp.QEMUMonitorProtocol): ...@@ -126,10 +150,58 @@ class QMPShell(qmp.QEMUMonitorProtocol):
if type(parent[optpath[-1]]) is dict: if type(parent[optpath[-1]]) is dict:
raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath)) raise QMPShellError('Cannot use "%s" as both leaf and non-leaf key' % '.'.join(curpath))
else: else:
raise QMPShellError('Cannot set "%s" multiple times' % opt[0]) raise QMPShellError('Cannot set "%s" multiple times' % key)
parent[optpath[-1]] = value 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 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): def _execute_cmd(self, cmdline):
try: try:
qmpcmd = self.__build_cmd(cmdline) qmpcmd = self.__build_cmd(cmdline)
...@@ -138,15 +210,16 @@ class QMPShell(qmp.QEMUMonitorProtocol): ...@@ -138,15 +210,16 @@ class QMPShell(qmp.QEMUMonitorProtocol):
print 'command format: <command-name> ', print 'command format: <command-name> ',
print '[arg-name1=arg1] ... [arg-nameN=argN]' print '[arg-name1=arg1] ... [arg-nameN=argN]'
return True 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) resp = self.cmd_obj(qmpcmd)
if resp is None: if resp is None:
print 'Disconnected' print 'Disconnected'
return False return False
self._print(resp)
if self._pp is not None:
self._pp.pprint(resp)
else:
print resp
return True return True
def connect(self): def connect(self):
...@@ -158,6 +231,11 @@ class QMPShell(qmp.QEMUMonitorProtocol): ...@@ -158,6 +231,11 @@ class QMPShell(qmp.QEMUMonitorProtocol):
version = self._greeting['QMP']['version']['qemu'] version = self._greeting['QMP']['version']['qemu']
print 'Connected to QEMU %d.%d.%d\n' % (version['major'],version['minor'],version['micro']) 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): def read_exec_command(self, prompt):
""" """
Read and execute a command. Read and execute a command.
...@@ -177,6 +255,9 @@ class QMPShell(qmp.QEMUMonitorProtocol): ...@@ -177,6 +255,9 @@ class QMPShell(qmp.QEMUMonitorProtocol):
else: else:
return self._execute_cmd(cmdline) return self._execute_cmd(cmdline)
def set_verbosity(self, verbose):
self._verbose = verbose
class HMPShell(QMPShell): class HMPShell(QMPShell):
def __init__(self, address): def __init__(self, address):
QMPShell.__init__(self, address) QMPShell.__init__(self, address)
...@@ -254,7 +335,7 @@ def die(msg): ...@@ -254,7 +335,7 @@ def die(msg):
def fail_cmdline(option=None): def fail_cmdline(option=None):
if option: if option:
sys.stderr.write('ERROR: bad command-line option \'%s\'\n' % 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) sys.exit(1)
def main(): def main():
...@@ -262,6 +343,7 @@ def main(): ...@@ -262,6 +343,7 @@ def main():
qemu = None qemu = None
hmp = False hmp = False
pp = None pp = None
verbose = False
try: try:
for arg in sys.argv[1:]: for arg in sys.argv[1:]:
...@@ -273,6 +355,8 @@ def main(): ...@@ -273,6 +355,8 @@ def main():
if pp is not None: if pp is not None:
fail_cmdline(arg) fail_cmdline(arg)
pp = pprint.PrettyPrinter(indent=4) pp = pprint.PrettyPrinter(indent=4)
elif arg == "-v":
verbose = True
else: else:
if qemu is not None: if qemu is not None:
fail_cmdline(arg) fail_cmdline(arg)
...@@ -297,7 +381,8 @@ def main(): ...@@ -297,7 +381,8 @@ def main():
die('Could not connect to %s' % addr) die('Could not connect to %s' % addr)
qemu.show_banner() qemu.show_banner()
while qemu.read_exec_command('(QEMU) '): qemu.set_verbosity(verbose)
while qemu.read_exec_command(qemu.get_prompt()):
pass pass
qemu.close() qemu.close()
......
/* /*
* Copyright IBM, Corp. 2009 * Copyright IBM, Corp. 2009
* Copyright (c) 2013 Red Hat Inc. * Copyright (c) 2013, 2015 Red Hat Inc.
* *
* Authors: * Authors:
* Anthony Liguori <aliguori@us.ibm.com> * Anthony Liguori <aliguori@us.ibm.com>
...@@ -1005,6 +1005,7 @@ static void keyword_literal(void) ...@@ -1005,6 +1005,7 @@ static void keyword_literal(void)
{ {
QObject *obj; QObject *obj;
QBool *qbool; QBool *qbool;
QObject *null;
QString *str; QString *str;
obj = qobject_from_json("true"); obj = qobject_from_json("true");
...@@ -1041,7 +1042,7 @@ static void keyword_literal(void) ...@@ -1041,7 +1042,7 @@ static void keyword_literal(void)
g_assert(qbool_get_int(qbool) == 0); g_assert(qbool_get_int(qbool) == 0);
QDECREF(qbool); QDECREF(qbool);
obj = qobject_from_jsonf("%i", true); obj = qobject_from_jsonf("%i", true);
g_assert(obj != NULL); g_assert(obj != NULL);
g_assert(qobject_type(obj) == QTYPE_QBOOL); g_assert(qobject_type(obj) == QTYPE_QBOOL);
...@@ -1050,6 +1051,16 @@ static void keyword_literal(void) ...@@ -1050,6 +1051,16 @@ static void keyword_literal(void)
g_assert(qbool_get_int(qbool) != 0); g_assert(qbool_get_int(qbool) != 0);
QDECREF(qbool); 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; typedef struct LiteralQDictEntry LiteralQDictEntry;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册