1 /* 2 * QTest migration helpers 3 * 4 * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates 5 * based on the vhost-user-test.c that is: 6 * Copyright (c) 2014 Virtual Open Systems Sarl. 7 * 8 * This work is licensed under the terms of the GNU GPL, version 2 or later. 9 * See the COPYING file in the top-level directory. 10 * 11 */ 12 13 #include "qemu/osdep.h" 14 #include "qapi/qmp/qjson.h" 15 16 #include "migration-helpers.h" 17 18 bool got_stop; 19 20 static void stop_cb(void *opaque, const char *name, QDict *data) 21 { 22 if (!strcmp(name, "STOP")) { 23 got_stop = true; 24 } 25 } 26 27 /* 28 * Events can get in the way of responses we are actually waiting for. 29 */ 30 QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) 31 { 32 va_list ap; 33 34 va_start(ap, command); 35 qtest_qmp_vsend_fds(who, &fd, 1, command, ap); 36 va_end(ap); 37 38 return qtest_qmp_receive_success(who, stop_cb, NULL); 39 } 40 41 /* 42 * Events can get in the way of responses we are actually waiting for. 43 */ 44 QDict *wait_command(QTestState *who, const char *command, ...) 45 { 46 va_list ap; 47 48 va_start(ap, command); 49 qtest_qmp_vsend(who, command, ap); 50 va_end(ap); 51 52 return qtest_qmp_receive_success(who, stop_cb, NULL); 53 } 54 55 /* 56 * Send QMP command "migrate". 57 * Arguments are built from @fmt... (formatted like 58 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. 59 */ 60 void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) 61 { 62 va_list ap; 63 QDict *args, *rsp; 64 65 va_start(ap, fmt); 66 args = qdict_from_vjsonf_nofail(fmt, ap); 67 va_end(ap); 68 69 g_assert(!qdict_haskey(args, "uri")); 70 qdict_put_str(args, "uri", uri); 71 72 rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); 73 74 g_assert(qdict_haskey(rsp, "return")); 75 qobject_unref(rsp); 76 } 77 78 /* 79 * Note: caller is responsible to free the returned object via 80 * qobject_unref() after use 81 */ 82 QDict *migrate_query(QTestState *who) 83 { 84 return wait_command(who, "{ 'execute': 'query-migrate' }"); 85 } 86 87 /* 88 * Note: caller is responsible to free the returned object via 89 * g_free() after use 90 */ 91 static gchar *migrate_query_status(QTestState *who) 92 { 93 QDict *rsp_return = migrate_query(who); 94 gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); 95 96 g_assert(status); 97 qobject_unref(rsp_return); 98 99 return status; 100 } 101 102 static bool check_migration_status(QTestState *who, const char *goal, 103 const char **ungoals) 104 { 105 bool ready; 106 char *current_status; 107 const char **ungoal; 108 109 current_status = migrate_query_status(who); 110 ready = strcmp(current_status, goal) == 0; 111 if (!ungoals) { 112 g_assert_cmpstr(current_status, !=, "failed"); 113 /* 114 * If looking for a state other than completed, 115 * completion of migration would cause the test to 116 * hang. 117 */ 118 if (strcmp(goal, "completed") != 0) { 119 g_assert_cmpstr(current_status, !=, "completed"); 120 } 121 } else { 122 for (ungoal = ungoals; *ungoal; ungoal++) { 123 g_assert_cmpstr(current_status, !=, *ungoal); 124 } 125 } 126 g_free(current_status); 127 return ready; 128 } 129 130 void wait_for_migration_status(QTestState *who, 131 const char *goal, const char **ungoals) 132 { 133 while (!check_migration_status(who, goal, ungoals)) { 134 usleep(1000); 135 } 136 } 137 138 void wait_for_migration_complete(QTestState *who) 139 { 140 wait_for_migration_status(who, "completed", NULL); 141 } 142 143 void wait_for_migration_fail(QTestState *from, bool allow_active) 144 { 145 QDict *rsp_return; 146 char *status; 147 bool failed; 148 149 do { 150 status = migrate_query_status(from); 151 bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || 152 (allow_active && !strcmp(status, "active")); 153 if (!result) { 154 fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", 155 __func__, status, allow_active); 156 } 157 g_assert(result); 158 failed = !strcmp(status, "failed"); 159 g_free(status); 160 } while (!failed); 161 162 /* Is the machine currently running? */ 163 rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); 164 g_assert(qdict_haskey(rsp_return, "running")); 165 g_assert(qdict_get_bool(rsp_return, "running")); 166 qobject_unref(rsp_return); 167 } 168