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