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 */
migration_event_wait(QTestState * s,const char * target)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 */
migrate_str_to_channel(const char * str)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
migrate_qmp_fail(QTestState * who,const char * uri,QObject * channels,const char * fmt,...)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 */
migrate_qmp(QTestState * who,QTestState * to,const char * uri,QObject * channels,const char * fmt,...)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
migrate_set_capability(QTestState * who,const char * capability,bool value)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
migrate_incoming_qmp(QTestState * to,const char * uri,QObject * channels,const char * fmt,...)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
check_migration_status(QTestState * who,const char * goal,const char ** ungoals)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
wait_for_migration_status(QTestState * who,const char * goal,const char ** ungoals)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
wait_for_migration_complete(QTestState * who)229 void wait_for_migration_complete(QTestState *who)
230 {
231 wait_for_migration_status(who, "completed", NULL);
232 }
233
wait_for_migration_fail(QTestState * from,bool allow_active)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
wait_for_stop(QTestState * who,QTestMigrationState * state)264 void wait_for_stop(QTestState *who, QTestMigrationState *state)
265 {
266 if (!state->stop_seen) {
267 qtest_qmp_eventwait(who, "STOP");
268 }
269 }
270
wait_for_resume(QTestState * who,QTestMigrationState * state)271 void wait_for_resume(QTestState *who, QTestMigrationState *state)
272 {
273 if (!state->resume_seen) {
274 qtest_qmp_eventwait(who, "RESUME");
275 }
276 }
277
wait_for_suspend(QTestState * who,QTestMigrationState * state)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 */
migrate_query(QTestState * who)289 QDict *migrate_query(QTestState *who)
290 {
291 return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
292 }
293
migrate_query_not_failed(QTestState * who)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 */
migrate_query_status(QTestState * who)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
read_ram_property_int(QTestState * who,const char * property)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
read_migrate_property_int(QTestState * who,const char * property)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
get_migration_pass(QTestState * who)350 uint64_t get_migration_pass(QTestState *who)
351 {
352 return read_ram_property_int(who, "dirty-sync-count");
353 }
354
read_blocktime(QTestState * who)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 */
wait_for_migration_pass(QTestState * who,QTestMigrationState * src_state)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
migrate_get_parameter_int(QTestState * who,const char * parameter)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
migrate_check_parameter_int(QTestState * who,const char * parameter,long long value)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
migrate_set_parameter_int(QTestState * who,const char * parameter,long long value)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
migrate_get_parameter_str(QTestState * who,const char * parameter)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
migrate_check_parameter_str(QTestState * who,const char * parameter,const char * value)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
migrate_set_parameter_str(QTestState * who,const char * parameter,const char * value)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
migrate_get_parameter_bool(QTestState * who,const char * parameter)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
migrate_check_parameter_bool(QTestState * who,const char * parameter,int value)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
migrate_set_parameter_bool(QTestState * who,const char * parameter,int value)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
migrate_ensure_non_converge(QTestState * who)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
migrate_ensure_converge(QTestState * who)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
migrate_pause(QTestState * who)486 void migrate_pause(QTestState *who)
487 {
488 qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }");
489 }
490
migrate_continue(QTestState * who,const char * state)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
migrate_recover(QTestState * who,const char * uri)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
migrate_cancel(QTestState * who)508 void migrate_cancel(QTestState *who)
509 {
510 qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }");
511 }
512
migrate_postcopy_start(QTestState * from,QTestState * to,QTestMigrationState * src_state)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