提交 4fd1cbaf 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/armbru/tags/pull-monitor-2018-07-03-v2' into staging

Monitor patches for 2018-07-03

# gpg: Signature made Tue 03 Jul 2018 22:20:13 BST
# gpg:                using RSA key 3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867  4E5F 3870 B400 EB91 8653

* remotes/armbru/tags/pull-monitor-2018-07-03-v2: (32 commits)
  qapi: Polish command flags documentation in qapi-code-gen.txt
  monitor: Improve some comments
  qmp: Clean up capability negotiation after commit 02130314
  qobject: Let qobject_from_jsonf() fail instead of abort
  qmp: Switch timestamp_put() to qdict_from_jsonf_nofail()
  qmp: Add some comments around null responses
  qmp: Simplify monitor_qmp_respond()
  qmp: Replace get_qmp_greeting() by qmp_greeting()
  qmp: Replace monitor_json_emitter{,raw}() by qmp_{queue,send}_response()
  qmp: Use QDict * instead of QObject * for response objects
  qmp: De-duplicate error response building
  qobject: New qdict_from_jsonf_nofail()
  monitor: Peel off @mon_global wrapper
  monitor: Rename use_io_thr to use_io_thread
  qmp: Don't let JSON errors jump the queue
  qmp: Don't let malformed in-band commands jump the queue
  tests/qmp-test: Demonstrate QMP errors jumping the queue
  qmp: Simplify code around monitor_qmp_dispatch_one()
  qmp: Always free QMPRequest with qmp_request_free()
  qmp: Revert change to handle_qmp_command tracepoint
  ...
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
......@@ -624,62 +624,48 @@ its return value.
In rare cases, QAPI cannot express a type-safe representation of a
corresponding Client JSON Protocol command. You then have to suppress
generation of a marshalling function by including a key 'gen' with
boolean value false, and instead write your own function. Please try
to avoid adding new commands that rely on this, and instead use
type-safe unions. For an example of this usage:
boolean value false, and instead write your own function. For
example:
{ 'command': 'netdev_add',
'data': {'type': 'str', 'id': 'str'},
'gen': false }
Please try to avoid adding new commands that rely on this, and instead
use type-safe unions.
Normally, the QAPI schema is used to describe synchronous exchanges,
where a response is expected. But in some cases, the action of a
command is expected to change state in a way that a successful
response is not possible (although the command will still return a
normal dictionary error on failure). When a successful reply is not
possible, the command expression should include the optional key
possible, the command expression includes the optional key
'success-response' with boolean value false. So far, only QGA makes
use of this member.
A command can be declared to support Out-Of-Band (OOB) execution. By
default, commands do not support OOB. To declare a command that
supports it, the schema includes an extra 'allow-oob' field. For
example:
Key 'allow-oob' declares whether the command supports out-of-band
(OOB) execution. It defaults to false. For example:
{ 'command': 'migrate_recover',
'data': { 'uri': 'str' }, 'allow-oob': true }
To execute a command with out-of-band priority, the client specifies
the "control" field in the request, with "run-oob" set to
true. Example:
=> { "execute": "command-support-oob",
"arguments": { ... },
"control": { "run-oob": true } }
<= { "return": { } }
Without it, even the commands that support out-of-band execution will
still be run in-band.
See qmp-spec.txt for out-of-band execution syntax and semantics.
Under normal QMP command execution, the following apply to each
command:
Commands supporting out-of-band execution can still be executed
in-band.
- They are executed in order,
- They run only in main thread of QEMU,
- They run with the BQL held.
When a command is executed in-band, its handler runs in the main
thread with the BQL held.
When a command is executed with OOB, the following changes occur:
When a command is executed out-of-band, its handler runs in a
dedicated monitor I/O thread with the BQL *not* held.
- They can be completed before a pending in-band command,
- They run in a dedicated monitor thread,
- They run with the BQL not held.
An OOB-capable command handler must satisfy the following conditions:
OOB command handlers must satisfy the following conditions:
- It terminates quickly,
- It does not invoke system calls that may block,
- It terminates quickly.
- It does not invoke system calls that may block.
- It does not access guest RAM that may block when userfaultfd is
enabled for postcopy live migration,
enabled for postcopy live migration.
- It takes only "fast" locks, i.e. all critical sections protected by
any lock it takes also satisfy the conditions for OOB command
handler code.
......@@ -688,17 +674,18 @@ The restrictions on locking limit access to shared state. Such access
requires synchronization, but OOB commands can't take the BQL or any
other "slow" lock.
If in doubt, do not implement OOB execution support.
When in doubt, do not implement OOB execution support.
A command may use the optional 'allow-preconfig' key to permit its execution
at early runtime configuration stage (preconfig runstate).
If not specified then a command defaults to 'allow-preconfig': false.
Key 'allow-preconfig' declares whether the command is available before
the machine is built. It defaults to false. For example:
An example of declaring a command that is enabled during preconfig:
{ 'command': 'qmp_capabilities',
'data': { '*enable': [ 'QMPCapability' ] },
'allow-preconfig': true }
QMP is available before the machine is built only when QEMU was
started with --preconfig.
=== Events ===
Usage: { 'event': STRING, '*data': COMPLEX-TYPE-NAME-OR-DICT,
......
......@@ -52,13 +52,14 @@ Escape character is '^]'.
"QMP": {
"version": {
"qemu": {
"micro": 50,
"minor": 6,
"major": 1
},
"package": ""
},
"micro": 0,
"minor": 0,
"major": 3
},
"package": "v3.0.0"
},
"capabilities": [
"oob"
]
}
}
......
......@@ -77,52 +77,65 @@ The greeting message format is:
is the same of the query-version command)
- The "capabilities" member specify the availability of features beyond the
baseline specification; the order of elements in this array has no
particular significance, so a client must search the entire array
when looking for a particular capability
particular significance.
2.2.1 Capabilities
------------------
Currently supported capabilities are:
- "oob": the QMP server supports "Out-Of-Band" (OOB) command
execution. For more details, please see the "run-oob" parameter in
the "Issuing Commands" section below. Not all commands allow this
"oob" execution. The "query-qmp-schema" command can be used to
inspect which commands support "oob" execution.
QMP clients can get a list of supported QMP capabilities of the QMP
server in the greeting message mentioned above. By default, all the
capabilities are off. To enable any QMP capabilities, the QMP client
needs to send the "qmp_capabilities" command with an extra parameter
for the requested capabilities.
- "oob": the QMP server supports "out-of-band" (OOB) command
execution, as described in section "2.3.1 Out-of-band execution".
2.3 Issuing Commands
--------------------
The format for command execution is:
{ "execute": json-string, "arguments": json-object, "id": json-value,
"control": json-object }
{ "execute": json-string, "arguments": json-object, "id": json-value }
or
{ "exec-oob": json-string, "arguments": json-object, "id": json-value }
Where,
- The "execute" member identifies the command to be executed by the Server
- The "execute" or "exec-oob" member identifies the command to be
executed by the server. The latter requests out-of-band execution.
- The "arguments" member is used to pass any arguments required for the
execution of the command, it is optional when no arguments are
required. Each command documents what contents will be considered
valid when handling the json-argument
- The "id" member is a transaction identification associated with the
command execution. It is required for all commands if the OOB -
capability was enabled at startup, and optional otherwise. The same
"id" field will be part of the response if provided. The "id" member
can be any json-value, although most clients merely use a
json-number incremented for each successive command
- The "control" member is optional, and currently only used for
out-of-band execution. The handling or response of an "oob" command
can overtake prior in-band commands. To enable "oob" handling of a
particular command, just provide a control field with: { "control":
{ "run-oob": true } }
command execution, it is optional and will be part of the response
if provided. The "id" member can be any json-value. A json-number
incremented for each successive command works fine.
2.3.1 Out-of-band execution
---------------------------
The server normally reads, executes and responds to one command after
the other. The client therefore receives command responses in issue
order.
With out-of-band execution enabled via capability negotiation (section
4.), the server reads and queues commands as they arrive. It executes
commands from the queue one after the other. Commands executed
out-of-band jump the queue: the command get executed right away,
possibly overtaking prior in-band commands. The client may therefore
receive such a command's response before responses from prior in-band
commands.
To be able to match responses back to their commands, the client needs
to pass "id" with out-of-band commands. Passing it with all commands
is recommended for clients that accept capability "oob".
If the client sends in-band commands faster than the server can
execute them, the server will eventually drop commands to limit the
queue length. The sever sends event COMMAND_DROPPED then.
Only a few commands support out-of-band execution. The ones that do
have "allow-oob": true in output of query-qmp-schema.
2.4 Commands Responses
----------------------
......@@ -223,12 +236,13 @@ This section provides some examples of real QMP usage, in all of them
3.1 Server greeting
-------------------
S: { "QMP": { "version": { "qemu": { "micro": 50, "minor": 6, "major": 1 },
"package": ""}, "capabilities": []}}
S: { "QMP": {"version": {"qemu": {"micro": 0, "minor": 0, "major": 3},
"package": "v3.0.0"}, "capabilities": ["oob"] } }
3.2 Capabilities negotiation
----------------------------
3.2 Client QMP negotiation
--------------------------
C: { "execute": "qmp_capabilities" }
C: { "execute": "qmp_capabilities", "arguments": { "enable": ["oob"] } }
S: { "return": {}}
3.3 Simple 'stop' execution
......@@ -255,6 +269,15 @@ S: { "error": { "class": "GenericError", "desc": "Invalid JSON syntax" } }
S: { "timestamp": { "seconds": 1258551470, "microseconds": 802384 },
"event": "POWERDOWN" }
3.7 Out-of-band execution
-------------------------
C: { "exec-oob": "migrate-pause", "id": 42 }
S: { "id": 42,
"error": { "class": "GenericError",
"desc": "migrate-pause is currently only supported during postcopy-active state" } }
4. Capabilities Negotiation
===========================
......
......@@ -41,15 +41,15 @@ void qmp_register_command(QmpCommandList *cmds, const char *name,
QmpCommandFunc *fn, QmpCommandOptions options);
void qmp_unregister_command(QmpCommandList *cmds, const char *name);
QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name);
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request);
void qmp_disable_command(QmpCommandList *cmds, const char *name);
void qmp_enable_command(QmpCommandList *cmds, const char *name);
bool qmp_command_is_enabled(const QmpCommand *cmd);
const char *qmp_command_name(const QmpCommand *cmd);
bool qmp_has_success_response(const QmpCommand *cmd);
QObject *qmp_build_error_object(Error *err);
QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp);
QDict *qmp_error_response(Error *err);
QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
bool allow_oob);
bool qmp_is_oob(QDict *dict);
typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque);
......
......@@ -19,6 +19,8 @@ QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2);
QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp)
GCC_FMT_ATTR(1, 0);
QDict *qdict_from_jsonf_nofail(const char *string, ...) GCC_FMT_ATTR(1, 2);
QString *qobject_to_json(const QObject *obj);
QString *qobject_to_json_pretty(const QObject *obj);
......
此差异已折叠。
......@@ -46,7 +46,7 @@
# Enumeration of capabilities to be advertised during initial client
# connection, used for agreeing on particular QMP extension behaviors.
#
# @oob: QMP ability to support Out-Of-Band requests.
# @oob: QMP ability to support out-of-band requests.
# (Please refer to qmp-spec.txt for more information on OOB)
#
# Since: 2.12
......@@ -3454,6 +3454,9 @@
# only be dropped when the oob capability is enabled.
#
# @id: The dropped command's "id" field.
# FIXME Broken by design. Events are broadcast to all monitors. If
# another monitor's client has a command with the same ID in flight,
# the event will incorrectly claim that command was dropped.
#
# @reason: The reason why the command is dropped.
#
......@@ -3469,24 +3472,6 @@
{ 'event': 'COMMAND_DROPPED' ,
'data': { 'id': 'any', 'reason': 'CommandDropReason' } }
##
# @x-oob-test:
#
# Test OOB functionality. When sending this command with lock=true,
# it'll try to hang the dispatcher. When sending it with lock=false,
# it'll try to notify the locked thread to continue. Note: it should
# only be used by QMP test program rather than anything else.
#
# Since: 2.12
#
# Example:
#
# { "execute": "x-oob-test",
# "arguments": { "lock": true } }
##
{ 'command': 'x-oob-test', 'data' : { 'lock': 'bool' },
'allow-oob': true }
##
# @set-numa-node:
#
......
......@@ -20,13 +20,14 @@
#include "qapi/qmp/qbool.h"
#include "sysemu/sysemu.h"
QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob,
Error **errp)
{
const char *exec_key = NULL;
const QDictEntry *ent;
const char *arg_name;
const QObject *arg_obj;
bool has_exec_key = false;
QDict *dict = NULL;
QDict *dict;
dict = qobject_to(QDict, request);
if (!dict) {
......@@ -39,25 +40,23 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
arg_name = qdict_entry_key(ent);
arg_obj = qdict_entry_value(ent);
if (!strcmp(arg_name, "execute")) {
if (!strcmp(arg_name, "execute")
|| (!strcmp(arg_name, "exec-oob") && allow_oob)) {
if (qobject_type(arg_obj) != QTYPE_QSTRING) {
error_setg(errp,
"QMP input member 'execute' must be a string");
error_setg(errp, "QMP input member '%s' must be a string",
arg_name);
return NULL;
}
has_exec_key = true;
} else if (!strcmp(arg_name, "arguments")) {
if (qobject_type(arg_obj) != QTYPE_QDICT) {
error_setg(errp,
"QMP input member 'arguments' must be an object");
if (exec_key) {
error_setg(errp, "QMP input member '%s' clashes with '%s'",
arg_name, exec_key);
return NULL;
}
} else if (!strcmp(arg_name, "id")) {
continue;
} else if (!strcmp(arg_name, "control")) {
exec_key = arg_name;
} else if (!strcmp(arg_name, "arguments")) {
if (qobject_type(arg_obj) != QTYPE_QDICT) {
error_setg(errp,
"QMP input member 'control' must be a dict");
"QMP input member 'arguments' must be an object");
return NULL;
}
} else {
......@@ -67,7 +66,7 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
}
}
if (!has_exec_key) {
if (!exec_key) {
error_setg(errp, "QMP input lacks member 'execute'");
return NULL;
}
......@@ -76,20 +75,27 @@ QDict *qmp_dispatch_check_obj(const QObject *request, Error **errp)
}
static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
Error **errp)
bool allow_oob, Error **errp)
{
Error *local_err = NULL;
bool oob;
const char *command;
QDict *args, *dict;
QmpCommand *cmd;
QObject *ret = NULL;
dict = qmp_dispatch_check_obj(request, errp);
dict = qmp_dispatch_check_obj(request, allow_oob, errp);
if (!dict) {
return NULL;
}
command = qdict_get_str(dict, "execute");
command = qdict_get_try_str(dict, "execute");
oob = false;
if (!command) {
assert(allow_oob);
command = qdict_get_str(dict, "exec-oob");
oob = true;
}
cmd = qmp_find_command(cmds, command);
if (cmd == NULL) {
error_set(errp, ERROR_CLASS_COMMAND_NOT_FOUND,
......@@ -101,6 +107,11 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
command);
return NULL;
}
if (oob && !(cmd->options & QCO_ALLOW_OOB)) {
error_setg(errp, "The command %s does not support OOB",
command);
return false;
}
if (runstate_check(RUN_STATE_PRECONFIG) &&
!(cmd->options & QCO_ALLOW_PRECONFIG)) {
......@@ -122,6 +133,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
} else if (cmd->options & QCO_NO_SUCCESS_RESP) {
g_assert(!ret);
} else if (!ret) {
/* TODO turn into assertion */
ret = QOBJECT(qdict_new());
}
......@@ -130,53 +142,44 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request,
return ret;
}
QObject *qmp_build_error_object(Error *err)
QDict *qmp_error_response(Error *err)
{
return qobject_from_jsonf("{ 'class': %s, 'desc': %s }",
QapiErrorClass_str(error_get_class(err)),
error_get_pretty(err));
QDict *rsp;
rsp = qdict_from_jsonf_nofail("{ 'error': { 'class': %s, 'desc': %s } }",
QapiErrorClass_str(error_get_class(err)),
error_get_pretty(err));
error_free(err);
return rsp;
}
/*
* Detect whether a request should be run out-of-band, by quickly
* peeking at whether we have: { "control": { "run-oob": true } }. By
* default commands are run in-band.
* Does @qdict look like a command to be run out-of-band?
*/
bool qmp_is_oob(QDict *dict)
{
QBool *bool_obj;
dict = qdict_get_qdict(dict, "control");
if (!dict) {
return false;
}
bool_obj = qobject_to(QBool, qdict_get(dict, "run-oob"));
if (!bool_obj) {
return false;
}
return qbool_get_bool(bool_obj);
return qdict_haskey(dict, "exec-oob")
&& !qdict_haskey(dict, "execute");
}
QObject *qmp_dispatch(QmpCommandList *cmds, QObject *request)
QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request,
bool allow_oob)
{
Error *err = NULL;
QObject *ret;
QDict *rsp;
ret = do_qmp_dispatch(cmds, request, &err);
ret = do_qmp_dispatch(cmds, request, allow_oob, &err);
rsp = qdict_new();
if (err) {
qdict_put_obj(rsp, "error", qmp_build_error_object(err));
error_free(err);
rsp = qmp_error_response(err);
} else if (ret) {
rsp = qdict_new();
qdict_put_obj(rsp, "return", ret);
} else {
qobject_unref(rsp);
return NULL;
/* Can only happen for commands with QCO_NO_SUCCESS_RESP */
rsp = NULL;
}
return QOBJECT(rsp);
return rsp;
}
......@@ -34,15 +34,15 @@ QMPEventFuncEmit qmp_event_get_func_emit(void)
static void timestamp_put(QDict *qdict)
{
int err;
QObject *obj;
QDict *ts;
qemu_timeval tv;
err = qemu_gettimeofday(&tv);
/* Put -1 to indicate failure of getting host time */
obj = qobject_from_jsonf("{ 'seconds': %lld, 'microseconds': %lld }",
err < 0 ? -1LL : (long long)tv.tv_sec,
err < 0 ? -1LL : (long long)tv.tv_usec);
qdict_put_obj(qdict, "timestamp", obj);
ts = qdict_from_jsonf_nofail("{ 'seconds': %lld, 'microseconds': %lld }",
err < 0 ? -1LL : (long long)tv.tv_sec,
err < 0 ? -1LL : (long long)tv.tv_usec);
qdict_put(qdict, "timestamp", ts);
}
/*
......
......@@ -545,7 +545,7 @@ fail:
#endif
}
static int send_response(GAState *s, QObject *payload)
static int send_response(GAState *s, QDict *payload)
{
const char *buf;
QString *payload_qstr, *response_qstr;
......@@ -553,7 +553,7 @@ static int send_response(GAState *s, QObject *payload)
g_assert(payload && s->channel);
payload_qstr = qobject_to_json(payload);
payload_qstr = qobject_to_json(QOBJECT(payload));
if (!payload_qstr) {
return -EINVAL;
}
......@@ -581,12 +581,12 @@ static int send_response(GAState *s, QObject *payload)
static void process_command(GAState *s, QDict *req)
{
QObject *rsp = NULL;
QDict *rsp;
int ret;
g_assert(req);
g_debug("processing command");
rsp = qmp_dispatch(&ga_commands, QOBJECT(req));
rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false);
if (rsp) {
ret = send_response(s, rsp);
if (ret < 0) {
......@@ -610,15 +610,13 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
qdict = qobject_to(QDict, json_parser_parse_err(tokens, NULL, &err));
if (err || !qdict) {
qobject_unref(qdict);
qdict = qdict_new();
if (!err) {
g_warning("failed to parse event: unknown error");
error_setg(&err, QERR_JSON_PARSING);
} else {
g_warning("failed to parse event: %s", error_get_pretty(err));
}
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
error_free(err);
qdict = qmp_error_response(err);
}
/* handle host->guest commands */
......@@ -627,13 +625,11 @@ static void process_event(JSONMessageParser *parser, GQueue *tokens)
} else {
if (!qdict_haskey(qdict, "error")) {
qobject_unref(qdict);
qdict = qdict_new();
g_warning("unrecognized payload format");
error_setg(&err, QERR_UNSUPPORTED);
qdict_put_obj(qdict, "error", qmp_build_error_object(err));
error_free(err);
qdict = qmp_error_response(err);
}
ret = send_response(s, QOBJECT(qdict));
ret = send_response(s, qdict);
if (ret < 0) {
g_warning("error sending error response: %s", strerror(-ret));
}
......
......@@ -737,19 +737,3 @@ MemoryInfo *qmp_query_memory_size_summary(Error **errp)
return mem_info;
}
static QemuSemaphore x_oob_test_sem;
static void __attribute__((constructor)) x_oob_test_init(void)
{
qemu_sem_init(&x_oob_test_sem, 0);
}
void qmp_x_oob_test(bool lock, Error **errp)
{
if (lock) {
qemu_sem_wait(&x_oob_test_sem);
} else {
qemu_sem_post(&x_oob_test_sem);
}
}
......@@ -59,10 +59,6 @@ QObject *qobject_from_json(const char *string, Error **errp)
return qobject_from_jsonv(string, NULL, errp);
}
/*
* IMPORTANT: This function aborts on error, thus it must not
* be used with untrusted arguments.
*/
QObject *qobject_from_jsonf(const char *string, ...)
{
QObject *obj;
......@@ -72,7 +68,24 @@ QObject *qobject_from_jsonf(const char *string, ...)
obj = qobject_from_jsonv(string, &ap, &error_abort);
va_end(ap);
assert(obj != NULL);
return obj;
}
/*
* Parse @string as JSON object with %-escapes interpolated.
* Abort on error. Do not use with untrusted @string.
* Return the resulting QDict. It is never null.
*/
QDict *qdict_from_jsonf_nofail(const char *string, ...)
{
QDict *obj;
va_list ap;
va_start(ap, string);
obj = qobject_to(QDict, qobject_from_jsonv(string, &ap, &error_abort));
va_end(ap);
assert(obj);
return obj;
}
......
......@@ -144,7 +144,7 @@
{ 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' }
{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true }
# Smoke test on Out-Of-Band and allow-preconfig-test
# Smoke test on out-of-band and allow-preconfig-test
{ 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true }
# For testing integer range flattening in opts-visitor. The following schema
......
......@@ -135,16 +135,65 @@ static void test_qmp_protocol(void)
qtest_quit(qts);
}
/* Tests for Out-Of-Band support. */
/* Out-of-band tests */
char tmpdir[] = "/tmp/qmp-test-XXXXXX";
char *fifo_name;
static void setup_blocking_cmd(void)
{
if (!mkdtemp(tmpdir)) {
g_error("mkdtemp: %s", strerror(errno));
}
fifo_name = g_strdup_printf("%s/fifo", tmpdir);
if (mkfifo(fifo_name, 0666)) {
g_error("mkfifo: %s", strerror(errno));
}
}
static void cleanup_blocking_cmd(void)
{
unlink(fifo_name);
rmdir(tmpdir);
}
static void send_cmd_that_blocks(QTestState *s, const char *id)
{
qtest_async_qmp(s, "{ 'execute': 'blockdev-add', 'id': %s,"
" 'arguments': {"
" 'driver': 'blkdebug', 'node-name': %s,"
" 'config': %s,"
" 'image': { 'driver': 'null-co' } } }",
id, id, fifo_name);
}
static void unblock_blocked_cmd(void)
{
int fd = open(fifo_name, O_WRONLY);
g_assert(fd >= 0);
close(fd);
}
static void send_oob_cmd_that_fails(QTestState *s, const char *id)
{
qtest_async_qmp(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id);
}
static void recv_cmd_id(QTestState *s, const char *id)
{
QDict *resp = qtest_qmp_receive(s);
g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
qobject_unref(resp);
}
static void test_qmp_oob(void)
{
QTestState *qts;
QDict *resp, *q;
int acks = 0;
const QListEntry *entry;
QList *capabilities;
QString *qstr;
const char *cmd_id;
qts = qtest_init_without_qmp_handshake(true, common_args);
......@@ -179,43 +228,33 @@ static void test_qmp_oob(void)
* Try any command that does not support OOB but with OOB flag. We
* should get failure.
*/
resp = qtest_qmp(qts,
"{ 'execute': 'query-cpus',"
" 'control': { 'run-oob': true } }");
resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }");
g_assert(qdict_haskey(resp, "error"));
qobject_unref(resp);
/*
* First send the "x-oob-test" command with lock=true and
* oob=false, it should hang the dispatcher and main thread;
* later, we send another lock=false with oob=true to continue
* that thread processing. Finally we should receive replies from
* both commands.
*/
qtest_async_qmp(qts,
"{ 'execute': 'x-oob-test',"
" 'arguments': { 'lock': true }, "
" 'id': 'lock-cmd'}");
qtest_async_qmp(qts,
"{ 'execute': 'x-oob-test', "
" 'arguments': { 'lock': false }, "
" 'control': { 'run-oob': true }, "
" 'id': 'unlock-cmd' }");
/* Ignore all events. Wait for 2 acks */
while (acks < 2) {
resp = qtest_qmp_receive(qts);
cmd_id = qdict_get_str(resp, "id");
if (!g_strcmp0(cmd_id, "lock-cmd") ||
!g_strcmp0(cmd_id, "unlock-cmd")) {
acks++;
}
qobject_unref(resp);
}
/* OOB command overtakes slow in-band command */
setup_blocking_cmd();
send_cmd_that_blocks(qts, "ib-blocks-1");
qtest_async_qmp(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }");
send_oob_cmd_that_fails(qts, "oob-1");
recv_cmd_id(qts, "oob-1");
unblock_blocked_cmd();
recv_cmd_id(qts, "ib-blocks-1");
recv_cmd_id(qts, "ib-quick-1");
/* Even malformed in-band command fails in-band */
send_cmd_that_blocks(qts, "blocks-2");
qtest_async_qmp(qts, "{ 'id': 'err-2' }");
unblock_blocked_cmd();
recv_cmd_id(qts, "blocks-2");
recv_cmd_id(qts, "err-2");
cleanup_blocking_cmd();
qtest_quit(qts);
}
/* Query smoke tests */
static int query_error_class(const char *cmd)
{
static struct {
......@@ -392,6 +431,8 @@ static void add_query_tests(QmpSchema *schema)
}
}
/* Preconfig tests */
static void test_qmp_preconfig(void)
{
QDict *rsp, *ret;
......
......@@ -227,6 +227,38 @@ static void test_qga_ping(gconstpointer fix)
qobject_unref(ret);
}
static void test_qga_invalid_id(gconstpointer fix)
{
const TestFixture *fixture = fix;
QDict *ret, *error;
const char *class;
ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}");
g_assert_nonnull(ret);
error = qdict_get_qdict(ret, "error");
class = qdict_get_try_str(error, "class");
g_assert_cmpstr(class, ==, "GenericError");
qobject_unref(ret);
}
static void test_qga_invalid_oob(gconstpointer fix)
{
const TestFixture *fixture = fix;
QDict *ret, *error;
const char *class;
ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}");
g_assert_nonnull(ret);
error = qdict_get_qdict(ret, "error");
class = qdict_get_try_str(error, "class");
g_assert_cmpstr(class, ==, "GenericError");
qobject_unref(ret);
}
static void test_qga_invalid_args(gconstpointer fix)
{
const TestFixture *fixture = fix;
......@@ -982,6 +1014,8 @@ int main(int argc, char **argv)
g_test_add_data_func("/qga/file-ops", &fix, test_qga_file_ops);
g_test_add_data_func("/qga/file-write-read", &fix, test_qga_file_write_read);
g_test_add_data_func("/qga/get-time", &fix, test_qga_get_time);
g_test_add_data_func("/qga/invalid-id", &fix, test_qga_invalid_id);
g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob);
g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd);
g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args);
g_test_add_data_func("/qga/fsfreeze-status", &fix,
......
......@@ -110,13 +110,13 @@ __org_qemu_x_Union1 *qmp___org_qemu_x_command(__org_qemu_x_EnumList *a,
static void test_dispatch_cmd(void)
{
QDict *req = qdict_new();
QObject *resp;
QDict *resp;
qdict_put_str(req, "execute", "user_def_cmd");
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp != NULL);
assert(!qdict_haskey(qobject_to(QDict, resp), "error"));
assert(!qdict_haskey(resp, "error"));
qobject_unref(resp);
qobject_unref(req);
......@@ -127,13 +127,13 @@ static void test_dispatch_cmd_failure(void)
{
QDict *req = qdict_new();
QDict *args = qdict_new();
QObject *resp;
QDict *resp;
qdict_put_str(req, "execute", "user_def_cmd2");
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp != NULL);
assert(qdict_haskey(qobject_to(QDict, resp), "error"));
assert(qdict_haskey(resp, "error"));
qobject_unref(resp);
qobject_unref(req);
......@@ -145,9 +145,9 @@ static void test_dispatch_cmd_failure(void)
qdict_put_str(req, "execute", "user_def_cmd");
resp = qmp_dispatch(&qmp_commands, QOBJECT(req));
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp != NULL);
assert(qdict_haskey(qobject_to(QDict, resp), "error"));
assert(qdict_haskey(resp, "error"));
qobject_unref(resp);
qobject_unref(req);
......@@ -155,18 +155,15 @@ static void test_dispatch_cmd_failure(void)
static QObject *test_qmp_dispatch(QDict *req)
{
QObject *resp_obj;
QDict *resp;
QObject *ret;
resp_obj = qmp_dispatch(&qmp_commands, QOBJECT(req));
assert(resp_obj);
resp = qobject_to(QDict, resp_obj);
resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false);
assert(resp && !qdict_haskey(resp, "error"));
ret = qdict_get(resp, "return");
assert(ret);
qobject_ref(ret);
qobject_unref(resp_obj);
qobject_unref(resp);
return ret;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册