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