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