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