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