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 "qemu/ctype.h" 15 #include "qapi/qmp/qjson.h" 16 #include "qapi/qapi-visit-sockets.h" 17 #include "qapi/qobject-input-visitor.h" 18 #include "qapi/error.h" 19 20 #include "migration-helpers.h" 21 22 /* 23 * Number of seconds we wait when looking for migration 24 * status changes, to avoid test suite hanging forever 25 * when things go wrong. Needs to be higher enough to 26 * avoid false positives on loaded hosts. 27 */ 28 #define MIGRATION_STATUS_WAIT_TIMEOUT 120 29 30 static char *SocketAddress_to_str(SocketAddress *addr) 31 { 32 switch (addr->type) { 33 case SOCKET_ADDRESS_TYPE_INET: 34 return g_strdup_printf("tcp:%s:%s", 35 addr->u.inet.host, 36 addr->u.inet.port); 37 case SOCKET_ADDRESS_TYPE_UNIX: 38 return g_strdup_printf("unix:%s", 39 addr->u.q_unix.path); 40 case SOCKET_ADDRESS_TYPE_FD: 41 return g_strdup_printf("fd:%s", addr->u.fd.str); 42 case SOCKET_ADDRESS_TYPE_VSOCK: 43 return g_strdup_printf("tcp:%s:%s", 44 addr->u.vsock.cid, 45 addr->u.vsock.port); 46 default: 47 return g_strdup("unknown address type"); 48 } 49 } 50 51 static SocketAddress *migrate_get_socket_address(QTestState *who) 52 { 53 QDict *rsp; 54 SocketAddressList *addrs; 55 SocketAddress *addr; 56 Visitor *iv = NULL; 57 QObject *object; 58 59 rsp = migrate_query(who); 60 object = qdict_get(rsp, "socket-address"); 61 62 iv = qobject_input_visitor_new(object); 63 visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); 64 addr = addrs->value; 65 visit_free(iv); 66 67 qobject_unref(rsp); 68 return addr; 69 } 70 71 static char * 72 migrate_get_connect_uri(QTestState *who) 73 { 74 SocketAddress *addrs; 75 char *connect_uri; 76 77 addrs = migrate_get_socket_address(who); 78 connect_uri = SocketAddress_to_str(addrs); 79 80 qapi_free_SocketAddress(addrs); 81 return connect_uri; 82 } 83 84 bool migrate_watch_for_events(QTestState *who, const char *name, 85 QDict *event, void *opaque) 86 { 87 QTestMigrationState *state = opaque; 88 89 if (g_str_equal(name, "STOP")) { 90 state->stop_seen = true; 91 return true; 92 } else if (g_str_equal(name, "SUSPEND")) { 93 state->suspend_seen = true; 94 return true; 95 } else if (g_str_equal(name, "RESUME")) { 96 state->resume_seen = true; 97 return true; 98 } 99 100 return false; 101 } 102 103 void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...) 104 { 105 va_list ap; 106 QDict *args, *err; 107 108 va_start(ap, fmt); 109 args = qdict_from_vjsonf_nofail(fmt, ap); 110 va_end(ap); 111 112 g_assert(!qdict_haskey(args, "uri")); 113 qdict_put_str(args, "uri", uri); 114 115 err = qtest_qmp_assert_failure_ref( 116 who, "{ 'execute': 'migrate', 'arguments': %p}", args); 117 118 g_assert(qdict_haskey(err, "desc")); 119 120 qobject_unref(err); 121 } 122 123 /* 124 * Send QMP command "migrate". 125 * Arguments are built from @fmt... (formatted like 126 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. 127 */ 128 void migrate_qmp(QTestState *who, QTestState *to, const char *uri, 129 const char *fmt, ...) 130 { 131 va_list ap; 132 QDict *args; 133 g_autofree char *connect_uri = NULL; 134 135 va_start(ap, fmt); 136 args = qdict_from_vjsonf_nofail(fmt, ap); 137 va_end(ap); 138 139 g_assert(!qdict_haskey(args, "uri")); 140 if (!uri) { 141 connect_uri = migrate_get_connect_uri(to); 142 } 143 qdict_put_str(args, "uri", uri ? uri : connect_uri); 144 145 qtest_qmp_assert_success(who, 146 "{ 'execute': 'migrate', 'arguments': %p}", args); 147 } 148 149 void migrate_set_capability(QTestState *who, const char *capability, 150 bool value) 151 { 152 qtest_qmp_assert_success(who, 153 "{ 'execute': 'migrate-set-capabilities'," 154 "'arguments': { " 155 "'capabilities': [ { " 156 "'capability': %s, 'state': %i } ] } }", 157 capability, value); 158 } 159 160 void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) 161 { 162 va_list ap; 163 QDict *args, *rsp, *data; 164 165 va_start(ap, fmt); 166 args = qdict_from_vjsonf_nofail(fmt, ap); 167 va_end(ap); 168 169 g_assert(!qdict_haskey(args, "uri")); 170 qdict_put_str(args, "uri", uri); 171 172 migrate_set_capability(to, "events", true); 173 174 rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", 175 args); 176 177 if (!qdict_haskey(rsp, "return")) { 178 g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); 179 g_test_message("%s", s->str); 180 } 181 182 g_assert(qdict_haskey(rsp, "return")); 183 qobject_unref(rsp); 184 185 rsp = qtest_qmp_eventwait_ref(to, "MIGRATION"); 186 g_assert(qdict_haskey(rsp, "data")); 187 188 data = qdict_get_qdict(rsp, "data"); 189 g_assert(qdict_haskey(data, "status")); 190 g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup"); 191 192 qobject_unref(rsp); 193 } 194 195 /* 196 * Note: caller is responsible to free the returned object via 197 * qobject_unref() after use 198 */ 199 QDict *migrate_query(QTestState *who) 200 { 201 return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); 202 } 203 204 QDict *migrate_query_not_failed(QTestState *who) 205 { 206 const char *status; 207 QDict *rsp = migrate_query(who); 208 status = qdict_get_str(rsp, "status"); 209 if (g_str_equal(status, "failed")) { 210 g_printerr("query-migrate shows failed migration: %s\n", 211 qdict_get_str(rsp, "error-desc")); 212 } 213 g_assert(!g_str_equal(status, "failed")); 214 return rsp; 215 } 216 217 /* 218 * Note: caller is responsible to free the returned object via 219 * g_free() after use 220 */ 221 static gchar *migrate_query_status(QTestState *who) 222 { 223 QDict *rsp_return = migrate_query(who); 224 gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); 225 226 g_assert(status); 227 qobject_unref(rsp_return); 228 229 return status; 230 } 231 232 static bool check_migration_status(QTestState *who, const char *goal, 233 const char **ungoals) 234 { 235 bool ready; 236 char *current_status; 237 const char **ungoal; 238 239 current_status = migrate_query_status(who); 240 ready = strcmp(current_status, goal) == 0; 241 if (!ungoals) { 242 g_assert_cmpstr(current_status, !=, "failed"); 243 /* 244 * If looking for a state other than completed, 245 * completion of migration would cause the test to 246 * hang. 247 */ 248 if (strcmp(goal, "completed") != 0) { 249 g_assert_cmpstr(current_status, !=, "completed"); 250 } 251 } else { 252 for (ungoal = ungoals; *ungoal; ungoal++) { 253 g_assert_cmpstr(current_status, !=, *ungoal); 254 } 255 } 256 g_free(current_status); 257 return ready; 258 } 259 260 void wait_for_migration_status(QTestState *who, 261 const char *goal, const char **ungoals) 262 { 263 g_test_timer_start(); 264 while (!check_migration_status(who, goal, ungoals)) { 265 usleep(1000); 266 267 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); 268 } 269 } 270 271 void wait_for_migration_complete(QTestState *who) 272 { 273 wait_for_migration_status(who, "completed", NULL); 274 } 275 276 void wait_for_migration_fail(QTestState *from, bool allow_active) 277 { 278 g_test_timer_start(); 279 QDict *rsp_return; 280 char *status; 281 bool failed; 282 283 do { 284 status = migrate_query_status(from); 285 bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || 286 (allow_active && !strcmp(status, "active")); 287 if (!result) { 288 fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", 289 __func__, status, allow_active); 290 } 291 g_assert(result); 292 failed = !strcmp(status, "failed"); 293 g_free(status); 294 295 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); 296 } while (!failed); 297 298 /* Is the machine currently running? */ 299 rsp_return = qtest_qmp_assert_success_ref(from, 300 "{ 'execute': 'query-status' }"); 301 g_assert(qdict_haskey(rsp_return, "running")); 302 g_assert(qdict_get_bool(rsp_return, "running")); 303 qobject_unref(rsp_return); 304 } 305 306 char *find_common_machine_version(const char *mtype, const char *var1, 307 const char *var2) 308 { 309 g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); 310 g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); 311 312 g_assert(type1 && type2); 313 314 if (g_str_equal(type1, type2)) { 315 /* either can be used */ 316 return g_strdup(type1); 317 } 318 319 if (qtest_has_machine_with_env(var2, type1)) { 320 return g_strdup(type1); 321 } 322 323 if (qtest_has_machine_with_env(var1, type2)) { 324 return g_strdup(type2); 325 } 326 327 g_test_message("No common machine version for machine type '%s' between " 328 "binaries %s and %s", mtype, getenv(var1), getenv(var2)); 329 g_assert_not_reached(); 330 } 331 332 char *resolve_machine_version(const char *alias, const char *var1, 333 const char *var2) 334 { 335 const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); 336 g_autofree char *machine_name = NULL; 337 338 if (mname) { 339 const char *dash = strrchr(mname, '-'); 340 const char *dot = strrchr(mname, '.'); 341 342 machine_name = g_strdup(mname); 343 344 if (dash && dot) { 345 assert(qtest_has_machine(machine_name)); 346 return g_steal_pointer(&machine_name); 347 } 348 /* else: probably an alias, let it be resolved below */ 349 } else { 350 /* use the hardcoded alias */ 351 machine_name = g_strdup(alias); 352 } 353 354 return find_common_machine_version(machine_name, var1, var2); 355 } 356 357 typedef struct { 358 char *name; 359 void (*func)(void); 360 } MigrationTest; 361 362 static void migration_test_destroy(gpointer data) 363 { 364 MigrationTest *test = (MigrationTest *)data; 365 366 g_free(test->name); 367 g_free(test); 368 } 369 370 static void migration_test_wrapper(const void *data) 371 { 372 MigrationTest *test = (MigrationTest *)data; 373 374 g_test_message("Running /%s%s", qtest_get_arch(), test->name); 375 test->func(); 376 } 377 378 void migration_test_add(const char *path, void (*fn)(void)) 379 { 380 MigrationTest *test = g_new0(MigrationTest, 1); 381 382 test->func = fn; 383 test->name = g_strdup(path); 384 385 qtest_add_data_func_full(path, test, migration_test_wrapper, 386 migration_test_destroy); 387 } 388