2017-03-03 20:32:23 +08:00
|
|
|
/*
|
|
|
|
* QMP protocol test cases
|
|
|
|
*
|
2018-08-24 00:39:33 +08:00
|
|
|
* Copyright (c) 2017-2018 Red Hat Inc.
|
2017-03-03 20:32:23 +08:00
|
|
|
*
|
|
|
|
* Authors:
|
2018-08-24 00:39:32 +08:00
|
|
|
* Markus Armbruster <armbru@redhat.com>
|
2017-03-03 20:32:23 +08:00
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
|
|
|
* See the COPYING file in the top-level directory.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "qemu/osdep.h"
|
|
|
|
#include "libqtest.h"
|
|
|
|
#include "qapi/error.h"
|
2018-02-27 07:13:27 +08:00
|
|
|
#include "qapi/qapi-visit-misc.h"
|
2018-02-01 19:18:39 +08:00
|
|
|
#include "qapi/qmp/qdict.h"
|
2018-02-01 19:18:38 +08:00
|
|
|
#include "qapi/qmp/qlist.h"
|
2017-03-03 20:32:23 +08:00
|
|
|
#include "qapi/qobject-input-visitor.h"
|
qmp: introduce QMPCapability
There were no QMP capabilities defined. Define the first capability,
"oob", to allow out-of-band messages.
After this patch, we will allow QMP clients to enable QMP capabilities
when sending the first "qmp_capabilities" command. Originally we are
starting QMP session with no arguments like:
{ "execute": "qmp_capabilities" }
Now we can enable some QMP capabilities using (take OOB as example,
which is the only capability that we support):
{ "execute": "qmp_capabilities",
"arguments": { "enable": [ "oob" ] } }
When the "arguments" key is not provided, no capability is enabled.
For capability "oob", the monitor needs to be run on a dedicated IO
thread, otherwise the command will fail. For example, trying to enable
OOB on a MUXed typed QMP monitor will fail.
One thing to mention is that QMP capabilities are per-monitor, and also
when the connection is closed due to some reason, the capabilities will
be reset.
Also, touch up qmp-test.c to test the new bits.
Signed-off-by: Peter Xu <peterx@redhat.com>
Message-Id: <20180309090006.10018-11-peterx@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[eblake: touch up commit message]
Signed-off-by: Eric Blake <eblake@redhat.com>
2018-03-09 16:59:53 +08:00
|
|
|
#include "qapi/qmp/qstring.h"
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
const char common_args[] = "-nodefaults -machine none";
|
|
|
|
|
|
|
|
static void test_version(QObject *version)
|
|
|
|
{
|
|
|
|
Visitor *v;
|
|
|
|
VersionInfo *vinfo;
|
|
|
|
|
|
|
|
g_assert(version);
|
2017-03-03 20:32:39 +08:00
|
|
|
v = qobject_input_visitor_new(version);
|
2017-03-03 20:32:23 +08:00
|
|
|
visit_type_VersionInfo(v, "version", &vinfo, &error_abort);
|
|
|
|
qapi_free_VersionInfo(vinfo);
|
|
|
|
visit_free(v);
|
|
|
|
}
|
|
|
|
|
2018-08-30 23:58:07 +08:00
|
|
|
static void assert_recovered(QTestState *qts)
|
2018-08-24 00:39:33 +08:00
|
|
|
{
|
|
|
|
QDict *resp;
|
|
|
|
|
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd' }");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "CommandNotFound");
|
2018-08-24 00:39:33 +08:00
|
|
|
}
|
|
|
|
|
2017-09-12 01:20:06 +08:00
|
|
|
static void test_malformed(QTestState *qts)
|
2017-03-03 20:32:23 +08:00
|
|
|
{
|
|
|
|
QDict *resp;
|
|
|
|
|
2018-08-24 00:39:33 +08:00
|
|
|
/* syntax error */
|
|
|
|
qtest_qmp_send_raw(qts, "{]\n");
|
|
|
|
resp = qtest_qmp_receive(qts);
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
|
|
|
assert_recovered(qts);
|
2018-08-24 00:39:33 +08:00
|
|
|
|
|
|
|
/* lexical error: impossible byte outside string */
|
|
|
|
qtest_qmp_send_raw(qts, "{\xFF");
|
|
|
|
resp = qtest_qmp_receive(qts);
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
|
|
|
assert_recovered(qts);
|
2018-08-24 00:39:33 +08:00
|
|
|
|
2018-08-24 00:39:42 +08:00
|
|
|
/* lexical error: funny control character outside string */
|
|
|
|
qtest_qmp_send_raw(qts, "{\x01");
|
|
|
|
resp = qtest_qmp_receive(qts);
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
|
|
|
assert_recovered(qts);
|
2018-08-24 00:39:42 +08:00
|
|
|
|
2018-08-24 00:39:33 +08:00
|
|
|
/* lexical error: impossible byte in string */
|
|
|
|
qtest_qmp_send_raw(qts, "{'bad \xFF");
|
|
|
|
resp = qtest_qmp_receive(qts);
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
|
|
|
assert_recovered(qts);
|
2018-08-24 00:39:33 +08:00
|
|
|
|
2018-08-24 00:39:42 +08:00
|
|
|
/* lexical error: control character in string */
|
2018-08-24 00:39:45 +08:00
|
|
|
qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n");
|
2018-08-24 00:39:42 +08:00
|
|
|
resp = qtest_qmp_receive(qts);
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
|
|
|
assert_recovered(qts);
|
2018-08-24 00:39:42 +08:00
|
|
|
|
2018-08-24 00:39:33 +08:00
|
|
|
/* lexical error: interpolation */
|
json: Nicer recovery from lexical errors
When the lexer chokes on an input character, it consumes the
character, emits a JSON error token, and enters its start state. This
can lead to suboptimal error recovery. For instance, input
0123 ,
produces the tokens
JSON_ERROR 01
JSON_INTEGER 23
JSON_COMMA ,
Make the lexer skip characters after a lexical error until a
structural character ('[', ']', '{', '}', ':', ','), an ASCII control
character, or '\xFE', or '\xFF'.
Note that we must not skip ASCII control characters, '\xFE', '\xFF',
because those are documented to force the JSON parser into known-good
state, by docs/interop/qmp-spec.txt.
The lexer now produces
JSON_ERROR 01
JSON_COMMA ,
Update qmp-test for the nicer error recovery: QMP now reports just one
error for input %p instead of two. Also drop the newline after %p; it
was needed to tease out the second error.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180831075841.13363-5-armbru@redhat.com>
[Conflict with commit ebb4d82d888 resolved]
2018-08-31 15:58:39 +08:00
|
|
|
qtest_qmp_send_raw(qts, "%%p");
|
2018-08-24 00:39:33 +08:00
|
|
|
resp = qtest_qmp_receive(qts);
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
|
|
|
assert_recovered(qts);
|
2018-08-24 00:39:33 +08:00
|
|
|
|
2017-03-03 20:32:23 +08:00
|
|
|
/* Not even a dictionary */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "null");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* No "execute" key */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{}");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* "execute" isn't a string */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': true }");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* "arguments" isn't a dictionary */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* extra key */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
2017-03-03 20:32:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void test_qmp_protocol(void)
|
|
|
|
{
|
|
|
|
QDict *resp, *q, *ret;
|
|
|
|
QList *capabilities;
|
2017-09-12 01:20:06 +08:00
|
|
|
QTestState *qts;
|
2017-03-03 20:32:23 +08:00
|
|
|
|
2018-10-09 14:27:16 +08:00
|
|
|
qts = qtest_init_without_qmp_handshake(common_args);
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test greeting */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp_receive(qts);
|
2017-03-03 20:32:23 +08:00
|
|
|
q = qdict_get_qdict(resp, "QMP");
|
|
|
|
g_assert(q);
|
|
|
|
test_version(qdict_get(q, "version"));
|
|
|
|
capabilities = qdict_get_qlist(q, "capabilities");
|
2018-10-09 14:27:15 +08:00
|
|
|
g_assert(capabilities);
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test valid command before handshake */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'query-version' }");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "CommandNotFound");
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test malformed commands before handshake */
|
2017-09-12 01:20:06 +08:00
|
|
|
test_malformed(qts);
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test handshake */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }");
|
2017-03-03 20:32:23 +08:00
|
|
|
ret = qdict_get_qdict(resp, "return");
|
|
|
|
g_assert(ret && !qdict_size(ret));
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test repeated handshake */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }");
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "CommandNotFound");
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test valid command */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'query-version' }");
|
2017-03-03 20:32:23 +08:00
|
|
|
test_version(qdict_get(resp, "return"));
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test malformed commands */
|
2017-09-12 01:20:06 +08:00
|
|
|
test_malformed(qts);
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test 'id' */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }");
|
2017-03-03 20:32:23 +08:00
|
|
|
ret = qdict_get_qdict(resp, "return");
|
|
|
|
g_assert(ret);
|
|
|
|
g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1");
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2017-03-03 20:32:23 +08:00
|
|
|
|
|
|
|
/* Test command failure with 'id' */
|
2017-09-12 01:20:06 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }");
|
2017-03-03 20:32:23 +08:00
|
|
|
g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2);
|
2018-08-30 23:58:07 +08:00
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
2017-03-03 20:32:23 +08:00
|
|
|
|
2017-09-12 01:20:06 +08:00
|
|
|
qtest_quit(qts);
|
2017-03-03 20:32:23 +08:00
|
|
|
}
|
|
|
|
|
qmp: Get rid of x-oob-test command
tests/qmp-test tests an out-of-band command overtaking a slow in-band
command. To do that, it needs:
1. An in-band command that *reliably* takes long enough to be
overtaken.
2. An out-of-band command to do the overtaking.
3. To avoid delays, a way to make the in-band command complete quickly
after it was overtaken.
To satisfy these needs, commit 469638f9cb3 provides the rather
peculiar oob-capable QMP command x-oob-test:
* With "lock": true, it waits for a global semaphore.
* With "lock": false, it signals the global semaphore.
To satisfy 1., the test runs x-oob-test in-band with "lock": true.
To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false.
Note that waiting for a semaphore violates the rules for oob-capable
commands. Running x-oob-test with "lock": true hangs the monitor
until you run x-oob-test with "lock": false on another monitor (which
you might not have set up).
Having an externally visible QMP command that may hang the monitor is
not nice. Let's apply a little more ingenuity to the problem. Idea:
have an existing command block on reading a FIFO special file, unblock
it by opening the FIFO for writing.
For 1., use
{"execute": "blockdev-add", "id": ID1,
"arguments": {
"driver": "blkdebug", "node-name": ID1, "config": FIFO,
"image": { "driver": "null-co"}}}
where ID1 is an arbitrary string, and FIFO is the name of the FIFO.
For 2., use
{"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}}
where ID2 is a different arbitrary string. Since there's no migration
to pause, the command will fail, but that's fine; instant failure is
still a test of out-of-band responses overtaking in-band commands.
For 3., open FIFO for writing.
Drop QMP command x-oob-test.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-6-armbru@redhat.com>
[Error checking tweaked]
2018-07-03 16:53:31 +08:00
|
|
|
/* 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)
|
|
|
|
{
|
2018-08-06 14:53:22 +08:00
|
|
|
qtest_qmp_send(s, "{ 'execute': 'blockdev-add', 'id': %s,"
|
|
|
|
" 'arguments': {"
|
|
|
|
" 'driver': 'blkdebug', 'node-name': %s,"
|
|
|
|
" 'config': %s,"
|
2019-07-29 20:46:00 +08:00
|
|
|
" 'image': { 'driver': 'null-co', 'read-zeroes': true } } }",
|
2018-08-06 14:53:22 +08:00
|
|
|
id, id, fifo_name);
|
qmp: Get rid of x-oob-test command
tests/qmp-test tests an out-of-band command overtaking a slow in-band
command. To do that, it needs:
1. An in-band command that *reliably* takes long enough to be
overtaken.
2. An out-of-band command to do the overtaking.
3. To avoid delays, a way to make the in-band command complete quickly
after it was overtaken.
To satisfy these needs, commit 469638f9cb3 provides the rather
peculiar oob-capable QMP command x-oob-test:
* With "lock": true, it waits for a global semaphore.
* With "lock": false, it signals the global semaphore.
To satisfy 1., the test runs x-oob-test in-band with "lock": true.
To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false.
Note that waiting for a semaphore violates the rules for oob-capable
commands. Running x-oob-test with "lock": true hangs the monitor
until you run x-oob-test with "lock": false on another monitor (which
you might not have set up).
Having an externally visible QMP command that may hang the monitor is
not nice. Let's apply a little more ingenuity to the problem. Idea:
have an existing command block on reading a FIFO special file, unblock
it by opening the FIFO for writing.
For 1., use
{"execute": "blockdev-add", "id": ID1,
"arguments": {
"driver": "blkdebug", "node-name": ID1, "config": FIFO,
"image": { "driver": "null-co"}}}
where ID1 is an arbitrary string, and FIFO is the name of the FIFO.
For 2., use
{"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}}
where ID2 is a different arbitrary string. Since there's no migration
to pause, the command will fail, but that's fine; instant failure is
still a test of out-of-band responses overtaking in-band commands.
For 3., open FIFO for writing.
Drop QMP command x-oob-test.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-6-armbru@redhat.com>
[Error checking tweaked]
2018-07-03 16:53:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2018-08-06 14:53:22 +08:00
|
|
|
qtest_qmp_send(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id);
|
qmp: Get rid of x-oob-test command
tests/qmp-test tests an out-of-band command overtaking a slow in-band
command. To do that, it needs:
1. An in-band command that *reliably* takes long enough to be
overtaken.
2. An out-of-band command to do the overtaking.
3. To avoid delays, a way to make the in-band command complete quickly
after it was overtaken.
To satisfy these needs, commit 469638f9cb3 provides the rather
peculiar oob-capable QMP command x-oob-test:
* With "lock": true, it waits for a global semaphore.
* With "lock": false, it signals the global semaphore.
To satisfy 1., the test runs x-oob-test in-band with "lock": true.
To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false.
Note that waiting for a semaphore violates the rules for oob-capable
commands. Running x-oob-test with "lock": true hangs the monitor
until you run x-oob-test with "lock": false on another monitor (which
you might not have set up).
Having an externally visible QMP command that may hang the monitor is
not nice. Let's apply a little more ingenuity to the problem. Idea:
have an existing command block on reading a FIFO special file, unblock
it by opening the FIFO for writing.
For 1., use
{"execute": "blockdev-add", "id": ID1,
"arguments": {
"driver": "blkdebug", "node-name": ID1, "config": FIFO,
"image": { "driver": "null-co"}}}
where ID1 is an arbitrary string, and FIFO is the name of the FIFO.
For 2., use
{"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}}
where ID2 is a different arbitrary string. Since there's no migration
to pause, the command will fail, but that's fine; instant failure is
still a test of out-of-band responses overtaking in-band commands.
For 3., open FIFO for writing.
Drop QMP command x-oob-test.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-6-armbru@redhat.com>
[Error checking tweaked]
2018-07-03 16:53:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-03-26 14:39:01 +08:00
|
|
|
static void test_qmp_oob(void)
|
|
|
|
{
|
|
|
|
QTestState *qts;
|
|
|
|
QDict *resp, *q;
|
|
|
|
const QListEntry *entry;
|
|
|
|
QList *capabilities;
|
|
|
|
QString *qstr;
|
|
|
|
|
2018-10-09 14:27:16 +08:00
|
|
|
qts = qtest_init_without_qmp_handshake(common_args);
|
2018-03-26 14:39:01 +08:00
|
|
|
|
|
|
|
/* Check the greeting message. */
|
|
|
|
resp = qtest_qmp_receive(qts);
|
|
|
|
q = qdict_get_qdict(resp, "QMP");
|
|
|
|
g_assert(q);
|
|
|
|
capabilities = qdict_get_qlist(q, "capabilities");
|
|
|
|
g_assert(capabilities && !qlist_empty(capabilities));
|
|
|
|
entry = qlist_first(capabilities);
|
|
|
|
g_assert(entry);
|
|
|
|
qstr = qobject_to(QString, entry->value);
|
|
|
|
g_assert(qstr);
|
|
|
|
g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2018-03-26 14:39:01 +08:00
|
|
|
|
|
|
|
/* Try a fake capability, it should fail. */
|
|
|
|
resp = qtest_qmp(qts,
|
|
|
|
"{ 'execute': 'qmp_capabilities', "
|
|
|
|
" 'arguments': { 'enable': [ 'cap-does-not-exist' ] } }");
|
|
|
|
g_assert(qdict_haskey(resp, "error"));
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2018-03-26 14:39:01 +08:00
|
|
|
|
|
|
|
/* Now, enable OOB in current QMP session, it should succeed. */
|
|
|
|
resp = qtest_qmp(qts,
|
|
|
|
"{ 'execute': 'qmp_capabilities', "
|
|
|
|
" 'arguments': { 'enable': [ 'oob' ] } }");
|
|
|
|
g_assert(qdict_haskey(resp, "return"));
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2018-03-26 14:39:01 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try any command that does not support OOB but with OOB flag. We
|
|
|
|
* should get failure.
|
|
|
|
*/
|
qmp: Redo how the client requests out-of-band execution
Commit cf869d53172 "qmp: support out-of-band (oob) execution" added a
general mechanism for command-independent arguments just for an
out-of-band flag:
The "control" key is introduced to store this extra flag. "control"
field is used to store arguments that are shared by all the commands,
rather than command specific arguments. Let "run-oob" be the first.
However, it failed to reject unknown members of "control". For
instance, in QMP command
{"execute": "query-name", "id": 42, "control": {"crap": true}}
"crap" gets silently ignored.
Instead of fixing this, revert the general "control" mechanism
(because YAGNI), and do it the way I initially proposed, with key
"exec-oob". Simpler code, simpler interface.
An out-of-band command
{"execute": "migrate-pause", "id": 42, "control": {"run-oob": true}}
becomes
{"exec-oob": "migrate-pause", "id": 42}
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-13-armbru@redhat.com>
[Commit message typo fixed]
2018-07-03 16:53:38 +08:00
|
|
|
resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }");
|
2018-03-26 14:39:01 +08:00
|
|
|
g_assert(qdict_haskey(resp, "error"));
|
2018-04-19 23:01:43 +08:00
|
|
|
qobject_unref(resp);
|
2018-03-26 14:39:01 +08:00
|
|
|
|
qmp: Get rid of x-oob-test command
tests/qmp-test tests an out-of-band command overtaking a slow in-band
command. To do that, it needs:
1. An in-band command that *reliably* takes long enough to be
overtaken.
2. An out-of-band command to do the overtaking.
3. To avoid delays, a way to make the in-band command complete quickly
after it was overtaken.
To satisfy these needs, commit 469638f9cb3 provides the rather
peculiar oob-capable QMP command x-oob-test:
* With "lock": true, it waits for a global semaphore.
* With "lock": false, it signals the global semaphore.
To satisfy 1., the test runs x-oob-test in-band with "lock": true.
To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false.
Note that waiting for a semaphore violates the rules for oob-capable
commands. Running x-oob-test with "lock": true hangs the monitor
until you run x-oob-test with "lock": false on another monitor (which
you might not have set up).
Having an externally visible QMP command that may hang the monitor is
not nice. Let's apply a little more ingenuity to the problem. Idea:
have an existing command block on reading a FIFO special file, unblock
it by opening the FIFO for writing.
For 1., use
{"execute": "blockdev-add", "id": ID1,
"arguments": {
"driver": "blkdebug", "node-name": ID1, "config": FIFO,
"image": { "driver": "null-co"}}}
where ID1 is an arbitrary string, and FIFO is the name of the FIFO.
For 2., use
{"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}}
where ID2 is a different arbitrary string. Since there's no migration
to pause, the command will fail, but that's fine; instant failure is
still a test of out-of-band responses overtaking in-band commands.
For 3., open FIFO for writing.
Drop QMP command x-oob-test.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-6-armbru@redhat.com>
[Error checking tweaked]
2018-07-03 16:53:31 +08:00
|
|
|
/* OOB command overtakes slow in-band command */
|
|
|
|
setup_blocking_cmd();
|
|
|
|
send_cmd_that_blocks(qts, "ib-blocks-1");
|
2018-08-06 14:53:22 +08:00
|
|
|
qtest_qmp_send(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }");
|
qmp: Get rid of x-oob-test command
tests/qmp-test tests an out-of-band command overtaking a slow in-band
command. To do that, it needs:
1. An in-band command that *reliably* takes long enough to be
overtaken.
2. An out-of-band command to do the overtaking.
3. To avoid delays, a way to make the in-band command complete quickly
after it was overtaken.
To satisfy these needs, commit 469638f9cb3 provides the rather
peculiar oob-capable QMP command x-oob-test:
* With "lock": true, it waits for a global semaphore.
* With "lock": false, it signals the global semaphore.
To satisfy 1., the test runs x-oob-test in-band with "lock": true.
To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false.
Note that waiting for a semaphore violates the rules for oob-capable
commands. Running x-oob-test with "lock": true hangs the monitor
until you run x-oob-test with "lock": false on another monitor (which
you might not have set up).
Having an externally visible QMP command that may hang the monitor is
not nice. Let's apply a little more ingenuity to the problem. Idea:
have an existing command block on reading a FIFO special file, unblock
it by opening the FIFO for writing.
For 1., use
{"execute": "blockdev-add", "id": ID1,
"arguments": {
"driver": "blkdebug", "node-name": ID1, "config": FIFO,
"image": { "driver": "null-co"}}}
where ID1 is an arbitrary string, and FIFO is the name of the FIFO.
For 2., use
{"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}}
where ID2 is a different arbitrary string. Since there's no migration
to pause, the command will fail, but that's fine; instant failure is
still a test of out-of-band responses overtaking in-band commands.
For 3., open FIFO for writing.
Drop QMP command x-oob-test.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-6-armbru@redhat.com>
[Error checking tweaked]
2018-07-03 16:53:31 +08:00
|
|
|
send_oob_cmd_that_fails(qts, "oob-1");
|
|
|
|
recv_cmd_id(qts, "oob-1");
|
|
|
|
unblock_blocked_cmd();
|
|
|
|
recv_cmd_id(qts, "ib-blocks-1");
|
2018-07-03 16:53:32 +08:00
|
|
|
recv_cmd_id(qts, "ib-quick-1");
|
2018-07-03 16:53:42 +08:00
|
|
|
|
2018-07-03 16:53:43 +08:00
|
|
|
/* Even malformed in-band command fails in-band */
|
2018-07-03 16:53:42 +08:00
|
|
|
send_cmd_that_blocks(qts, "blocks-2");
|
2018-08-06 14:53:22 +08:00
|
|
|
qtest_qmp_send(qts, "{ 'id': 'err-2' }");
|
2018-07-03 16:53:42 +08:00
|
|
|
unblock_blocked_cmd();
|
|
|
|
recv_cmd_id(qts, "blocks-2");
|
2018-07-03 16:53:43 +08:00
|
|
|
recv_cmd_id(qts, "err-2");
|
qmp: Get rid of x-oob-test command
tests/qmp-test tests an out-of-band command overtaking a slow in-band
command. To do that, it needs:
1. An in-band command that *reliably* takes long enough to be
overtaken.
2. An out-of-band command to do the overtaking.
3. To avoid delays, a way to make the in-band command complete quickly
after it was overtaken.
To satisfy these needs, commit 469638f9cb3 provides the rather
peculiar oob-capable QMP command x-oob-test:
* With "lock": true, it waits for a global semaphore.
* With "lock": false, it signals the global semaphore.
To satisfy 1., the test runs x-oob-test in-band with "lock": true.
To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false.
Note that waiting for a semaphore violates the rules for oob-capable
commands. Running x-oob-test with "lock": true hangs the monitor
until you run x-oob-test with "lock": false on another monitor (which
you might not have set up).
Having an externally visible QMP command that may hang the monitor is
not nice. Let's apply a little more ingenuity to the problem. Idea:
have an existing command block on reading a FIFO special file, unblock
it by opening the FIFO for writing.
For 1., use
{"execute": "blockdev-add", "id": ID1,
"arguments": {
"driver": "blkdebug", "node-name": ID1, "config": FIFO,
"image": { "driver": "null-co"}}}
where ID1 is an arbitrary string, and FIFO is the name of the FIFO.
For 2., use
{"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}}
where ID2 is a different arbitrary string. Since there's no migration
to pause, the command will fail, but that's fine; instant failure is
still a test of out-of-band responses overtaking in-band commands.
For 3., open FIFO for writing.
Drop QMP command x-oob-test.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-6-armbru@redhat.com>
[Error checking tweaked]
2018-07-03 16:53:31 +08:00
|
|
|
cleanup_blocking_cmd();
|
2018-03-26 14:39:01 +08:00
|
|
|
|
|
|
|
qtest_quit(qts);
|
|
|
|
}
|
|
|
|
|
qmp: Get rid of x-oob-test command
tests/qmp-test tests an out-of-band command overtaking a slow in-band
command. To do that, it needs:
1. An in-band command that *reliably* takes long enough to be
overtaken.
2. An out-of-band command to do the overtaking.
3. To avoid delays, a way to make the in-band command complete quickly
after it was overtaken.
To satisfy these needs, commit 469638f9cb3 provides the rather
peculiar oob-capable QMP command x-oob-test:
* With "lock": true, it waits for a global semaphore.
* With "lock": false, it signals the global semaphore.
To satisfy 1., the test runs x-oob-test in-band with "lock": true.
To satisfy 2. and 3., it runs x-oob-test out-of-band with "lock": false.
Note that waiting for a semaphore violates the rules for oob-capable
commands. Running x-oob-test with "lock": true hangs the monitor
until you run x-oob-test with "lock": false on another monitor (which
you might not have set up).
Having an externally visible QMP command that may hang the monitor is
not nice. Let's apply a little more ingenuity to the problem. Idea:
have an existing command block on reading a FIFO special file, unblock
it by opening the FIFO for writing.
For 1., use
{"execute": "blockdev-add", "id": ID1,
"arguments": {
"driver": "blkdebug", "node-name": ID1, "config": FIFO,
"image": { "driver": "null-co"}}}
where ID1 is an arbitrary string, and FIFO is the name of the FIFO.
For 2., use
{"execute": "migrate-pause", "id": ID2, "control": {"run-oob": true}}
where ID2 is a different arbitrary string. Since there's no migration
to pause, the command will fail, but that's fine; instant failure is
still a test of out-of-band responses overtaking in-band commands.
For 3., open FIFO for writing.
Drop QMP command x-oob-test.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Message-Id: <20180703085358.13941-6-armbru@redhat.com>
[Error checking tweaked]
2018-07-03 16:53:31 +08:00
|
|
|
/* Preconfig tests */
|
|
|
|
|
2018-05-17 19:28:44 +08:00
|
|
|
static void test_qmp_preconfig(void)
|
|
|
|
{
|
|
|
|
QDict *rsp, *ret;
|
2018-08-06 14:53:43 +08:00
|
|
|
QTestState *qs = qtest_initf("%s --preconfig", common_args);
|
2018-05-17 19:28:44 +08:00
|
|
|
|
|
|
|
/* preconfig state */
|
|
|
|
/* enabled commands, no error expected */
|
|
|
|
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }")));
|
|
|
|
|
|
|
|
/* forbidden commands, expected error */
|
|
|
|
g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
|
|
|
|
|
|
|
|
/* check that query-status returns preconfig state */
|
|
|
|
rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
|
|
|
|
ret = qdict_get_qdict(rsp, "return");
|
|
|
|
g_assert(ret);
|
|
|
|
g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig");
|
|
|
|
qobject_unref(rsp);
|
|
|
|
|
|
|
|
/* exit preconfig state */
|
2018-07-05 17:14:02 +08:00
|
|
|
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }")));
|
2018-05-17 19:28:44 +08:00
|
|
|
qtest_qmp_eventwait(qs, "RESUME");
|
|
|
|
|
|
|
|
/* check that query-status returns running state */
|
|
|
|
rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
|
|
|
|
ret = qdict_get_qdict(rsp, "return");
|
|
|
|
g_assert(ret);
|
|
|
|
g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running");
|
|
|
|
qobject_unref(rsp);
|
|
|
|
|
2018-07-05 17:14:02 +08:00
|
|
|
/* check that x-exit-preconfig returns error after exiting preconfig */
|
|
|
|
g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }")));
|
2018-05-17 19:28:44 +08:00
|
|
|
|
|
|
|
/* enabled commands, no error expected */
|
|
|
|
g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
|
|
|
|
|
|
|
|
qtest_quit(qs);
|
|
|
|
}
|
|
|
|
|
2018-10-29 22:57:09 +08:00
|
|
|
static void test_qmp_missing_any_arg(void)
|
|
|
|
{
|
|
|
|
QTestState *qts;
|
|
|
|
QDict *resp;
|
|
|
|
|
|
|
|
qts = qtest_init(common_args);
|
|
|
|
resp = qtest_qmp(qts, "{'execute': 'qom-set', 'arguments':"
|
|
|
|
" { 'path': '/machine', 'property': 'rtc-time' } }");
|
|
|
|
g_assert_nonnull(resp);
|
|
|
|
qmp_assert_error_class(resp, "GenericError");
|
|
|
|
qtest_quit(qts);
|
|
|
|
}
|
|
|
|
|
2017-03-03 20:32:23 +08:00
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
g_test_init(&argc, &argv, NULL);
|
|
|
|
|
|
|
|
qtest_add_func("qmp/protocol", test_qmp_protocol);
|
2018-03-26 14:39:01 +08:00
|
|
|
qtest_add_func("qmp/oob", test_qmp_oob);
|
2018-05-17 19:28:44 +08:00
|
|
|
qtest_add_func("qmp/preconfig", test_qmp_preconfig);
|
2018-10-29 22:57:09 +08:00
|
|
|
qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg);
|
tests/qmp-test: Add generic, basic test of query commands
A command is a query if it has no side effect and yields a result.
Such commands are typically named query-FOO, but there are exceptions.
The basic idea is to find candidates with query-qmp-schema, filter out
the ones that aren't queries with an explicit blacklist, and test the
remaining ones against a QEMU with no special arguments.
The current blacklist is just add-fd.
The test can't do queries with arguments, because it knows nothing
about the arguments. No coverage for query-cpu-model-baseline,
query-cpu-model-comparison, query-cpu-model-expansion, query-rocker,
query-rocker-ports, query-rocker-of-dpa-flows, and
query-rocker-of-dpa-groups.
Most tested commands are expected to succeed. The test does not check
the return value then.
query-balloon and query-vm-generation-id are expected to fail because
they need a virtio-balloon / vmgenid device to succeed, and this test
is too dumb to set one up. Could be addressed later.
query-acpi-ospm-status and query-hotpluggable-cpus are expected to
fail because they require features provided only by special machine
types, and this test is too dumb to set that up. Could also be
addressed later.
Several commands may either be functional or stubs that always fail,
depending on build configuration. Ideally, the stubs shouldn't be in
query-qmp-schema, but that requires QAPI schema compile-time
configuration, which we don't have, yet. Until we do, we need to
figure out whether a command is a stub. When we have a suitable
CONFIG_FOO preprocessor symbol is available, use that. Else,
simply blacklist the command for now.
We get basic test coverage for the following commands, except as
noted:
qom-list-types
query-acpi-ospm-status (expected to fail)
query-balloon (expected to fail)
query-block
query-block-jobs
query-blockstats
query-chardev
query-chardev-backends
query-command-line-options
query-commands
query-cpu-definitions (blacklisted for now)
query-cpus
query-dump
query-dump-guest-memory-capability
query-events
query-fdsets
query-gic-capabilities (blacklisted for now)
query-hotpluggable-cpus (expected to fail)
query-iothreads
query-kvm
query-machines
query-memdev
query-memory-devices
query-mice
query-migrate
query-migrate-cache-size
query-migrate-capabilities
query-migrate-parameters
query-name
query-named-block-nodes
query-pci (blacklisted for now)
query-qmp-schema
query-rx-filter
query-spice
query-status
query-target
query-tpm
query-tpm-models
query-tpm-types
query-uuid
query-version
query-vm-generation-id (expected to fail)
query-vnc
query-vnc-servers
query-xen-replication-status
Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <1502461148-10154-1-git-send-email-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[Typos in code under #ifndef and in the commit message fixed]
2017-08-11 22:19:08 +08:00
|
|
|
|
2018-08-24 00:39:32 +08:00
|
|
|
return g_test_run();
|
2017-03-03 20:32:23 +08:00
|
|
|
}
|