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 /* 19 * Number of seconds we wait when looking for migration 20 * status changes, to avoid test suite hanging forever 21 * when things go wrong. Needs to be higher enough to 22 * avoid false positives on loaded hosts. 23 */ 24 #define MIGRATION_STATUS_WAIT_TIMEOUT 120 25 26 bool got_stop; 27 28 static void check_stop_event(QTestState *who) 29 { 30 QDict *event = qtest_qmp_event_ref(who, "STOP"); 31 if (event) { 32 got_stop = true; 33 qobject_unref(event); 34 } 35 } 36 37 /* 38 * Events can get in the way of responses we are actually waiting for. 39 */ 40 QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...) 41 { 42 va_list ap; 43 QDict *resp, *ret; 44 45 va_start(ap, command); 46 qtest_qmp_vsend_fds(who, &fd, 1, command, ap); 47 va_end(ap); 48 49 resp = qtest_qmp_receive(who); 50 check_stop_event(who); 51 52 g_assert(!qdict_haskey(resp, "error")); 53 g_assert(qdict_haskey(resp, "return")); 54 55 ret = qdict_get_qdict(resp, "return"); 56 qobject_ref(ret); 57 qobject_unref(resp); 58 59 return ret; 60 } 61 62 /* 63 * Events can get in the way of responses we are actually waiting for. 64 */ 65 QDict *wait_command(QTestState *who, const char *command, ...) 66 { 67 va_list ap; 68 QDict *resp, *ret; 69 70 va_start(ap, command); 71 resp = qtest_vqmp(who, command, ap); 72 va_end(ap); 73 74 check_stop_event(who); 75 76 g_assert(!qdict_haskey(resp, "error")); 77 g_assert(qdict_haskey(resp, "return")); 78 79 ret = qdict_get_qdict(resp, "return"); 80 qobject_ref(ret); 81 qobject_unref(resp); 82 83 return ret; 84 } 85 86 /* 87 * Execute the qmp command only 88 */ 89 QDict *qmp_command(QTestState *who, const char *command, ...) 90 { 91 va_list ap; 92 QDict *resp, *ret; 93 94 va_start(ap, command); 95 resp = qtest_vqmp(who, command, ap); 96 va_end(ap); 97 98 g_assert(!qdict_haskey(resp, "error")); 99 g_assert(qdict_haskey(resp, "return")); 100 101 ret = qdict_get_qdict(resp, "return"); 102 qobject_ref(ret); 103 qobject_unref(resp); 104 105 return ret; 106 } 107 108 /* 109 * Send QMP command "migrate". 110 * Arguments are built from @fmt... (formatted like 111 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. 112 */ 113 void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) 114 { 115 va_list ap; 116 QDict *args, *rsp; 117 118 va_start(ap, fmt); 119 args = qdict_from_vjsonf_nofail(fmt, ap); 120 va_end(ap); 121 122 g_assert(!qdict_haskey(args, "uri")); 123 qdict_put_str(args, "uri", uri); 124 125 rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args); 126 127 g_assert(qdict_haskey(rsp, "return")); 128 qobject_unref(rsp); 129 } 130 131 /* 132 * Note: caller is responsible to free the returned object via 133 * qobject_unref() after use 134 */ 135 QDict *migrate_query(QTestState *who) 136 { 137 return wait_command(who, "{ 'execute': 'query-migrate' }"); 138 } 139 140 QDict *migrate_query_not_failed(QTestState *who) 141 { 142 const char *status; 143 QDict *rsp = migrate_query(who); 144 status = qdict_get_str(rsp, "status"); 145 if (g_str_equal(status, "failed")) { 146 g_printerr("query-migrate shows failed migration: %s\n", 147 qdict_get_str(rsp, "error-desc")); 148 } 149 g_assert(!g_str_equal(status, "failed")); 150 return rsp; 151 } 152 153 /* 154 * Note: caller is responsible to free the returned object via 155 * g_free() after use 156 */ 157 static gchar *migrate_query_status(QTestState *who) 158 { 159 QDict *rsp_return = migrate_query(who); 160 gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); 161 162 g_assert(status); 163 qobject_unref(rsp_return); 164 165 return status; 166 } 167 168 static bool check_migration_status(QTestState *who, const char *goal, 169 const char **ungoals) 170 { 171 bool ready; 172 char *current_status; 173 const char **ungoal; 174 175 current_status = migrate_query_status(who); 176 ready = strcmp(current_status, goal) == 0; 177 if (!ungoals) { 178 g_assert_cmpstr(current_status, !=, "failed"); 179 /* 180 * If looking for a state other than completed, 181 * completion of migration would cause the test to 182 * hang. 183 */ 184 if (strcmp(goal, "completed") != 0) { 185 g_assert_cmpstr(current_status, !=, "completed"); 186 } 187 } else { 188 for (ungoal = ungoals; *ungoal; ungoal++) { 189 g_assert_cmpstr(current_status, !=, *ungoal); 190 } 191 } 192 g_free(current_status); 193 return ready; 194 } 195 196 void wait_for_migration_status(QTestState *who, 197 const char *goal, const char **ungoals) 198 { 199 g_test_timer_start(); 200 while (!check_migration_status(who, goal, ungoals)) { 201 usleep(1000); 202 203 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); 204 } 205 } 206 207 void wait_for_migration_complete(QTestState *who) 208 { 209 wait_for_migration_status(who, "completed", NULL); 210 } 211 212 void wait_for_migration_fail(QTestState *from, bool allow_active) 213 { 214 g_test_timer_start(); 215 QDict *rsp_return; 216 char *status; 217 bool failed; 218 219 do { 220 status = migrate_query_status(from); 221 bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || 222 (allow_active && !strcmp(status, "active")); 223 if (!result) { 224 fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", 225 __func__, status, allow_active); 226 } 227 g_assert(result); 228 failed = !strcmp(status, "failed"); 229 g_free(status); 230 231 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); 232 } while (!failed); 233 234 /* Is the machine currently running? */ 235 rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); 236 g_assert(qdict_haskey(rsp_return, "running")); 237 g_assert(qdict_get_bool(rsp_return, "running")); 238 qobject_unref(rsp_return); 239 } 240