1 /* 2 * QTest QMP helpers for migration 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 "libqtest.h" 15 #include "migration-qmp.h" 16 #include "migration-util.h" 17 #include "qapi/error.h" 18 #include "qapi/qapi-types-migration.h" 19 #include "qapi/qapi-visit-migration.h" 20 #include "qobject/qdict.h" 21 #include "qobject/qjson.h" 22 #include "qobject/qlist.h" 23 #include "qapi/qobject-input-visitor.h" 24 #include "qapi/qobject-output-visitor.h" 25 26 /* 27 * Number of seconds we wait when looking for migration 28 * status changes, to avoid test suite hanging forever 29 * when things go wrong. Needs to be higher enough to 30 * avoid false positives on loaded hosts. 31 */ 32 #define MIGRATION_STATUS_WAIT_TIMEOUT 120 33 34 /* 35 * Wait for a "MIGRATION" event. This is what Libvirt uses to track 36 * migration status changes. 37 */ 38 void migration_event_wait(QTestState *s, const char *target) 39 { 40 QDict *response, *data; 41 const char *status; 42 bool found; 43 44 do { 45 response = qtest_qmp_eventwait_ref(s, "MIGRATION"); 46 data = qdict_get_qdict(response, "data"); 47 g_assert(data); 48 status = qdict_get_str(data, "status"); 49 found = (strcmp(status, target) == 0); 50 qobject_unref(response); 51 } while (!found); 52 } 53 54 /* 55 * Convert a string representing a single channel to an object. 56 * @str may be in JSON or dotted keys format. 57 */ 58 QObject *migrate_str_to_channel(const char *str) 59 { 60 Visitor *v; 61 MigrationChannel *channel; 62 QObject *obj; 63 64 /* Create the channel */ 65 v = qobject_input_visitor_new_str(str, "channel-type", &error_abort); 66 visit_type_MigrationChannel(v, NULL, &channel, &error_abort); 67 visit_free(v); 68 69 /* Create the object */ 70 v = qobject_output_visitor_new(&obj); 71 visit_type_MigrationChannel(v, NULL, &channel, &error_abort); 72 visit_complete(v, &obj); 73 visit_free(v); 74 75 qapi_free_MigrationChannel(channel); 76 return obj; 77 } 78 79 void migrate_qmp_fail(QTestState *who, const char *uri, 80 QObject *channels, const char *fmt, ...) 81 { 82 va_list ap; 83 QDict *args, *err; 84 85 va_start(ap, fmt); 86 args = qdict_from_vjsonf_nofail(fmt, ap); 87 va_end(ap); 88 89 g_assert(!qdict_haskey(args, "uri")); 90 if (uri) { 91 qdict_put_str(args, "uri", uri); 92 } 93 94 g_assert(!qdict_haskey(args, "channels")); 95 if (channels) { 96 qdict_put_obj(args, "channels", channels); 97 } 98 99 err = qtest_qmp_assert_failure_ref( 100 who, "{ 'execute': 'migrate', 'arguments': %p}", args); 101 102 g_assert(qdict_haskey(err, "desc")); 103 104 qobject_unref(err); 105 } 106 107 /* 108 * Send QMP command "migrate". 109 * Arguments are built from @fmt... (formatted like 110 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. 111 */ 112 void migrate_qmp(QTestState *who, QTestState *to, const char *uri, 113 QObject *channels, const char *fmt, ...) 114 { 115 va_list ap; 116 QDict *args; 117 g_autofree char *connect_uri = NULL; 118 119 va_start(ap, fmt); 120 args = qdict_from_vjsonf_nofail(fmt, ap); 121 va_end(ap); 122 123 g_assert(!qdict_haskey(args, "uri")); 124 if (uri) { 125 qdict_put_str(args, "uri", uri); 126 } else if (!channels) { 127 connect_uri = migrate_get_connect_uri(to); 128 qdict_put_str(args, "uri", connect_uri); 129 } 130 131 g_assert(!qdict_haskey(args, "channels")); 132 if (channels) { 133 QList *channel_list = qobject_to(QList, channels); 134 migrate_set_ports(to, channel_list); 135 qdict_put_obj(args, "channels", channels); 136 } 137 138 qtest_qmp_assert_success(who, 139 "{ 'execute': 'migrate', 'arguments': %p}", args); 140 } 141 142 void migrate_set_capability(QTestState *who, const char *capability, 143 bool value) 144 { 145 qtest_qmp_assert_success(who, 146 "{ 'execute': 'migrate-set-capabilities'," 147 "'arguments': { " 148 "'capabilities': [ { " 149 "'capability': %s, 'state': %i } ] } }", 150 capability, value); 151 } 152 153 void migrate_incoming_qmp(QTestState *to, const char *uri, QObject *channels, 154 const char *fmt, ...) 155 { 156 va_list ap; 157 QDict *args, *rsp; 158 159 va_start(ap, fmt); 160 args = qdict_from_vjsonf_nofail(fmt, ap); 161 va_end(ap); 162 163 g_assert(!qdict_haskey(args, "uri")); 164 if (uri) { 165 qdict_put_str(args, "uri", uri); 166 } 167 168 g_assert(!qdict_haskey(args, "channels")); 169 if (channels) { 170 qdict_put_obj(args, "channels", channels); 171 } 172 173 /* This function relies on the event to work, make sure it's enabled */ 174 migrate_set_capability(to, "events", true); 175 176 rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", 177 args); 178 179 if (!qdict_haskey(rsp, "return")) { 180 g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); 181 g_test_message("%s", s->str); 182 } 183 184 g_assert(qdict_haskey(rsp, "return")); 185 qobject_unref(rsp); 186 187 migration_event_wait(to, "setup"); 188 } 189 190 static bool check_migration_status(QTestState *who, const char *goal, 191 const char **ungoals) 192 { 193 bool ready; 194 char *current_status; 195 const char **ungoal; 196 197 current_status = migrate_query_status(who); 198 ready = strcmp(current_status, goal) == 0; 199 if (!ungoals) { 200 g_assert_cmpstr(current_status, !=, "failed"); 201 /* 202 * If looking for a state other than completed, 203 * completion of migration would cause the test to 204 * hang. 205 */ 206 if (strcmp(goal, "completed") != 0) { 207 g_assert_cmpstr(current_status, !=, "completed"); 208 } 209 } else { 210 for (ungoal = ungoals; *ungoal; ungoal++) { 211 g_assert_cmpstr(current_status, !=, *ungoal); 212 } 213 } 214 g_free(current_status); 215 return ready; 216 } 217 218 void wait_for_migration_status(QTestState *who, 219 const char *goal, const char **ungoals) 220 { 221 g_test_timer_start(); 222 while (!check_migration_status(who, goal, ungoals)) { 223 usleep(1000); 224 225 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); 226 } 227 } 228 229 void wait_for_migration_complete(QTestState *who) 230 { 231 wait_for_migration_status(who, "completed", NULL); 232 } 233 234 void wait_for_migration_fail(QTestState *from, bool allow_active) 235 { 236 g_test_timer_start(); 237 QDict *rsp_return; 238 char *status; 239 bool failed; 240 241 do { 242 status = migrate_query_status(from); 243 bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || 244 (allow_active && !strcmp(status, "active")); 245 if (!result) { 246 fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", 247 __func__, status, allow_active); 248 } 249 g_assert(result); 250 failed = !strcmp(status, "failed"); 251 g_free(status); 252 253 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); 254 } while (!failed); 255 256 /* Is the machine currently running? */ 257 rsp_return = qtest_qmp_assert_success_ref(from, 258 "{ 'execute': 'query-status' }"); 259 g_assert(qdict_haskey(rsp_return, "running")); 260 g_assert(qdict_get_bool(rsp_return, "running")); 261 qobject_unref(rsp_return); 262 } 263 264 void wait_for_stop(QTestState *who, QTestMigrationState *state) 265 { 266 if (!state->stop_seen) { 267 qtest_qmp_eventwait(who, "STOP"); 268 } 269 } 270 271 void wait_for_resume(QTestState *who, QTestMigrationState *state) 272 { 273 if (!state->resume_seen) { 274 qtest_qmp_eventwait(who, "RESUME"); 275 } 276 } 277 278 void wait_for_suspend(QTestState *who, QTestMigrationState *state) 279 { 280 if (state->suspend_me && !state->suspend_seen) { 281 qtest_qmp_eventwait(who, "SUSPEND"); 282 } 283 } 284 285 /* 286 * Note: caller is responsible to free the returned object via 287 * qobject_unref() after use 288 */ 289 QDict *migrate_query(QTestState *who) 290 { 291 return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); 292 } 293 294 QDict *migrate_query_not_failed(QTestState *who) 295 { 296 const char *status; 297 QDict *rsp = migrate_query(who); 298 status = qdict_get_str(rsp, "status"); 299 if (g_str_equal(status, "failed")) { 300 g_printerr("query-migrate shows failed migration: %s\n", 301 qdict_get_str(rsp, "error-desc")); 302 } 303 g_assert(!g_str_equal(status, "failed")); 304 return rsp; 305 } 306 307 /* 308 * Note: caller is responsible to free the returned object via 309 * g_free() after use 310 */ 311 gchar *migrate_query_status(QTestState *who) 312 { 313 QDict *rsp_return = migrate_query(who); 314 gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); 315 316 g_assert(status); 317 qobject_unref(rsp_return); 318 319 return status; 320 } 321 322 int64_t read_ram_property_int(QTestState *who, const char *property) 323 { 324 QDict *rsp_return, *rsp_ram; 325 int64_t result; 326 327 rsp_return = migrate_query_not_failed(who); 328 if (!qdict_haskey(rsp_return, "ram")) { 329 /* Still in setup */ 330 result = 0; 331 } else { 332 rsp_ram = qdict_get_qdict(rsp_return, "ram"); 333 result = qdict_get_try_int(rsp_ram, property, 0); 334 } 335 qobject_unref(rsp_return); 336 return result; 337 } 338 339 int64_t read_migrate_property_int(QTestState *who, const char *property) 340 { 341 QDict *rsp_return; 342 int64_t result; 343 344 rsp_return = migrate_query_not_failed(who); 345 result = qdict_get_try_int(rsp_return, property, 0); 346 qobject_unref(rsp_return); 347 return result; 348 } 349 350 uint64_t get_migration_pass(QTestState *who) 351 { 352 return read_ram_property_int(who, "dirty-sync-count"); 353 } 354 355 void read_blocktime(QTestState *who) 356 { 357 QDict *rsp_return; 358 359 rsp_return = migrate_query_not_failed(who); 360 g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); 361 qobject_unref(rsp_return); 362 } 363 364 /* 365 * Wait for two changes in the migration pass count, but bail if we stop. 366 */ 367 void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state) 368 { 369 uint64_t pass, prev_pass = 0, changes = 0; 370 371 while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) { 372 usleep(1000); 373 pass = get_migration_pass(who); 374 changes += (pass != prev_pass); 375 prev_pass = pass; 376 } 377 } 378 379 static long long migrate_get_parameter_int(QTestState *who, 380 const char *parameter) 381 { 382 QDict *rsp; 383 long long result; 384 385 rsp = qtest_qmp_assert_success_ref( 386 who, "{ 'execute': 'query-migrate-parameters' }"); 387 result = qdict_get_int(rsp, parameter); 388 qobject_unref(rsp); 389 return result; 390 } 391 392 static void migrate_check_parameter_int(QTestState *who, const char *parameter, 393 long long value) 394 { 395 long long result; 396 397 result = migrate_get_parameter_int(who, parameter); 398 g_assert_cmpint(result, ==, value); 399 } 400 401 void migrate_set_parameter_int(QTestState *who, const char *parameter, 402 long long value) 403 { 404 qtest_qmp_assert_success(who, 405 "{ 'execute': 'migrate-set-parameters'," 406 "'arguments': { %s: %lld } }", 407 parameter, value); 408 migrate_check_parameter_int(who, parameter, value); 409 } 410 411 static char *migrate_get_parameter_str(QTestState *who, const char *parameter) 412 { 413 QDict *rsp; 414 char *result; 415 416 rsp = qtest_qmp_assert_success_ref( 417 who, "{ 'execute': 'query-migrate-parameters' }"); 418 result = g_strdup(qdict_get_str(rsp, parameter)); 419 qobject_unref(rsp); 420 return result; 421 } 422 423 static void migrate_check_parameter_str(QTestState *who, const char *parameter, 424 const char *value) 425 { 426 g_autofree char *result = migrate_get_parameter_str(who, parameter); 427 g_assert_cmpstr(result, ==, value); 428 } 429 430 void migrate_set_parameter_str(QTestState *who, const char *parameter, 431 const char *value) 432 { 433 qtest_qmp_assert_success(who, 434 "{ 'execute': 'migrate-set-parameters'," 435 "'arguments': { %s: %s } }", 436 parameter, value); 437 migrate_check_parameter_str(who, parameter, value); 438 } 439 440 static long long migrate_get_parameter_bool(QTestState *who, 441 const char *parameter) 442 { 443 QDict *rsp; 444 int result; 445 446 rsp = qtest_qmp_assert_success_ref( 447 who, "{ 'execute': 'query-migrate-parameters' }"); 448 result = qdict_get_bool(rsp, parameter); 449 qobject_unref(rsp); 450 return !!result; 451 } 452 453 static void migrate_check_parameter_bool(QTestState *who, const char *parameter, 454 int value) 455 { 456 int result; 457 458 result = migrate_get_parameter_bool(who, parameter); 459 g_assert_cmpint(result, ==, value); 460 } 461 462 void migrate_set_parameter_bool(QTestState *who, const char *parameter, 463 int value) 464 { 465 qtest_qmp_assert_success(who, 466 "{ 'execute': 'migrate-set-parameters'," 467 "'arguments': { %s: %i } }", 468 parameter, value); 469 migrate_check_parameter_bool(who, parameter, value); 470 } 471 472 void migrate_ensure_non_converge(QTestState *who) 473 { 474 /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ 475 migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); 476 migrate_set_parameter_int(who, "downtime-limit", 1); 477 } 478 479 void migrate_ensure_converge(QTestState *who) 480 { 481 /* Should converge with 30s downtime + 1 gbs bandwidth limit */ 482 migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); 483 migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); 484 } 485 486 void migrate_pause(QTestState *who) 487 { 488 qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); 489 } 490 491 void migrate_continue(QTestState *who, const char *state) 492 { 493 qtest_qmp_assert_success(who, 494 "{ 'execute': 'migrate-continue'," 495 " 'arguments': { 'state': %s } }", 496 state); 497 } 498 499 void migrate_recover(QTestState *who, const char *uri) 500 { 501 qtest_qmp_assert_success(who, 502 "{ 'exec-oob': 'migrate-recover', " 503 " 'id': 'recover-cmd', " 504 " 'arguments': { 'uri': %s } }", 505 uri); 506 } 507 508 void migrate_cancel(QTestState *who) 509 { 510 qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); 511 } 512 513 void migrate_postcopy_start(QTestState *from, QTestState *to, 514 QTestMigrationState *src_state) 515 { 516 qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); 517 518 wait_for_stop(from, src_state); 519 qtest_qmp_eventwait(to, "RESUME"); 520 } 521