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"
17 #include "migration-helpers.h"
20 * Number of seconds we wait when looking for migration
21 * status changes, to avoid test suite hanging forever
22 * when things go wrong. Needs to be higher enough to
23 * avoid false positives on loaded hosts.
25 #define MIGRATION_STATUS_WAIT_TIMEOUT 120
27 bool migrate_watch_for_events(QTestState
*who
, const char *name
,
28 QDict
*event
, void *opaque
)
30 QTestMigrationState
*state
= opaque
;
32 if (g_str_equal(name
, "STOP")) {
33 state
->stop_seen
= true;
35 } else if (g_str_equal(name
, "SUSPEND")) {
36 state
->suspend_seen
= true;
38 } else if (g_str_equal(name
, "RESUME")) {
39 state
->resume_seen
= true;
46 void migrate_qmp_fail(QTestState
*who
, const char *uri
, const char *fmt
, ...)
52 args
= qdict_from_vjsonf_nofail(fmt
, ap
);
55 g_assert(!qdict_haskey(args
, "uri"));
56 qdict_put_str(args
, "uri", uri
);
58 err
= qtest_qmp_assert_failure_ref(
59 who
, "{ 'execute': 'migrate', 'arguments': %p}", args
);
61 g_assert(qdict_haskey(err
, "desc"));
67 * Send QMP command "migrate".
68 * Arguments are built from @fmt... (formatted like
69 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
71 void migrate_qmp(QTestState
*who
, const char *uri
, const char *fmt
, ...)
77 args
= qdict_from_vjsonf_nofail(fmt
, ap
);
80 g_assert(!qdict_haskey(args
, "uri"));
81 qdict_put_str(args
, "uri", uri
);
83 qtest_qmp_assert_success(who
,
84 "{ 'execute': 'migrate', 'arguments': %p}", args
);
87 void migrate_set_capability(QTestState
*who
, const char *capability
,
90 qtest_qmp_assert_success(who
,
91 "{ 'execute': 'migrate-set-capabilities',"
93 "'capabilities': [ { "
94 "'capability': %s, 'state': %i } ] } }",
98 void migrate_incoming_qmp(QTestState
*to
, const char *uri
, const char *fmt
, ...)
101 QDict
*args
, *rsp
, *data
;
104 args
= qdict_from_vjsonf_nofail(fmt
, ap
);
107 g_assert(!qdict_haskey(args
, "uri"));
108 qdict_put_str(args
, "uri", uri
);
110 migrate_set_capability(to
, "events", true);
112 rsp
= qtest_qmp(to
, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
115 if (!qdict_haskey(rsp
, "return")) {
116 g_autoptr(GString
) s
= qobject_to_json_pretty(QOBJECT(rsp
), true);
117 g_test_message("%s", s
->str
);
120 g_assert(qdict_haskey(rsp
, "return"));
123 rsp
= qtest_qmp_eventwait_ref(to
, "MIGRATION");
124 g_assert(qdict_haskey(rsp
, "data"));
126 data
= qdict_get_qdict(rsp
, "data");
127 g_assert(qdict_haskey(data
, "status"));
128 g_assert_cmpstr(qdict_get_str(data
, "status"), ==, "setup");
134 * Note: caller is responsible to free the returned object via
135 * qobject_unref() after use
137 QDict
*migrate_query(QTestState
*who
)
139 return qtest_qmp_assert_success_ref(who
, "{ 'execute': 'query-migrate' }");
142 QDict
*migrate_query_not_failed(QTestState
*who
)
145 QDict
*rsp
= migrate_query(who
);
146 status
= qdict_get_str(rsp
, "status");
147 if (g_str_equal(status
, "failed")) {
148 g_printerr("query-migrate shows failed migration: %s\n",
149 qdict_get_str(rsp
, "error-desc"));
151 g_assert(!g_str_equal(status
, "failed"));
156 * Note: caller is responsible to free the returned object via
159 static gchar
*migrate_query_status(QTestState
*who
)
161 QDict
*rsp_return
= migrate_query(who
);
162 gchar
*status
= g_strdup(qdict_get_str(rsp_return
, "status"));
165 qobject_unref(rsp_return
);
170 static bool check_migration_status(QTestState
*who
, const char *goal
,
171 const char **ungoals
)
174 char *current_status
;
177 current_status
= migrate_query_status(who
);
178 ready
= strcmp(current_status
, goal
) == 0;
180 g_assert_cmpstr(current_status
, !=, "failed");
182 * If looking for a state other than completed,
183 * completion of migration would cause the test to
186 if (strcmp(goal
, "completed") != 0) {
187 g_assert_cmpstr(current_status
, !=, "completed");
190 for (ungoal
= ungoals
; *ungoal
; ungoal
++) {
191 g_assert_cmpstr(current_status
, !=, *ungoal
);
194 g_free(current_status
);
198 void wait_for_migration_status(QTestState
*who
,
199 const char *goal
, const char **ungoals
)
201 g_test_timer_start();
202 while (!check_migration_status(who
, goal
, ungoals
)) {
205 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT
);
209 void wait_for_migration_complete(QTestState
*who
)
211 wait_for_migration_status(who
, "completed", NULL
);
214 void wait_for_migration_fail(QTestState
*from
, bool allow_active
)
216 g_test_timer_start();
222 status
= migrate_query_status(from
);
223 bool result
= !strcmp(status
, "setup") || !strcmp(status
, "failed") ||
224 (allow_active
&& !strcmp(status
, "active"));
226 fprintf(stderr
, "%s: unexpected status status=%s allow_active=%d\n",
227 __func__
, status
, allow_active
);
230 failed
= !strcmp(status
, "failed");
233 g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT
);
236 /* Is the machine currently running? */
237 rsp_return
= qtest_qmp_assert_success_ref(from
,
238 "{ 'execute': 'query-status' }");
239 g_assert(qdict_haskey(rsp_return
, "running"));
240 g_assert(qdict_get_bool(rsp_return
, "running"));
241 qobject_unref(rsp_return
);
244 char *find_common_machine_version(const char *mtype
, const char *var1
,
247 g_autofree
char *type1
= qtest_resolve_machine_alias(var1
, mtype
);
248 g_autofree
char *type2
= qtest_resolve_machine_alias(var2
, mtype
);
250 g_assert(type1
&& type2
);
252 if (g_str_equal(type1
, type2
)) {
253 /* either can be used */
254 return g_strdup(type1
);
257 if (qtest_has_machine_with_env(var2
, type1
)) {
258 return g_strdup(type1
);
261 if (qtest_has_machine_with_env(var1
, type2
)) {
262 return g_strdup(type2
);
265 g_test_message("No common machine version for machine type '%s' between "
266 "binaries %s and %s", mtype
, getenv(var1
), getenv(var2
));
267 g_assert_not_reached();
270 char *resolve_machine_version(const char *alias
, const char *var1
,
273 const char *mname
= g_getenv("QTEST_QEMU_MACHINE_TYPE");
274 g_autofree
char *machine_name
= NULL
;
277 const char *dash
= strrchr(mname
, '-');
278 const char *dot
= strrchr(mname
, '.');
280 machine_name
= g_strdup(mname
);
283 assert(qtest_has_machine(machine_name
));
284 return g_steal_pointer(&machine_name
);
286 /* else: probably an alias, let it be resolved below */
288 /* use the hardcoded alias */
289 machine_name
= g_strdup(alias
);
292 return find_common_machine_version(machine_name
, var1
, var2
);
300 static void migration_test_destroy(gpointer data
)
302 MigrationTest
*test
= (MigrationTest
*)data
;
308 static void migration_test_wrapper(const void *data
)
310 MigrationTest
*test
= (MigrationTest
*)data
;
312 g_test_message("Running /%s%s", qtest_get_arch(), test
->name
);
316 void migration_test_add(const char *path
, void (*fn
)(void))
318 MigrationTest
*test
= g_new0(MigrationTest
, 1);
321 test
->name
= g_strdup(path
);
323 qtest_add_data_func_full(path
, test
, migration_test_wrapper
,
324 migration_test_destroy
);