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