target/alpha: Split out gen_goto_tb
[qemu/armbru.git] / tests / qtest / migration-helpers.c
blobce6d6615b529c5d2092ecabd54ebe4b73b06ab71
1 /*
2 * QTest migration helpers
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.
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.
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"
22 #include "migration-helpers.h"
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.
30 #define MIGRATION_STATUS_WAIT_TIMEOUT 120
32 static char *SocketAddress_to_str(SocketAddress *addr)
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");
53 static QDict *SocketAddress_to_qdict(SocketAddress *addr)
55 QDict *dict = qdict_new();
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;
81 return dict;
84 static SocketAddress *migrate_get_socket_address(QTestState *who)
86 QDict *rsp;
87 SocketAddressList *addrs;
88 SocketAddress *addr;
89 Visitor *iv = NULL;
90 QObject *object;
92 rsp = migrate_query(who);
93 object = qdict_get(rsp, "socket-address");
95 iv = qobject_input_visitor_new(object);
96 visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort);
97 addr = addrs->value;
98 visit_free(iv);
100 qobject_unref(rsp);
101 return addr;
104 static char *
105 migrate_get_connect_uri(QTestState *who)
107 SocketAddress *addrs;
108 char *connect_uri;
110 addrs = migrate_get_socket_address(who);
111 connect_uri = SocketAddress_to_str(addrs);
113 qapi_free_SocketAddress(addrs);
114 return connect_uri;
117 static QDict *
118 migrate_get_connect_qdict(QTestState *who)
120 SocketAddress *addrs;
121 QDict *connect_qdict;
123 addrs = migrate_get_socket_address(who);
124 connect_qdict = SocketAddress_to_qdict(addrs);
126 qapi_free_SocketAddress(addrs);
127 return connect_qdict;
130 static void migrate_set_ports(QTestState *to, QList *channel_list)
132 QDict *addr;
133 QListEntry *entry;
134 const char *addr_port = NULL;
136 addr = migrate_get_connect_qdict(to);
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");
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));
150 qobject_unref(addr);
153 bool migrate_watch_for_events(QTestState *who, const char *name,
154 QDict *event, void *opaque)
156 QTestMigrationState *state = opaque;
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;
169 return false;
172 void migrate_qmp_fail(QTestState *who, const char *uri,
173 const char *channels, const char *fmt, ...)
175 va_list ap;
176 QDict *args, *err;
178 va_start(ap, fmt);
179 args = qdict_from_vjsonf_nofail(fmt, ap);
180 va_end(ap);
182 g_assert(!qdict_haskey(args, "uri"));
183 if (uri) {
184 qdict_put_str(args, "uri", uri);
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);
193 err = qtest_qmp_assert_failure_ref(
194 who, "{ 'execute': 'migrate', 'arguments': %p}", args);
196 g_assert(qdict_haskey(err, "desc"));
198 qobject_unref(err);
202 * Send QMP command "migrate".
203 * Arguments are built from @fmt... (formatted like
204 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
206 void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
207 const char *channels, const char *fmt, ...)
209 va_list ap;
210 QDict *args;
211 g_autofree char *connect_uri = NULL;
213 va_start(ap, fmt);
214 args = qdict_from_vjsonf_nofail(fmt, ap);
215 va_end(ap);
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);
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);
233 qtest_qmp_assert_success(who,
234 "{ 'execute': 'migrate', 'arguments': %p}", args);
237 void migrate_set_capability(QTestState *who, const char *capability,
238 bool value)
240 qtest_qmp_assert_success(who,
241 "{ 'execute': 'migrate-set-capabilities',"
242 "'arguments': { "
243 "'capabilities': [ { "
244 "'capability': %s, 'state': %i } ] } }",
245 capability, value);
248 void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
250 va_list ap;
251 QDict *args, *rsp, *data;
253 va_start(ap, fmt);
254 args = qdict_from_vjsonf_nofail(fmt, ap);
255 va_end(ap);
257 g_assert(!qdict_haskey(args, "uri"));
258 qdict_put_str(args, "uri", uri);
260 migrate_set_capability(to, "events", true);
262 rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
263 args);
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);
270 g_assert(qdict_haskey(rsp, "return"));
271 qobject_unref(rsp);
273 rsp = qtest_qmp_eventwait_ref(to, "MIGRATION");
274 g_assert(qdict_haskey(rsp, "data"));
276 data = qdict_get_qdict(rsp, "data");
277 g_assert(qdict_haskey(data, "status"));
278 g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup");
280 qobject_unref(rsp);
284 * Note: caller is responsible to free the returned object via
285 * qobject_unref() after use
287 QDict *migrate_query(QTestState *who)
289 return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
292 QDict *migrate_query_not_failed(QTestState *who)
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"));
301 g_assert(!g_str_equal(status, "failed"));
302 return rsp;
306 * Note: caller is responsible to free the returned object via
307 * g_free() after use
309 static gchar *migrate_query_status(QTestState *who)
311 QDict *rsp_return = migrate_query(who);
312 gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
314 g_assert(status);
315 qobject_unref(rsp_return);
317 return status;
320 static bool check_migration_status(QTestState *who, const char *goal,
321 const char **ungoals)
323 bool ready;
324 char *current_status;
325 const char **ungoal;
327 current_status = migrate_query_status(who);
328 ready = strcmp(current_status, goal) == 0;
329 if (!ungoals) {
330 g_assert_cmpstr(current_status, !=, "failed");
332 * If looking for a state other than completed,
333 * completion of migration would cause the test to
334 * hang.
336 if (strcmp(goal, "completed") != 0) {
337 g_assert_cmpstr(current_status, !=, "completed");
339 } else {
340 for (ungoal = ungoals; *ungoal; ungoal++) {
341 g_assert_cmpstr(current_status, !=, *ungoal);
344 g_free(current_status);
345 return ready;
348 void wait_for_migration_status(QTestState *who,
349 const char *goal, const char **ungoals)
351 g_test_timer_start();
352 while (!check_migration_status(who, goal, ungoals)) {
353 usleep(1000);
355 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
359 void wait_for_migration_complete(QTestState *who)
361 wait_for_migration_status(who, "completed", NULL);
364 void wait_for_migration_fail(QTestState *from, bool allow_active)
366 g_test_timer_start();
367 QDict *rsp_return;
368 char *status;
369 bool failed;
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);
379 g_assert(result);
380 failed = !strcmp(status, "failed");
381 g_free(status);
383 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
384 } while (!failed);
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);
394 char *find_common_machine_version(const char *mtype, const char *var1,
395 const char *var2)
397 g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype);
398 g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype);
400 g_assert(type1 && type2);
402 if (g_str_equal(type1, type2)) {
403 /* either can be used */
404 return g_strdup(type1);
407 if (qtest_has_machine_with_env(var2, type1)) {
408 return g_strdup(type1);
411 if (qtest_has_machine_with_env(var1, type2)) {
412 return g_strdup(type2);
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();
420 char *resolve_machine_version(const char *alias, const char *var1,
421 const char *var2)
423 const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE");
424 g_autofree char *machine_name = NULL;
426 if (mname) {
427 const char *dash = strrchr(mname, '-');
428 const char *dot = strrchr(mname, '.');
430 machine_name = g_strdup(mname);
432 if (dash && dot) {
433 assert(qtest_has_machine(machine_name));
434 return g_steal_pointer(&machine_name);
436 /* else: probably an alias, let it be resolved below */
437 } else {
438 /* use the hardcoded alias */
439 machine_name = g_strdup(alias);
442 return find_common_machine_version(machine_name, var1, var2);
445 typedef struct {
446 char *name;
447 void (*func)(void);
448 } MigrationTest;
450 static void migration_test_destroy(gpointer data)
452 MigrationTest *test = (MigrationTest *)data;
454 g_free(test->name);
455 g_free(test);
458 static void migration_test_wrapper(const void *data)
460 MigrationTest *test = (MigrationTest *)data;
462 g_test_message("Running /%s%s", qtest_get_arch(), test->name);
463 test->func();
466 void migration_test_add(const char *path, void (*fn)(void))
468 MigrationTest *test = g_new0(MigrationTest, 1);
470 test->func = fn;
471 test->name = g_strdup(path);
473 qtest_add_data_func_full(path, test, migration_test_wrapper,
474 migration_test_destroy);