master
c 558 lines 16.1 KB
Raw
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 /*
177 * Set the incoming migration to never exit QEMU abruptly during
178 * the tests. It causes issues when running sanitizers and
179 * expecting a failure exit code can mask other issues.
180 */
181 g_assert(!qdict_haskey(args, "exit-on-error"));
182 qdict_put_bool(args, "exit-on-error", false);
183 rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
184 args);
185
186 if (!qdict_haskey(rsp, "return")) {
187 g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
188 g_test_message("%s", s->str);
189 }
190
191 g_assert(qdict_haskey(rsp, "return"));
192 qobject_unref(rsp);
193
194 migration_event_wait(to, "setup");
195 }
196
197 static bool check_migration_status(QTestState *who, const char *goal,
198 const char **ungoals)
199 {
200 bool ready;
201 char *current_status;
202 const char **ungoal;
203
204 current_status = migrate_query_status(who);
205 ready = strcmp(current_status, goal) == 0;
206 if (!ungoals) {
207 g_assert_cmpstr(current_status, !=, "failed");
208 /*
209 * If looking for a state other than completed,
210 * completion of migration would cause the test to
211 * hang.
212 */
213 if (strcmp(goal, "completed") != 0) {
214 g_assert_cmpstr(current_status, !=, "completed");
215 }
216 } else {
217 for (ungoal = ungoals; *ungoal; ungoal++) {
218 g_assert_cmpstr(current_status, !=, *ungoal);
219 }
220 }
221 g_free(current_status);
222 return ready;
223 }
224
225 void wait_for_migration_status(QTestState *who,
226 const char *goal, const char **ungoals)
227 {
228 g_test_timer_start();
229 while (!check_migration_status(who, goal, ungoals)) {
230 usleep(1000);
231
232 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
233 }
234 }
235
236 void wait_for_migration_complete(QTestState *who)
237 {
238 wait_for_migration_status(who, "completed", NULL);
239 }
240
241 void wait_for_migration_fail(QTestState *from, bool allow_active)
242 {
243 g_test_timer_start();
244 QDict *rsp_return;
245 char *status;
246 bool failed;
247
248 do {
249 status = migrate_query_status(from);
250 bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
251 (allow_active && !strcmp(status, "active")) ||
252 !strcmp(status, "failing");
253 if (!result) {
254 fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
255 __func__, status, allow_active);
256 }
257 g_assert(result);
258 failed = !strcmp(status, "failed");
259 g_free(status);
260
261 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
262 } while (!failed);
263
264 /* Is the machine currently running? */
265 rsp_return = qtest_qmp_assert_success_ref(from,
266 "{ 'execute': 'query-status' }");
267 g_assert(qdict_haskey(rsp_return, "running"));
268 g_assert(qdict_get_bool(rsp_return, "running"));
269 qobject_unref(rsp_return);
270 }
271
272 void wait_for_stop(QTestState *who, QTestMigrationState *state)
273 {
274 if (!state->stop_seen) {
275 qtest_qmp_eventwait(who, "STOP");
276 }
277 }
278
279 void wait_for_resume(QTestState *who, QTestMigrationState *state)
280 {
281 if (!state->resume_seen) {
282 qtest_qmp_eventwait(who, "RESUME");
283 }
284 }
285
286 void wait_for_suspend(QTestState *who, QTestMigrationState *state)
287 {
288 if (state->suspend_me && !state->suspend_seen) {
289 qtest_qmp_eventwait(who, "SUSPEND");
290 }
291 }
292
293 /*
294 * Note: caller is responsible to free the returned object via
295 * qobject_unref() after use
296 */
297 QDict *migrate_query(QTestState *who)
298 {
299 return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
300 }
301
302 QDict *migrate_query_not_failed(QTestState *who)
303 {
304 const char *status;
305 QDict *rsp = migrate_query(who);
306 status = qdict_get_str(rsp, "status");
307 if (g_str_equal(status, "failed")) {
308 g_printerr("query-migrate shows failed migration: %s\n",
309 qdict_get_str(rsp, "error-desc"));
310 }
311 g_assert(!g_str_equal(status, "failed"));
312 return rsp;
313 }
314
315 /*
316 * Note: caller is responsible to free the returned object via
317 * g_free() after use
318 */
319 gchar *migrate_query_status(QTestState *who)
320 {
321 QDict *rsp_return = migrate_query(who);
322 gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
323
324 g_assert(status);
325 qobject_unref(rsp_return);
326
327 return status;
328 }
329
330 int64_t read_ram_property_int(QTestState *who, const char *property)
331 {
332 QDict *rsp_return, *rsp_ram;
333 int64_t result;
334
335 rsp_return = migrate_query_not_failed(who);
336 if (!qdict_haskey(rsp_return, "ram")) {
337 /* Still in setup */
338 result = 0;
339 } else {
340 rsp_ram = qdict_get_qdict(rsp_return, "ram");
341 result = qdict_get_try_int(rsp_ram, property, 0);
342 }
343 qobject_unref(rsp_return);
344 return result;
345 }
346
347 int64_t read_migrate_property_int(QTestState *who, const char *property)
348 {
349 QDict *rsp_return;
350 int64_t result;
351
352 rsp_return = migrate_query_not_failed(who);
353 result = qdict_get_try_int(rsp_return, property, 0);
354 qobject_unref(rsp_return);
355 return result;
356 }
357
358 uint64_t get_migration_pass(QTestState *who)
359 {
360 return read_ram_property_int(who, "dirty-sync-count");
361 }
362
363 void read_blocktime(QTestState *who)
364 {
365 QDict *rsp_return;
366
367 rsp_return = migrate_query_not_failed(who);
368 g_assert(qdict_haskey(rsp_return, "postcopy-blocktime"));
369 g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-blocktime"));
370 g_assert(qdict_haskey(rsp_return, "postcopy-latency"));
371 g_assert(qdict_haskey(rsp_return, "postcopy-latency-dist"));
372 g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-latency"));
373 g_assert(qdict_haskey(rsp_return, "postcopy-non-vcpu-latency"));
374 qobject_unref(rsp_return);
375 }
376
377 /*
378 * Wait for two changes in the migration pass count, but bail if we stop.
379 */
380 void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state)
381 {
382 uint64_t pass, prev_pass = 0, changes = 0;
383
384 while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) {
385 usleep(1000);
386 pass = get_migration_pass(who);
387 changes += (pass != prev_pass);
388 prev_pass = pass;
389 }
390 }
391
392 static long long migrate_get_parameter_int(QTestState *who,
393 const char *parameter)
394 {
395 QDict *rsp;
396 long long result;
397
398 rsp = qtest_qmp_assert_success_ref(
399 who, "{ 'execute': 'query-migrate-parameters' }");
400 result = qdict_get_int(rsp, parameter);
401 qobject_unref(rsp);
402 return result;
403 }
404
405 static void migrate_check_parameter_int(QTestState *who, const char *parameter,
406 long long value)
407 {
408 long long result;
409
410 result = migrate_get_parameter_int(who, parameter);
411 g_assert_cmpint(result, ==, value);
412 }
413
414 void migrate_set_parameter_int(QTestState *who, const char *parameter,
415 long long value)
416 {
417 qtest_qmp_assert_success(who,
418 "{ 'execute': 'migrate-set-parameters',"
419 "'arguments': { %s: %lld } }",
420 parameter, value);
421 migrate_check_parameter_int(who, parameter, value);
422 }
423
424 static char *migrate_get_parameter_str(QTestState *who, const char *parameter)
425 {
426 QDict *rsp;
427 char *result;
428
429 rsp = qtest_qmp_assert_success_ref(
430 who, "{ 'execute': 'query-migrate-parameters' }");
431 result = g_strdup(qdict_get_str(rsp, parameter));
432 qobject_unref(rsp);
433 return result;
434 }
435
436 static void migrate_check_parameter_str(QTestState *who, const char *parameter,
437 const char *value)
438 {
439 g_autofree char *result = migrate_get_parameter_str(who, parameter);
440 g_assert_cmpstr(result, ==, value);
441 }
442
443 void migrate_set_parameter_str(QTestState *who, const char *parameter,
444 const char *value)
445 {
446 qtest_qmp_assert_success(who,
447 "{ 'execute': 'migrate-set-parameters',"
448 "'arguments': { %s: %s } }",
449 parameter, value);
450 migrate_check_parameter_str(who, parameter, value);
451 }
452
453 void migrate_set_parameter_strv(QTestState *who, const char *parameter,
454 char **strv)
455 {
456 g_autofree char *args = g_strjoinv("\",\"", strv);
457 g_autoptr(GString) value = g_string_new("");
458 g_autofree char *command = NULL;
459
460 g_string_printf(value, "\"%s\"", args);
461
462 command = g_strdup_printf("{ 'execute': 'migrate-set-parameters',"
463 "'arguments': { %%s: [ %s ]}}",
464 value->str);
465
466 qtest_qmp_assert_success(who, command, parameter);
467 }
468
469 void migrate_set_parameter_null(QTestState *who, const char *parameter)
470 {
471 qtest_qmp_assert_success(who,
472 "{ 'execute': 'migrate-set-parameters',"
473 "'arguments': { %s: null } }",
474 parameter);
475 migrate_check_parameter_str(who, parameter, "");
476 }
477
478 static long long migrate_get_parameter_bool(QTestState *who,
479 const char *parameter)
480 {
481 QDict *rsp;
482 int result;
483
484 rsp = qtest_qmp_assert_success_ref(
485 who, "{ 'execute': 'query-migrate-parameters' }");
486 result = qdict_get_bool(rsp, parameter);
487 qobject_unref(rsp);
488 return !!result;
489 }
490
491 static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
492 int value)
493 {
494 int result;
495
496 result = migrate_get_parameter_bool(who, parameter);
497 g_assert_cmpint(result, ==, value);
498 }
499
500 void migrate_set_parameter_bool(QTestState *who, const char *parameter,
501 int value)
502 {
503 qtest_qmp_assert_success(who,
504 "{ 'execute': 'migrate-set-parameters',"
505 "'arguments': { %s: %i } }",
506 parameter, value);
507 migrate_check_parameter_bool(who, parameter, value);
508 }
509
510 void migrate_ensure_non_converge(QTestState *who)
511 {
512 /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */
513 migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000);
514 migrate_set_parameter_int(who, "downtime-limit", 1);
515 }
516
517 void migrate_ensure_converge(QTestState *who)
518 {
519 /* Should converge with 30s downtime + 1 gbs bandwidth limit */
520 migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000);
521 migrate_set_parameter_int(who, "downtime-limit", 30 * 1000);
522 }
523
524 void migrate_pause(QTestState *who)
525 {
526 qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }");
527 }
528
529 void migrate_continue(QTestState *who, const char *state)
530 {
531 qtest_qmp_assert_success(who,
532 "{ 'execute': 'migrate-continue',"
533 " 'arguments': { 'state': %s } }",
534 state);
535 }
536
537 void migrate_recover(QTestState *who, const char *uri)
538 {
539 qtest_qmp_assert_success(who,
540 "{ 'exec-oob': 'migrate-recover', "
541 " 'id': 'recover-cmd', "
542 " 'arguments': { 'uri': %s } }",
543 uri);
544 }
545
546 void migrate_cancel(QTestState *who)
547 {
548 qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }");
549 }
550
551 void migrate_postcopy_start(QTestState *from, QTestState *to,
552 QTestMigrationState *src_state)
553 {
554 qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }");
555
556 wait_for_stop(from, src_state);
557 qtest_qmp_eventwait(to, "RESUME");
558 }