xref: /qemu/tests/qtest/qmp-test.c (revision d93bb9d5c34c1b4ed97bf824e6459b5516db94f6)
1f66e7ac8SMarkus Armbruster /*
2f66e7ac8SMarkus Armbruster  * QMP protocol test cases
3f66e7ac8SMarkus Armbruster  *
4f66e7ac8SMarkus Armbruster  * Copyright (c) 2017 Red Hat Inc.
5f66e7ac8SMarkus Armbruster  *
6f66e7ac8SMarkus Armbruster  * Authors:
7*d93bb9d5SMarkus Armbruster  *  Markus Armbruster <armbru@redhat.com>
8f66e7ac8SMarkus Armbruster  *
9f66e7ac8SMarkus Armbruster  * This work is licensed under the terms of the GNU GPL, version 2 or later.
10f66e7ac8SMarkus Armbruster  * See the COPYING file in the top-level directory.
11f66e7ac8SMarkus Armbruster  */
12f66e7ac8SMarkus Armbruster 
13f66e7ac8SMarkus Armbruster #include "qemu/osdep.h"
14f66e7ac8SMarkus Armbruster #include "libqtest.h"
15f66e7ac8SMarkus Armbruster #include "qapi/error.h"
16112ed241SMarkus Armbruster #include "qapi/qapi-visit-misc.h"
17452fcdbcSMarkus Armbruster #include "qapi/qmp/qdict.h"
1847e6b297SMarkus Armbruster #include "qapi/qmp/qlist.h"
19f66e7ac8SMarkus Armbruster #include "qapi/qobject-input-visitor.h"
2002130314SPeter Xu #include "qapi/qmp/qstring.h"
21f66e7ac8SMarkus Armbruster 
22f66e7ac8SMarkus Armbruster const char common_args[] = "-nodefaults -machine none";
23f66e7ac8SMarkus Armbruster 
24f66e7ac8SMarkus Armbruster static const char *get_error_class(QDict *resp)
25f66e7ac8SMarkus Armbruster {
26f66e7ac8SMarkus Armbruster     QDict *error = qdict_get_qdict(resp, "error");
27f66e7ac8SMarkus Armbruster     const char *desc = qdict_get_try_str(error, "desc");
28f66e7ac8SMarkus Armbruster 
29f66e7ac8SMarkus Armbruster     g_assert(desc);
30f66e7ac8SMarkus Armbruster     return error ? qdict_get_try_str(error, "class") : NULL;
31f66e7ac8SMarkus Armbruster }
32f66e7ac8SMarkus Armbruster 
33f66e7ac8SMarkus Armbruster static void test_version(QObject *version)
34f66e7ac8SMarkus Armbruster {
35f66e7ac8SMarkus Armbruster     Visitor *v;
36f66e7ac8SMarkus Armbruster     VersionInfo *vinfo;
37f66e7ac8SMarkus Armbruster 
38f66e7ac8SMarkus Armbruster     g_assert(version);
39048abb7bSMarkus Armbruster     v = qobject_input_visitor_new(version);
40f66e7ac8SMarkus Armbruster     visit_type_VersionInfo(v, "version", &vinfo, &error_abort);
41f66e7ac8SMarkus Armbruster     qapi_free_VersionInfo(vinfo);
42f66e7ac8SMarkus Armbruster     visit_free(v);
43f66e7ac8SMarkus Armbruster }
44f66e7ac8SMarkus Armbruster 
456a5c88f5SEric Blake static void test_malformed(QTestState *qts)
46f66e7ac8SMarkus Armbruster {
47f66e7ac8SMarkus Armbruster     QDict *resp;
48f66e7ac8SMarkus Armbruster 
49f66e7ac8SMarkus Armbruster     /* Not even a dictionary */
506a5c88f5SEric Blake     resp = qtest_qmp(qts, "null");
51f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
52cb3e7f08SMarc-André Lureau     qobject_unref(resp);
53f66e7ac8SMarkus Armbruster 
54f66e7ac8SMarkus Armbruster     /* No "execute" key */
556a5c88f5SEric Blake     resp = qtest_qmp(qts, "{}");
56f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
57cb3e7f08SMarc-André Lureau     qobject_unref(resp);
58f66e7ac8SMarkus Armbruster 
59f66e7ac8SMarkus Armbruster     /* "execute" isn't a string */
606a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': true }");
61f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
62cb3e7f08SMarc-André Lureau     qobject_unref(resp);
63f66e7ac8SMarkus Armbruster 
64f66e7ac8SMarkus Armbruster     /* "arguments" isn't a dictionary */
656a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }");
66f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
67cb3e7f08SMarc-André Lureau     qobject_unref(resp);
68f66e7ac8SMarkus Armbruster 
69f66e7ac8SMarkus Armbruster     /* extra key */
706a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }");
71f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
72cb3e7f08SMarc-André Lureau     qobject_unref(resp);
73f66e7ac8SMarkus Armbruster }
74f66e7ac8SMarkus Armbruster 
75f66e7ac8SMarkus Armbruster static void test_qmp_protocol(void)
76f66e7ac8SMarkus Armbruster {
77f66e7ac8SMarkus Armbruster     QDict *resp, *q, *ret;
78f66e7ac8SMarkus Armbruster     QList *capabilities;
796a5c88f5SEric Blake     QTestState *qts;
80f66e7ac8SMarkus Armbruster 
81ddee57e0SEric Blake     qts = qtest_init_without_qmp_handshake(false, common_args);
82f66e7ac8SMarkus Armbruster 
83f66e7ac8SMarkus Armbruster     /* Test greeting */
846a5c88f5SEric Blake     resp = qtest_qmp_receive(qts);
85f66e7ac8SMarkus Armbruster     q = qdict_get_qdict(resp, "QMP");
86f66e7ac8SMarkus Armbruster     g_assert(q);
87f66e7ac8SMarkus Armbruster     test_version(qdict_get(q, "version"));
88f66e7ac8SMarkus Armbruster     capabilities = qdict_get_qlist(q, "capabilities");
89a4f90923SPeter Xu     g_assert(capabilities && qlist_empty(capabilities));
90cb3e7f08SMarc-André Lureau     qobject_unref(resp);
91f66e7ac8SMarkus Armbruster 
92f66e7ac8SMarkus Armbruster     /* Test valid command before handshake */
936a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'query-version' }");
94f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound");
95cb3e7f08SMarc-André Lureau     qobject_unref(resp);
96f66e7ac8SMarkus Armbruster 
97f66e7ac8SMarkus Armbruster     /* Test malformed commands before handshake */
986a5c88f5SEric Blake     test_malformed(qts);
99f66e7ac8SMarkus Armbruster 
100f66e7ac8SMarkus Armbruster     /* Test handshake */
1016a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }");
102f66e7ac8SMarkus Armbruster     ret = qdict_get_qdict(resp, "return");
103f66e7ac8SMarkus Armbruster     g_assert(ret && !qdict_size(ret));
104cb3e7f08SMarc-André Lureau     qobject_unref(resp);
105f66e7ac8SMarkus Armbruster 
106f66e7ac8SMarkus Armbruster     /* Test repeated handshake */
1076a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }");
108f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound");
109cb3e7f08SMarc-André Lureau     qobject_unref(resp);
110f66e7ac8SMarkus Armbruster 
111f66e7ac8SMarkus Armbruster     /* Test valid command */
1126a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'query-version' }");
113f66e7ac8SMarkus Armbruster     test_version(qdict_get(resp, "return"));
114cb3e7f08SMarc-André Lureau     qobject_unref(resp);
115f66e7ac8SMarkus Armbruster 
116f66e7ac8SMarkus Armbruster     /* Test malformed commands */
1176a5c88f5SEric Blake     test_malformed(qts);
118f66e7ac8SMarkus Armbruster 
119f66e7ac8SMarkus Armbruster     /* Test 'id' */
1206a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'query-name', 'id': 'cookie#1' }");
121f66e7ac8SMarkus Armbruster     ret = qdict_get_qdict(resp, "return");
122f66e7ac8SMarkus Armbruster     g_assert(ret);
123f66e7ac8SMarkus Armbruster     g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, "cookie#1");
124cb3e7f08SMarc-André Lureau     qobject_unref(resp);
125f66e7ac8SMarkus Armbruster 
126f66e7ac8SMarkus Armbruster     /* Test command failure with 'id' */
1276a5c88f5SEric Blake     resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }");
128f66e7ac8SMarkus Armbruster     g_assert_cmpstr(get_error_class(resp), ==, "GenericError");
129f66e7ac8SMarkus Armbruster     g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2);
130cb3e7f08SMarc-André Lureau     qobject_unref(resp);
131f66e7ac8SMarkus Armbruster 
1326a5c88f5SEric Blake     qtest_quit(qts);
133f66e7ac8SMarkus Armbruster }
134f66e7ac8SMarkus Armbruster 
13597ca0712SMarkus Armbruster /* Out-of-band tests */
13697ca0712SMarkus Armbruster 
13797ca0712SMarkus Armbruster char tmpdir[] = "/tmp/qmp-test-XXXXXX";
13897ca0712SMarkus Armbruster char *fifo_name;
13997ca0712SMarkus Armbruster 
14097ca0712SMarkus Armbruster static void setup_blocking_cmd(void)
14197ca0712SMarkus Armbruster {
14297ca0712SMarkus Armbruster     if (!mkdtemp(tmpdir)) {
14397ca0712SMarkus Armbruster         g_error("mkdtemp: %s", strerror(errno));
14497ca0712SMarkus Armbruster     }
14597ca0712SMarkus Armbruster     fifo_name = g_strdup_printf("%s/fifo", tmpdir);
14697ca0712SMarkus Armbruster     if (mkfifo(fifo_name, 0666)) {
14797ca0712SMarkus Armbruster         g_error("mkfifo: %s", strerror(errno));
14897ca0712SMarkus Armbruster     }
14997ca0712SMarkus Armbruster }
15097ca0712SMarkus Armbruster 
15197ca0712SMarkus Armbruster static void cleanup_blocking_cmd(void)
15297ca0712SMarkus Armbruster {
15397ca0712SMarkus Armbruster     unlink(fifo_name);
15497ca0712SMarkus Armbruster     rmdir(tmpdir);
15597ca0712SMarkus Armbruster }
15697ca0712SMarkus Armbruster 
15797ca0712SMarkus Armbruster static void send_cmd_that_blocks(QTestState *s, const char *id)
15897ca0712SMarkus Armbruster {
1594277f1ebSMarkus Armbruster     qtest_qmp_send(s, "{ 'execute': 'blockdev-add',  'id': %s,"
16097ca0712SMarkus Armbruster                    " 'arguments': {"
16197ca0712SMarkus Armbruster                    " 'driver': 'blkdebug', 'node-name': %s,"
16297ca0712SMarkus Armbruster                    " 'config': %s,"
16397ca0712SMarkus Armbruster                    " 'image': { 'driver': 'null-co' } } }",
16497ca0712SMarkus Armbruster                    id, id, fifo_name);
16597ca0712SMarkus Armbruster }
16697ca0712SMarkus Armbruster 
16797ca0712SMarkus Armbruster static void unblock_blocked_cmd(void)
16897ca0712SMarkus Armbruster {
16997ca0712SMarkus Armbruster     int fd = open(fifo_name, O_WRONLY);
17097ca0712SMarkus Armbruster     g_assert(fd >= 0);
17197ca0712SMarkus Armbruster     close(fd);
17297ca0712SMarkus Armbruster }
17397ca0712SMarkus Armbruster 
17497ca0712SMarkus Armbruster static void send_oob_cmd_that_fails(QTestState *s, const char *id)
17597ca0712SMarkus Armbruster {
1764277f1ebSMarkus Armbruster     qtest_qmp_send(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id);
17797ca0712SMarkus Armbruster }
17897ca0712SMarkus Armbruster 
17997ca0712SMarkus Armbruster static void recv_cmd_id(QTestState *s, const char *id)
18097ca0712SMarkus Armbruster {
18197ca0712SMarkus Armbruster     QDict *resp = qtest_qmp_receive(s);
18297ca0712SMarkus Armbruster 
18397ca0712SMarkus Armbruster     g_assert_cmpstr(qdict_get_try_str(resp, "id"), ==, id);
18497ca0712SMarkus Armbruster     qobject_unref(resp);
18597ca0712SMarkus Armbruster }
18697ca0712SMarkus Armbruster 
187fa198ad9SPeter Xu static void test_qmp_oob(void)
188fa198ad9SPeter Xu {
189fa198ad9SPeter Xu     QTestState *qts;
190fa198ad9SPeter Xu     QDict *resp, *q;
191fa198ad9SPeter Xu     const QListEntry *entry;
192fa198ad9SPeter Xu     QList *capabilities;
193fa198ad9SPeter Xu     QString *qstr;
194fa198ad9SPeter Xu 
195fa198ad9SPeter Xu     qts = qtest_init_without_qmp_handshake(true, common_args);
196fa198ad9SPeter Xu 
197fa198ad9SPeter Xu     /* Check the greeting message. */
198fa198ad9SPeter Xu     resp = qtest_qmp_receive(qts);
199fa198ad9SPeter Xu     q = qdict_get_qdict(resp, "QMP");
200fa198ad9SPeter Xu     g_assert(q);
201fa198ad9SPeter Xu     capabilities = qdict_get_qlist(q, "capabilities");
202fa198ad9SPeter Xu     g_assert(capabilities && !qlist_empty(capabilities));
203fa198ad9SPeter Xu     entry = qlist_first(capabilities);
204fa198ad9SPeter Xu     g_assert(entry);
205fa198ad9SPeter Xu     qstr = qobject_to(QString, entry->value);
206fa198ad9SPeter Xu     g_assert(qstr);
207fa198ad9SPeter Xu     g_assert_cmpstr(qstring_get_str(qstr), ==, "oob");
208cb3e7f08SMarc-André Lureau     qobject_unref(resp);
209fa198ad9SPeter Xu 
210fa198ad9SPeter Xu     /* Try a fake capability, it should fail. */
211fa198ad9SPeter Xu     resp = qtest_qmp(qts,
212fa198ad9SPeter Xu                      "{ 'execute': 'qmp_capabilities', "
213fa198ad9SPeter Xu                      "  'arguments': { 'enable': [ 'cap-does-not-exist' ] } }");
214fa198ad9SPeter Xu     g_assert(qdict_haskey(resp, "error"));
215cb3e7f08SMarc-André Lureau     qobject_unref(resp);
216fa198ad9SPeter Xu 
217fa198ad9SPeter Xu     /* Now, enable OOB in current QMP session, it should succeed. */
218fa198ad9SPeter Xu     resp = qtest_qmp(qts,
219fa198ad9SPeter Xu                      "{ 'execute': 'qmp_capabilities', "
220fa198ad9SPeter Xu                      "  'arguments': { 'enable': [ 'oob' ] } }");
221fa198ad9SPeter Xu     g_assert(qdict_haskey(resp, "return"));
222cb3e7f08SMarc-André Lureau     qobject_unref(resp);
223fa198ad9SPeter Xu 
224fa198ad9SPeter Xu     /*
225fa198ad9SPeter Xu      * Try any command that does not support OOB but with OOB flag. We
226fa198ad9SPeter Xu      * should get failure.
227fa198ad9SPeter Xu      */
22800ecec15SMarkus Armbruster     resp = qtest_qmp(qts, "{ 'exec-oob': 'query-cpus' }");
229fa198ad9SPeter Xu     g_assert(qdict_haskey(resp, "error"));
230cb3e7f08SMarc-André Lureau     qobject_unref(resp);
231fa198ad9SPeter Xu 
23297ca0712SMarkus Armbruster     /* OOB command overtakes slow in-band command */
23397ca0712SMarkus Armbruster     setup_blocking_cmd();
23497ca0712SMarkus Armbruster     send_cmd_that_blocks(qts, "ib-blocks-1");
2354277f1ebSMarkus Armbruster     qtest_qmp_send(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }");
23697ca0712SMarkus Armbruster     send_oob_cmd_that_fails(qts, "oob-1");
23797ca0712SMarkus Armbruster     recv_cmd_id(qts, "oob-1");
23897ca0712SMarkus Armbruster     unblock_blocked_cmd();
23997ca0712SMarkus Armbruster     recv_cmd_id(qts, "ib-blocks-1");
2402970b446SMarkus Armbruster     recv_cmd_id(qts, "ib-quick-1");
241e8f4a221SMarkus Armbruster 
24269240fe6SMarkus Armbruster     /* Even malformed in-band command fails in-band */
243e8f4a221SMarkus Armbruster     send_cmd_that_blocks(qts, "blocks-2");
2444277f1ebSMarkus Armbruster     qtest_qmp_send(qts, "{ 'id': 'err-2' }");
245e8f4a221SMarkus Armbruster     unblock_blocked_cmd();
246e8f4a221SMarkus Armbruster     recv_cmd_id(qts, "blocks-2");
24769240fe6SMarkus Armbruster     recv_cmd_id(qts, "err-2");
24897ca0712SMarkus Armbruster     cleanup_blocking_cmd();
249fa198ad9SPeter Xu 
250fa198ad9SPeter Xu     qtest_quit(qts);
251fa198ad9SPeter Xu }
252fa198ad9SPeter Xu 
25397ca0712SMarkus Armbruster /* Preconfig tests */
25497ca0712SMarkus Armbruster 
255fb1e58f7SIgor Mammedov static void test_qmp_preconfig(void)
256fb1e58f7SIgor Mammedov {
257fb1e58f7SIgor Mammedov     QDict *rsp, *ret;
25888b988c8SMarkus Armbruster     QTestState *qs = qtest_initf("%s --preconfig", common_args);
259fb1e58f7SIgor Mammedov 
260fb1e58f7SIgor Mammedov     /* preconfig state */
261fb1e58f7SIgor Mammedov     /* enabled commands, no error expected  */
262fb1e58f7SIgor Mammedov     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-commands' }")));
263fb1e58f7SIgor Mammedov 
264fb1e58f7SIgor Mammedov     /* forbidden commands, expected error */
265fb1e58f7SIgor Mammedov     g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
266fb1e58f7SIgor Mammedov 
267fb1e58f7SIgor Mammedov     /* check that query-status returns preconfig state */
268fb1e58f7SIgor Mammedov     rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
269fb1e58f7SIgor Mammedov     ret = qdict_get_qdict(rsp, "return");
270fb1e58f7SIgor Mammedov     g_assert(ret);
271fb1e58f7SIgor Mammedov     g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "preconfig");
272fb1e58f7SIgor Mammedov     qobject_unref(rsp);
273fb1e58f7SIgor Mammedov 
274fb1e58f7SIgor Mammedov     /* exit preconfig state */
275361ac948SMarkus Armbruster     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }")));
276fb1e58f7SIgor Mammedov     qtest_qmp_eventwait(qs, "RESUME");
277fb1e58f7SIgor Mammedov 
278fb1e58f7SIgor Mammedov     /* check that query-status returns running state */
279fb1e58f7SIgor Mammedov     rsp = qtest_qmp(qs, "{ 'execute': 'query-status' }");
280fb1e58f7SIgor Mammedov     ret = qdict_get_qdict(rsp, "return");
281fb1e58f7SIgor Mammedov     g_assert(ret);
282fb1e58f7SIgor Mammedov     g_assert_cmpstr(qdict_get_try_str(ret, "status"), ==, "running");
283fb1e58f7SIgor Mammedov     qobject_unref(rsp);
284fb1e58f7SIgor Mammedov 
285361ac948SMarkus Armbruster     /* check that x-exit-preconfig returns error after exiting preconfig */
286361ac948SMarkus Armbruster     g_assert(qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'x-exit-preconfig' }")));
287fb1e58f7SIgor Mammedov 
288fb1e58f7SIgor Mammedov     /* enabled commands, no error expected  */
289fb1e58f7SIgor Mammedov     g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'query-cpus' }")));
290fb1e58f7SIgor Mammedov 
291fb1e58f7SIgor Mammedov     qtest_quit(qs);
292fb1e58f7SIgor Mammedov }
293fb1e58f7SIgor Mammedov 
294f66e7ac8SMarkus Armbruster int main(int argc, char *argv[])
295f66e7ac8SMarkus Armbruster {
296f66e7ac8SMarkus Armbruster     g_test_init(&argc, &argv, NULL);
297f66e7ac8SMarkus Armbruster 
298f66e7ac8SMarkus Armbruster     qtest_add_func("qmp/protocol", test_qmp_protocol);
299fa198ad9SPeter Xu     qtest_add_func("qmp/oob", test_qmp_oob);
300fb1e58f7SIgor Mammedov     qtest_add_func("qmp/preconfig", test_qmp_preconfig);
301f66e7ac8SMarkus Armbruster 
302*d93bb9d5SMarkus Armbruster     return g_test_run();
303f66e7ac8SMarkus Armbruster }
304