xref: /qemu/tests/qtest/migration/migration-qmp.c (revision 513823e7521a09ed7ad1e32e6454bac3b2cbf52d)
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