xref: /openbmc/qemu/tests/qtest/migration-helpers.c (revision cd313b66f203381f2f2f984d5155d7942d26725d)
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 SocketAddress *migrate_get_socket_address(QTestState *who)
86 {
87     QDict *rsp;
88     SocketAddressList *addrs;
89     SocketAddress *addr;
90     Visitor *iv = NULL;
91     QObject *object;
92 
93     rsp = migrate_query(who);
94     object = qdict_get(rsp, "socket-address");
95 
96     iv = qobject_input_visitor_new(object);
97     visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort);
98     addr = addrs->value;
99     visit_free(iv);
100 
101     qobject_unref(rsp);
102     return addr;
103 }
104 
105 static char *
106 migrate_get_connect_uri(QTestState *who)
107 {
108     SocketAddress *addrs;
109     char *connect_uri;
110 
111     addrs = migrate_get_socket_address(who);
112     connect_uri = SocketAddress_to_str(addrs);
113 
114     qapi_free_SocketAddress(addrs);
115     return connect_uri;
116 }
117 
118 static QDict *
119 migrate_get_connect_qdict(QTestState *who)
120 {
121     SocketAddress *addrs;
122     QDict *connect_qdict;
123 
124     addrs = migrate_get_socket_address(who);
125     connect_qdict = SocketAddress_to_qdict(addrs);
126 
127     qapi_free_SocketAddress(addrs);
128     return connect_qdict;
129 }
130 
131 static void migrate_set_ports(QTestState *to, QList *channel_list)
132 {
133     QDict *addr;
134     QListEntry *entry;
135     const char *addr_port = NULL;
136 
137     addr = migrate_get_connect_qdict(to);
138 
139     QLIST_FOREACH_ENTRY(channel_list, entry) {
140         QDict *channel = qobject_to(QDict, qlist_entry_obj(entry));
141         QDict *addrdict = qdict_get_qdict(channel, "addr");
142 
143         if (qdict_haskey(addrdict, "port") &&
144             qdict_haskey(addr, "port") &&
145             (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) {
146                 addr_port = qdict_get_str(addr, "port");
147                 qdict_put_str(addrdict, "port", g_strdup(addr_port));
148         }
149     }
150 
151     qobject_unref(addr);
152 }
153 
154 bool migrate_watch_for_events(QTestState *who, const char *name,
155                               QDict *event, void *opaque)
156 {
157     QTestMigrationState *state = opaque;
158 
159     if (g_str_equal(name, "STOP")) {
160         state->stop_seen = true;
161         return true;
162     } else if (g_str_equal(name, "SUSPEND")) {
163         state->suspend_seen = true;
164         return true;
165     } else if (g_str_equal(name, "RESUME")) {
166         state->resume_seen = true;
167         return true;
168     }
169 
170     return false;
171 }
172 
173 void migrate_qmp_fail(QTestState *who, const char *uri,
174                       const char *channels, const char *fmt, ...)
175 {
176     va_list ap;
177     QDict *args, *err;
178 
179     va_start(ap, fmt);
180     args = qdict_from_vjsonf_nofail(fmt, ap);
181     va_end(ap);
182 
183     g_assert(!qdict_haskey(args, "uri"));
184     if (uri) {
185         qdict_put_str(args, "uri", uri);
186     }
187 
188     g_assert(!qdict_haskey(args, "channels"));
189     if (channels) {
190         QObject *channels_obj = qobject_from_json(channels, &error_abort);
191         qdict_put_obj(args, "channels", channels_obj);
192     }
193 
194     err = qtest_qmp_assert_failure_ref(
195         who, "{ 'execute': 'migrate', 'arguments': %p}", args);
196 
197     g_assert(qdict_haskey(err, "desc"));
198 
199     qobject_unref(err);
200 }
201 
202 /*
203  * Send QMP command "migrate".
204  * Arguments are built from @fmt... (formatted like
205  * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
206  */
207 void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
208                  const char *channels, const char *fmt, ...)
209 {
210     va_list ap;
211     QDict *args;
212     g_autofree char *connect_uri = NULL;
213 
214     va_start(ap, fmt);
215     args = qdict_from_vjsonf_nofail(fmt, ap);
216     va_end(ap);
217 
218     g_assert(!qdict_haskey(args, "uri"));
219     if (uri) {
220         qdict_put_str(args, "uri", uri);
221     } else if (!channels) {
222         connect_uri = migrate_get_connect_uri(to);
223         qdict_put_str(args, "uri", connect_uri);
224     }
225 
226     g_assert(!qdict_haskey(args, "channels"));
227     if (channels) {
228         QObject *channels_obj = qobject_from_json(channels, &error_abort);
229         QList *channel_list = qobject_to(QList, channels_obj);
230         migrate_set_ports(to, channel_list);
231         qdict_put_obj(args, "channels", channels_obj);
232     }
233 
234     qtest_qmp_assert_success(who,
235                              "{ 'execute': 'migrate', 'arguments': %p}", args);
236 }
237 
238 void migrate_set_capability(QTestState *who, const char *capability,
239                             bool value)
240 {
241     qtest_qmp_assert_success(who,
242                              "{ 'execute': 'migrate-set-capabilities',"
243                              "'arguments': { "
244                              "'capabilities': [ { "
245                              "'capability': %s, 'state': %i } ] } }",
246                              capability, value);
247 }
248 
249 void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
250 {
251     va_list ap;
252     QDict *args, *rsp, *data;
253 
254     va_start(ap, fmt);
255     args = qdict_from_vjsonf_nofail(fmt, ap);
256     va_end(ap);
257 
258     g_assert(!qdict_haskey(args, "uri"));
259     qdict_put_str(args, "uri", uri);
260 
261     /* This function relies on the event to work, make sure it's enabled */
262     migrate_set_capability(to, "events", true);
263 
264     rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
265                     args);
266 
267     if (!qdict_haskey(rsp, "return")) {
268         g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
269         g_test_message("%s", s->str);
270     }
271 
272     g_assert(qdict_haskey(rsp, "return"));
273     qobject_unref(rsp);
274 
275     rsp = qtest_qmp_eventwait_ref(to, "MIGRATION");
276     g_assert(qdict_haskey(rsp, "data"));
277 
278     data = qdict_get_qdict(rsp, "data");
279     g_assert(qdict_haskey(data, "status"));
280     g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup");
281 
282     qobject_unref(rsp);
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 static 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 static bool check_migration_status(QTestState *who, const char *goal,
323                                    const char **ungoals)
324 {
325     bool ready;
326     char *current_status;
327     const char **ungoal;
328 
329     current_status = migrate_query_status(who);
330     ready = strcmp(current_status, goal) == 0;
331     if (!ungoals) {
332         g_assert_cmpstr(current_status, !=, "failed");
333         /*
334          * If looking for a state other than completed,
335          * completion of migration would cause the test to
336          * hang.
337          */
338         if (strcmp(goal, "completed") != 0) {
339             g_assert_cmpstr(current_status, !=, "completed");
340         }
341     } else {
342         for (ungoal = ungoals; *ungoal; ungoal++) {
343             g_assert_cmpstr(current_status, !=,  *ungoal);
344         }
345     }
346     g_free(current_status);
347     return ready;
348 }
349 
350 void wait_for_migration_status(QTestState *who,
351                                const char *goal, const char **ungoals)
352 {
353     g_test_timer_start();
354     while (!check_migration_status(who, goal, ungoals)) {
355         usleep(1000);
356 
357         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
358     }
359 }
360 
361 void wait_for_migration_complete(QTestState *who)
362 {
363     wait_for_migration_status(who, "completed", NULL);
364 }
365 
366 void wait_for_migration_fail(QTestState *from, bool allow_active)
367 {
368     g_test_timer_start();
369     QDict *rsp_return;
370     char *status;
371     bool failed;
372 
373     do {
374         status = migrate_query_status(from);
375         bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
376             (allow_active && !strcmp(status, "active"));
377         if (!result) {
378             fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
379                     __func__, status, allow_active);
380         }
381         g_assert(result);
382         failed = !strcmp(status, "failed");
383         g_free(status);
384 
385         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
386     } while (!failed);
387 
388     /* Is the machine currently running? */
389     rsp_return = qtest_qmp_assert_success_ref(from,
390                                               "{ 'execute': 'query-status' }");
391     g_assert(qdict_haskey(rsp_return, "running"));
392     g_assert(qdict_get_bool(rsp_return, "running"));
393     qobject_unref(rsp_return);
394 }
395 
396 char *find_common_machine_version(const char *mtype, const char *var1,
397                                   const char *var2)
398 {
399     g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype);
400     g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype);
401 
402     g_assert(type1 && type2);
403 
404     if (g_str_equal(type1, type2)) {
405         /* either can be used */
406         return g_strdup(type1);
407     }
408 
409     if (qtest_has_machine_with_env(var2, type1)) {
410         return g_strdup(type1);
411     }
412 
413     if (qtest_has_machine_with_env(var1, type2)) {
414         return g_strdup(type2);
415     }
416 
417     g_test_message("No common machine version for machine type '%s' between "
418                    "binaries %s and %s", mtype, getenv(var1), getenv(var2));
419     g_assert_not_reached();
420 }
421 
422 char *resolve_machine_version(const char *alias, const char *var1,
423                               const char *var2)
424 {
425     const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE");
426     g_autofree char *machine_name = NULL;
427 
428     if (mname) {
429         const char *dash = strrchr(mname, '-');
430         const char *dot = strrchr(mname, '.');
431 
432         machine_name = g_strdup(mname);
433 
434         if (dash && dot) {
435             assert(qtest_has_machine(machine_name));
436             return g_steal_pointer(&machine_name);
437         }
438         /* else: probably an alias, let it be resolved below */
439     } else {
440         /* use the hardcoded alias */
441         machine_name = g_strdup(alias);
442     }
443 
444     return find_common_machine_version(machine_name, var1, var2);
445 }
446 
447 typedef struct {
448     char *name;
449     void (*func)(void);
450 } MigrationTest;
451 
452 static void migration_test_destroy(gpointer data)
453 {
454     MigrationTest *test = (MigrationTest *)data;
455 
456     g_free(test->name);
457     g_free(test);
458 }
459 
460 static void migration_test_wrapper(const void *data)
461 {
462     MigrationTest *test = (MigrationTest *)data;
463 
464     g_test_message("Running /%s%s", qtest_get_arch(), test->name);
465     test->func();
466 }
467 
468 void migration_test_add(const char *path, void (*fn)(void))
469 {
470     MigrationTest *test = g_new0(MigrationTest, 1);
471 
472     test->func = fn;
473     test->name = g_strdup(path);
474 
475     qtest_add_data_func_full(path, test, migration_test_wrapper,
476                              migration_test_destroy);
477 }
478 
479 #ifdef O_DIRECT
480 /*
481  * Probe for O_DIRECT support on the filesystem. Since this is used
482  * for tests, be conservative, if anything fails, assume it's
483  * unsupported.
484  */
485 bool probe_o_direct_support(const char *tmpfs)
486 {
487     g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs);
488     int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT;
489     void *buf;
490     ssize_t ret, len;
491     uint64_t offset;
492 
493     fd = open(filename, flags, 0660);
494     if (fd < 0) {
495         unlink(filename);
496         return false;
497     }
498 
499     /*
500      * Using 1MB alignment as conservative choice to satisfy any
501      * plausible architecture default page size, and/or filesystem
502      * alignment restrictions.
503      */
504     len = 0x100000;
505     offset = 0x100000;
506 
507     buf = qemu_try_memalign(len, len);
508     g_assert(buf);
509 
510     ret = pwrite(fd, buf, len, offset);
511     unlink(filename);
512     g_free(buf);
513 
514     if (ret < 0) {
515         return false;
516     }
517 
518     return true;
519 }
520 #endif
521