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
)
35 case SOCKET_ADDRESS_TYPE_INET
:
36 return g_strdup_printf("tcp:%s:%s",
39 case SOCKET_ADDRESS_TYPE_UNIX
:
40 return g_strdup_printf("unix:%s",
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",
49 return g_strdup("unknown address type");
53 static QDict
*SocketAddress_to_qdict(SocketAddress
*addr
)
55 QDict
*dict
= qdict_new();
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
);
63 case SOCKET_ADDRESS_TYPE_UNIX
:
64 qdict_put_str(dict
, "type", "unix");
65 qdict_put_str(dict
, "path", addr
->u
.q_unix
.path
);
67 case SOCKET_ADDRESS_TYPE_FD
:
68 qdict_put_str(dict
, "type", "fd");
69 qdict_put_str(dict
, "str", addr
->u
.fd
.str
);
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
);
77 g_assert_not_reached();
84 static SocketAddress
*migrate_get_socket_address(QTestState
*who
)
87 SocketAddressList
*addrs
;
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
);
105 migrate_get_connect_uri(QTestState
*who
)
107 SocketAddress
*addrs
;
110 addrs
= migrate_get_socket_address(who
);
111 connect_uri
= SocketAddress_to_str(addrs
);
113 qapi_free_SocketAddress(addrs
);
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
)
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
));
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;
161 } else if (g_str_equal(name
, "SUSPEND")) {
162 state
->suspend_seen
= true;
164 } else if (g_str_equal(name
, "RESUME")) {
165 state
->resume_seen
= true;
172 void migrate_qmp_fail(QTestState
*who
, const char *uri
,
173 const char *channels
, const char *fmt
, ...)
179 args
= qdict_from_vjsonf_nofail(fmt
, ap
);
182 g_assert(!qdict_haskey(args
, "uri"));
184 qdict_put_str(args
, "uri", uri
);
187 g_assert(!qdict_haskey(args
, "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"));
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
, ...)
211 g_autofree
char *connect_uri
= NULL
;
214 args
= qdict_from_vjsonf_nofail(fmt
, ap
);
217 g_assert(!qdict_haskey(args
, "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"));
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
,
240 qtest_qmp_assert_success(who
,
241 "{ 'execute': 'migrate-set-capabilities',"
243 "'capabilities': [ { "
244 "'capability': %s, 'state': %i } ] } }",
248 void migrate_incoming_qmp(QTestState
*to
, const char *uri
, const char *fmt
, ...)
251 QDict
*args
, *rsp
, *data
;
254 args
= qdict_from_vjsonf_nofail(fmt
, 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}",
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"));
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");
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
)
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"));
306 * Note: caller is responsible to free the returned object via
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"));
315 qobject_unref(rsp_return
);
320 static bool check_migration_status(QTestState
*who
, const char *goal
,
321 const char **ungoals
)
324 char *current_status
;
327 current_status
= migrate_query_status(who
);
328 ready
= strcmp(current_status
, goal
) == 0;
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
336 if (strcmp(goal
, "completed") != 0) {
337 g_assert_cmpstr(current_status
, !=, "completed");
340 for (ungoal
= ungoals
; *ungoal
; ungoal
++) {
341 g_assert_cmpstr(current_status
, !=, *ungoal
);
344 g_free(current_status
);
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
)) {
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();
372 status
= migrate_query_status(from
);
373 bool result
= !strcmp(status
, "setup") || !strcmp(status
, "failed") ||
374 (allow_active
&& !strcmp(status
, "active"));
376 fprintf(stderr
, "%s: unexpected status status=%s allow_active=%d\n",
377 __func__
, status
, allow_active
);
380 failed
= !strcmp(status
, "failed");
383 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT
);
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
,
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
,
423 const char *mname
= g_getenv("QTEST_QEMU_MACHINE_TYPE");
424 g_autofree
char *machine_name
= NULL
;
427 const char *dash
= strrchr(mname
, '-');
428 const char *dot
= strrchr(mname
, '.');
430 machine_name
= g_strdup(mname
);
433 assert(qtest_has_machine(machine_name
));
434 return g_steal_pointer(&machine_name
);
436 /* else: probably an alias, let it be resolved below */
438 /* use the hardcoded alias */
439 machine_name
= g_strdup(alias
);
442 return find_common_machine_version(machine_name
, var1
, var2
);
450 static void migration_test_destroy(gpointer data
)
452 MigrationTest
*test
= (MigrationTest
*)data
;
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
);
466 void migration_test_add(const char *path
, void (*fn
)(void))
468 MigrationTest
*test
= g_new0(MigrationTest
, 1);
471 test
->name
= g_strdup(path
);
473 qtest_add_data_func_full(path
, test
, migration_test_wrapper
,
474 migration_test_destroy
);