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