xref: /qemu/tests/qtest/migration-helpers.c (revision b4358ed4)
1 /*
2  * QTest migration helpers
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 "qemu/ctype.h"
15 #include "qapi/qmp/qjson.h"
16 #include "qapi/qapi-visit-sockets.h"
17 #include "qapi/qobject-input-visitor.h"
18 #include "qapi/error.h"
19 #include "qapi/qmp/qlist.h"
20 #include "qemu/cutils.h"
21 #include "qemu/memalign.h"
22 
23 #include "migration-helpers.h"
24 
25 /*
26  * Number of seconds we wait when looking for migration
27  * status changes, to avoid test suite hanging forever
28  * when things go wrong. Needs to be higher enough to
29  * avoid false positives on loaded hosts.
30  */
31 #define MIGRATION_STATUS_WAIT_TIMEOUT 120
32 
33 static char *SocketAddress_to_str(SocketAddress *addr)
34 {
35     switch (addr->type) {
36     case SOCKET_ADDRESS_TYPE_INET:
37         return g_strdup_printf("tcp:%s:%s",
38                                addr->u.inet.host,
39                                addr->u.inet.port);
40     case SOCKET_ADDRESS_TYPE_UNIX:
41         return g_strdup_printf("unix:%s",
42                                addr->u.q_unix.path);
43     case SOCKET_ADDRESS_TYPE_FD:
44         return g_strdup_printf("fd:%s", addr->u.fd.str);
45     case SOCKET_ADDRESS_TYPE_VSOCK:
46         return g_strdup_printf("vsock:%s:%s",
47                                addr->u.vsock.cid,
48                                addr->u.vsock.port);
49     default:
50         return g_strdup("unknown address type");
51     }
52 }
53 
54 static QDict *SocketAddress_to_qdict(SocketAddress *addr)
55 {
56     QDict *dict = qdict_new();
57 
58     switch (addr->type) {
59     case SOCKET_ADDRESS_TYPE_INET:
60         qdict_put_str(dict, "type", "inet");
61         qdict_put_str(dict, "host", addr->u.inet.host);
62         qdict_put_str(dict, "port", addr->u.inet.port);
63         break;
64     case SOCKET_ADDRESS_TYPE_UNIX:
65         qdict_put_str(dict, "type", "unix");
66         qdict_put_str(dict, "path", addr->u.q_unix.path);
67         break;
68     case SOCKET_ADDRESS_TYPE_FD:
69         qdict_put_str(dict, "type", "fd");
70         qdict_put_str(dict, "str", addr->u.fd.str);
71         break;
72     case SOCKET_ADDRESS_TYPE_VSOCK:
73         qdict_put_str(dict, "type", "vsock");
74         qdict_put_str(dict, "cid", addr->u.vsock.cid);
75         qdict_put_str(dict, "port", addr->u.vsock.port);
76         break;
77     default:
78         g_assert_not_reached();
79         break;
80     }
81 
82     return dict;
83 }
84 
85 static SocketAddressList *migrate_get_socket_address(QTestState *who)
86 {
87     QDict *rsp;
88     SocketAddressList *addrs;
89     Visitor *iv = NULL;
90     QObject *object;
91 
92     rsp = migrate_query(who);
93     object = qdict_get(rsp, "socket-address");
94 
95     iv = qobject_input_visitor_new(object);
96     visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort);
97     visit_free(iv);
98 
99     qobject_unref(rsp);
100     return addrs;
101 }
102 
103 static char *
104 migrate_get_connect_uri(QTestState *who)
105 {
106     SocketAddressList *addrs;
107     char *connect_uri;
108 
109     addrs = migrate_get_socket_address(who);
110     connect_uri = SocketAddress_to_str(addrs->value);
111 
112     qapi_free_SocketAddressList(addrs);
113     return connect_uri;
114 }
115 
116 static QDict *
117 migrate_get_connect_qdict(QTestState *who)
118 {
119     SocketAddressList *addrs;
120     QDict *connect_qdict;
121 
122     addrs = migrate_get_socket_address(who);
123     connect_qdict = SocketAddress_to_qdict(addrs->value);
124 
125     qapi_free_SocketAddressList(addrs);
126     return connect_qdict;
127 }
128 
129 static void migrate_set_ports(QTestState *to, QList *channel_list)
130 {
131     QDict *addr;
132     QListEntry *entry;
133     const char *addr_port = NULL;
134 
135     addr = migrate_get_connect_qdict(to);
136 
137     QLIST_FOREACH_ENTRY(channel_list, entry) {
138         QDict *channel = qobject_to(QDict, qlist_entry_obj(entry));
139         QDict *addrdict = qdict_get_qdict(channel, "addr");
140 
141         if (qdict_haskey(addrdict, "port") &&
142             qdict_haskey(addr, "port") &&
143             (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) {
144                 addr_port = qdict_get_str(addr, "port");
145                 qdict_put_str(addrdict, "port", addr_port);
146         }
147     }
148 
149     qobject_unref(addr);
150 }
151 
152 bool migrate_watch_for_events(QTestState *who, const char *name,
153                               QDict *event, void *opaque)
154 {
155     QTestMigrationState *state = opaque;
156 
157     if (g_str_equal(name, "STOP")) {
158         state->stop_seen = true;
159         return true;
160     } else if (g_str_equal(name, "SUSPEND")) {
161         state->suspend_seen = true;
162         return true;
163     } else if (g_str_equal(name, "RESUME")) {
164         state->resume_seen = true;
165         return true;
166     }
167 
168     return false;
169 }
170 
171 void migrate_qmp_fail(QTestState *who, const char *uri,
172                       const char *channels, const char *fmt, ...)
173 {
174     va_list ap;
175     QDict *args, *err;
176 
177     va_start(ap, fmt);
178     args = qdict_from_vjsonf_nofail(fmt, ap);
179     va_end(ap);
180 
181     g_assert(!qdict_haskey(args, "uri"));
182     if (uri) {
183         qdict_put_str(args, "uri", uri);
184     }
185 
186     g_assert(!qdict_haskey(args, "channels"));
187     if (channels) {
188         QObject *channels_obj = qobject_from_json(channels, &error_abort);
189         qdict_put_obj(args, "channels", channels_obj);
190     }
191 
192     err = qtest_qmp_assert_failure_ref(
193         who, "{ 'execute': 'migrate', 'arguments': %p}", args);
194 
195     g_assert(qdict_haskey(err, "desc"));
196 
197     qobject_unref(err);
198 }
199 
200 /*
201  * Send QMP command "migrate".
202  * Arguments are built from @fmt... (formatted like
203  * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
204  */
205 void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
206                  const char *channels, const char *fmt, ...)
207 {
208     va_list ap;
209     QDict *args;
210     g_autofree char *connect_uri = NULL;
211 
212     va_start(ap, fmt);
213     args = qdict_from_vjsonf_nofail(fmt, ap);
214     va_end(ap);
215 
216     g_assert(!qdict_haskey(args, "uri"));
217     if (uri) {
218         qdict_put_str(args, "uri", uri);
219     } else if (!channels) {
220         connect_uri = migrate_get_connect_uri(to);
221         qdict_put_str(args, "uri", connect_uri);
222     }
223 
224     g_assert(!qdict_haskey(args, "channels"));
225     if (channels) {
226         QObject *channels_obj = qobject_from_json(channels, &error_abort);
227         QList *channel_list = qobject_to(QList, channels_obj);
228         migrate_set_ports(to, channel_list);
229         qdict_put_obj(args, "channels", channels_obj);
230     }
231 
232     qtest_qmp_assert_success(who,
233                              "{ 'execute': 'migrate', 'arguments': %p}", args);
234 }
235 
236 void migrate_set_capability(QTestState *who, const char *capability,
237                             bool value)
238 {
239     qtest_qmp_assert_success(who,
240                              "{ 'execute': 'migrate-set-capabilities',"
241                              "'arguments': { "
242                              "'capabilities': [ { "
243                              "'capability': %s, 'state': %i } ] } }",
244                              capability, value);
245 }
246 
247 void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
248 {
249     va_list ap;
250     QDict *args, *rsp;
251 
252     va_start(ap, fmt);
253     args = qdict_from_vjsonf_nofail(fmt, ap);
254     va_end(ap);
255 
256     g_assert(!qdict_haskey(args, "uri"));
257     qdict_put_str(args, "uri", uri);
258 
259     /* This function relies on the event to work, make sure it's enabled */
260     migrate_set_capability(to, "events", true);
261 
262     rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
263                     args);
264 
265     if (!qdict_haskey(rsp, "return")) {
266         g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
267         g_test_message("%s", s->str);
268     }
269 
270     g_assert(qdict_haskey(rsp, "return"));
271     qobject_unref(rsp);
272 
273     migration_event_wait(to, "setup");
274 }
275 
276 /*
277  * Note: caller is responsible to free the returned object via
278  * qobject_unref() after use
279  */
280 QDict *migrate_query(QTestState *who)
281 {
282     return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
283 }
284 
285 QDict *migrate_query_not_failed(QTestState *who)
286 {
287     const char *status;
288     QDict *rsp = migrate_query(who);
289     status = qdict_get_str(rsp, "status");
290     if (g_str_equal(status, "failed")) {
291         g_printerr("query-migrate shows failed migration: %s\n",
292                    qdict_get_str(rsp, "error-desc"));
293     }
294     g_assert(!g_str_equal(status, "failed"));
295     return rsp;
296 }
297 
298 /*
299  * Note: caller is responsible to free the returned object via
300  * g_free() after use
301  */
302 static gchar *migrate_query_status(QTestState *who)
303 {
304     QDict *rsp_return = migrate_query(who);
305     gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
306 
307     g_assert(status);
308     qobject_unref(rsp_return);
309 
310     return status;
311 }
312 
313 static bool check_migration_status(QTestState *who, const char *goal,
314                                    const char **ungoals)
315 {
316     bool ready;
317     char *current_status;
318     const char **ungoal;
319 
320     current_status = migrate_query_status(who);
321     ready = strcmp(current_status, goal) == 0;
322     if (!ungoals) {
323         g_assert_cmpstr(current_status, !=, "failed");
324         /*
325          * If looking for a state other than completed,
326          * completion of migration would cause the test to
327          * hang.
328          */
329         if (strcmp(goal, "completed") != 0) {
330             g_assert_cmpstr(current_status, !=, "completed");
331         }
332     } else {
333         for (ungoal = ungoals; *ungoal; ungoal++) {
334             g_assert_cmpstr(current_status, !=,  *ungoal);
335         }
336     }
337     g_free(current_status);
338     return ready;
339 }
340 
341 void wait_for_migration_status(QTestState *who,
342                                const char *goal, const char **ungoals)
343 {
344     g_test_timer_start();
345     while (!check_migration_status(who, goal, ungoals)) {
346         usleep(1000);
347 
348         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
349     }
350 }
351 
352 void wait_for_migration_complete(QTestState *who)
353 {
354     wait_for_migration_status(who, "completed", NULL);
355 }
356 
357 void wait_for_migration_fail(QTestState *from, bool allow_active)
358 {
359     g_test_timer_start();
360     QDict *rsp_return;
361     char *status;
362     bool failed;
363 
364     do {
365         status = migrate_query_status(from);
366         bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
367             (allow_active && !strcmp(status, "active"));
368         if (!result) {
369             fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
370                     __func__, status, allow_active);
371         }
372         g_assert(result);
373         failed = !strcmp(status, "failed");
374         g_free(status);
375 
376         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
377     } while (!failed);
378 
379     /* Is the machine currently running? */
380     rsp_return = qtest_qmp_assert_success_ref(from,
381                                               "{ 'execute': 'query-status' }");
382     g_assert(qdict_haskey(rsp_return, "running"));
383     g_assert(qdict_get_bool(rsp_return, "running"));
384     qobject_unref(rsp_return);
385 }
386 
387 char *find_common_machine_version(const char *mtype, const char *var1,
388                                   const char *var2)
389 {
390     g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype);
391     g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype);
392 
393     g_assert(type1 && type2);
394 
395     if (g_str_equal(type1, type2)) {
396         /* either can be used */
397         return g_strdup(type1);
398     }
399 
400     if (qtest_has_machine_with_env(var2, type1)) {
401         return g_strdup(type1);
402     }
403 
404     if (qtest_has_machine_with_env(var1, type2)) {
405         return g_strdup(type2);
406     }
407 
408     g_test_message("No common machine version for machine type '%s' between "
409                    "binaries %s and %s", mtype, getenv(var1), getenv(var2));
410     g_assert_not_reached();
411 }
412 
413 char *resolve_machine_version(const char *alias, const char *var1,
414                               const char *var2)
415 {
416     const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE");
417     g_autofree char *machine_name = NULL;
418 
419     if (mname) {
420         const char *dash = strrchr(mname, '-');
421         const char *dot = strrchr(mname, '.');
422 
423         machine_name = g_strdup(mname);
424 
425         if (dash && dot) {
426             assert(qtest_has_machine(machine_name));
427             return g_steal_pointer(&machine_name);
428         }
429         /* else: probably an alias, let it be resolved below */
430     } else {
431         /* use the hardcoded alias */
432         machine_name = g_strdup(alias);
433     }
434 
435     return find_common_machine_version(machine_name, var1, var2);
436 }
437 
438 typedef struct {
439     char *name;
440     void (*func)(void);
441 } MigrationTest;
442 
443 static void migration_test_destroy(gpointer data)
444 {
445     MigrationTest *test = (MigrationTest *)data;
446 
447     g_free(test->name);
448     g_free(test);
449 }
450 
451 static void migration_test_wrapper(const void *data)
452 {
453     MigrationTest *test = (MigrationTest *)data;
454 
455     g_test_message("Running /%s%s", qtest_get_arch(), test->name);
456     test->func();
457 }
458 
459 void migration_test_add(const char *path, void (*fn)(void))
460 {
461     MigrationTest *test = g_new0(MigrationTest, 1);
462 
463     test->func = fn;
464     test->name = g_strdup(path);
465 
466     qtest_add_data_func_full(path, test, migration_test_wrapper,
467                              migration_test_destroy);
468 }
469 
470 #ifdef O_DIRECT
471 /*
472  * Probe for O_DIRECT support on the filesystem. Since this is used
473  * for tests, be conservative, if anything fails, assume it's
474  * unsupported.
475  */
476 bool probe_o_direct_support(const char *tmpfs)
477 {
478     g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs);
479     int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT;
480     void *buf;
481     ssize_t ret, len;
482     uint64_t offset;
483 
484     fd = open(filename, flags, 0660);
485     if (fd < 0) {
486         unlink(filename);
487         return false;
488     }
489 
490     /*
491      * Using 1MB alignment as conservative choice to satisfy any
492      * plausible architecture default page size, and/or filesystem
493      * alignment restrictions.
494      */
495     len = 0x100000;
496     offset = 0x100000;
497 
498     buf = qemu_try_memalign(len, len);
499     g_assert(buf);
500 
501     ret = pwrite(fd, buf, len, offset);
502     unlink(filename);
503     g_free(buf);
504 
505     if (ret < 0) {
506         return false;
507     }
508 
509     return true;
510 }
511 #endif
512 
513 /*
514  * Wait for a "MIGRATION" event.  This is what Libvirt uses to track
515  * migration status changes.
516  */
517 void migration_event_wait(QTestState *s, const char *target)
518 {
519     QDict *response, *data;
520     const char *status;
521     bool found;
522 
523     do {
524         response = qtest_qmp_eventwait_ref(s, "MIGRATION");
525         data = qdict_get_qdict(response, "data");
526         g_assert(data);
527         status = qdict_get_str(data, "status");
528         found = (strcmp(status, target) == 0);
529         qobject_unref(response);
530     } while (!found);
531 }
532