tests/qtest: migration: Add migrate_incoming_qmp helper
[qemu/armbru.git] / tests / qtest / virtio-net-failover.c
blob4a809590bf795484c68ba2f3910ed341d71fd8fb
1 /*
2 * QTest testcase for virtio-net failover
4 * See docs/system/virtio-net-failover.rst
6 * Copyright (c) 2021 Red Hat, Inc.
8 * SPDX-License-Identifier: GPL-2.0-or-later
9 */
10 #include "qemu/osdep.h"
11 #include "libqtest.h"
12 #include "libqos/pci.h"
13 #include "libqos/pci-pc.h"
14 #include "qapi/qmp/qdict.h"
15 #include "qapi/qmp/qlist.h"
16 #include "qapi/qmp/qjson.h"
17 #include "libqos/malloc-pc.h"
18 #include "libqos/virtio-pci.h"
19 #include "hw/pci/pci.h"
21 #define VIRTIO_NET_F_STANDBY 62
23 #define ACPI_PCIHP_ADDR_ICH9 0x0cc0
24 #define PCI_EJ_BASE 0x0008
25 #define PCI_SEL_BASE 0x0010
27 #define BASE_MACHINE "-M q35 -nodefaults " \
28 "-device pcie-root-port,id=root0,addr=0x1,bus=pcie.0,chassis=1 " \
29 "-device pcie-root-port,id=root1,addr=0x2,bus=pcie.0,chassis=2 "
31 #define MAC_PRIMARY0 "52:54:00:11:11:11"
32 #define MAC_STANDBY0 "52:54:00:22:22:22"
33 #define MAC_PRIMARY1 "52:54:00:33:33:33"
34 #define MAC_STANDBY1 "52:54:00:44:44:44"
36 static QGuestAllocator guest_malloc;
37 static QPCIBus *pcibus;
39 static QTestState *machine_start(const char *args, int numbus)
41 QTestState *qts;
42 QPCIDevice *dev;
43 int bus;
45 qts = qtest_init(args);
47 pc_alloc_init(&guest_malloc, qts, 0);
48 pcibus = qpci_new_pc(qts, &guest_malloc);
49 g_assert(qpci_secondary_buses_init(pcibus) == numbus);
51 for (bus = 1; bus <= numbus; bus++) {
52 dev = qpci_device_find(pcibus, QPCI_DEVFN(bus, 0));
53 g_assert_nonnull(dev);
55 qpci_device_enable(dev);
56 qpci_iomap(dev, 4, NULL);
58 g_free(dev);
61 return qts;
64 static void machine_stop(QTestState *qts)
66 qpci_free_pc(pcibus);
67 alloc_destroy(&guest_malloc);
68 qtest_quit(qts);
71 static void test_error_id(void)
73 QTestState *qts;
74 QDict *resp;
75 QDict *err;
77 qts = machine_start(BASE_MACHINE
78 "-device virtio-net,bus=root0,id=standby0,failover=on",
79 2);
81 resp = qtest_qmp(qts, "{'execute': 'device_add',"
82 "'arguments': {"
83 "'driver': 'virtio-net',"
84 "'bus': 'root1',"
85 "'failover_pair_id': 'standby0'"
86 "} }");
87 g_assert(qdict_haskey(resp, "error"));
89 err = qdict_get_qdict(resp, "error");
90 g_assert(qdict_haskey(err, "desc"));
92 g_assert_cmpstr(qdict_get_str(err, "desc"), ==,
93 "Device with failover_pair_id needs to have id");
95 qobject_unref(resp);
97 machine_stop(qts);
100 static void test_error_pcie(void)
102 QTestState *qts;
103 QDict *resp;
104 QDict *err;
106 qts = machine_start(BASE_MACHINE
107 "-device virtio-net,bus=root0,id=standby0,failover=on",
110 resp = qtest_qmp(qts, "{'execute': 'device_add',"
111 "'arguments': {"
112 "'driver': 'virtio-net',"
113 "'id': 'primary0',"
114 "'bus': 'pcie.0',"
115 "'failover_pair_id': 'standby0'"
116 "} }");
117 g_assert(qdict_haskey(resp, "error"));
119 err = qdict_get_qdict(resp, "error");
120 g_assert(qdict_haskey(err, "desc"));
122 g_assert_cmpstr(qdict_get_str(err, "desc"), ==,
123 "Bus 'pcie.0' does not support hotplugging");
125 qobject_unref(resp);
127 machine_stop(qts);
130 static QDict *find_device(QDict *bus, const char *name)
132 const QObject *obj;
133 QList *devices;
134 QList *list;
136 devices = qdict_get_qlist(bus, "devices");
137 if (devices == NULL) {
138 return NULL;
141 list = qlist_copy(devices);
142 while ((obj = qlist_pop(list))) {
143 QDict *device;
145 device = qobject_to(QDict, obj);
147 if (qdict_haskey(device, "pci_bridge")) {
148 QDict *bridge;
149 QDict *bridge_device;
151 bridge = qdict_get_qdict(device, "pci_bridge");
153 if (qdict_haskey(bridge, "devices")) {
154 bridge_device = find_device(bridge, name);
155 if (bridge_device) {
156 qobject_unref(device);
157 qobject_unref(list);
158 return bridge_device;
163 if (!qdict_haskey(device, "qdev_id")) {
164 qobject_unref(device);
165 continue;
168 if (strcmp(qdict_get_str(device, "qdev_id"), name) == 0) {
169 qobject_unref(list);
170 return device;
172 qobject_unref(device);
174 qobject_unref(list);
176 return NULL;
179 static QDict *get_bus(QTestState *qts, int num)
181 QObject *obj;
182 QDict *resp;
183 QList *ret;
185 resp = qtest_qmp(qts, "{ 'execute': 'query-pci' }");
186 g_assert(qdict_haskey(resp, "return"));
188 ret = qdict_get_qlist(resp, "return");
189 g_assert_nonnull(ret);
191 while ((obj = qlist_pop(ret))) {
192 QDict *bus;
194 bus = qobject_to(QDict, obj);
195 if (!qdict_haskey(bus, "bus")) {
196 qobject_unref(bus);
197 continue;
199 if (qdict_get_int(bus, "bus") == num) {
200 qobject_unref(resp);
201 return bus;
203 qobject_ref(bus);
205 qobject_unref(resp);
207 return NULL;
210 static char *get_mac(QTestState *qts, const char *name)
212 QDict *resp;
213 char *mac;
215 resp = qtest_qmp(qts, "{ 'execute': 'qom-get', "
216 "'arguments': { "
217 "'path': %s, "
218 "'property': 'mac' } }", name);
220 g_assert(qdict_haskey(resp, "return"));
222 mac = g_strdup(qdict_get_str(resp, "return"));
224 qobject_unref(resp);
226 return mac;
229 #define check_one_card(qts, present, id, mac) \
230 do { \
231 QDict *device; \
232 QDict *bus; \
233 char *addr; \
234 bus = get_bus(qts, 0); \
235 device = find_device(bus, id); \
236 if (present) { \
237 char *path; \
238 g_assert_nonnull(device); \
239 qobject_unref(device); \
240 path = g_strdup_printf("/machine/peripheral/%s", id); \
241 addr = get_mac(qts, path); \
242 g_free(path); \
243 g_assert_cmpstr(mac, ==, addr); \
244 g_free(addr); \
245 } else { \
246 g_assert_null(device); \
248 qobject_unref(bus); \
249 } while (0)
251 static QDict *get_failover_negociated_event(QTestState *qts)
253 QDict *resp;
254 QDict *data;
256 resp = qtest_qmp_eventwait_ref(qts, "FAILOVER_NEGOTIATED");
257 g_assert(qdict_haskey(resp, "data"));
259 data = qdict_get_qdict(resp, "data");
260 g_assert(qdict_haskey(data, "device-id"));
261 qobject_ref(data);
262 qobject_unref(resp);
264 return data;
267 static QVirtioPCIDevice *start_virtio_net_internal(QTestState *qts,
268 int bus, int slot,
269 uint64_t *features)
271 QVirtioPCIDevice *dev;
272 QPCIAddress addr;
274 addr.devfn = QPCI_DEVFN((bus << 5) + slot, 0);
275 dev = virtio_pci_new(pcibus, &addr);
276 g_assert_nonnull(dev);
277 qvirtio_pci_device_enable(dev);
278 qvirtio_start_device(&dev->vdev);
279 *features &= qvirtio_get_features(&dev->vdev);
280 qvirtio_set_features(&dev->vdev, *features);
281 qvirtio_set_driver_ok(&dev->vdev);
282 return dev;
285 static QVirtioPCIDevice *start_virtio_net(QTestState *qts, int bus, int slot,
286 const char *id, bool failover)
288 QVirtioPCIDevice *dev;
289 uint64_t features;
291 features = ~(QVIRTIO_F_BAD_FEATURE |
292 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
293 (1ull << VIRTIO_RING_F_EVENT_IDX));
295 dev = start_virtio_net_internal(qts, bus, slot, &features);
297 g_assert(!!(features & (1ull << VIRTIO_NET_F_STANDBY)) == failover);
299 if (failover) {
300 QDict *resp;
302 resp = get_failover_negociated_event(qts);
303 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, id);
304 qobject_unref(resp);
307 return dev;
310 static void test_on(void)
312 QTestState *qts;
314 qts = machine_start(BASE_MACHINE
315 "-netdev user,id=hs0 "
316 "-device virtio-net,bus=root0,id=standby0,"
317 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
318 "-netdev user,id=hs1 "
319 "-device virtio-net,bus=root1,id=primary0,"
320 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0,
323 check_one_card(qts, true, "standby0", MAC_STANDBY0);
324 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
326 machine_stop(qts);
329 static void test_on_mismatch(void)
331 QTestState *qts;
332 QVirtioPCIDevice *vdev;
334 qts = machine_start(BASE_MACHINE
335 "-netdev user,id=hs0 "
336 "-device virtio-net,bus=root0,id=standby0,"
337 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
338 "-netdev user,id=hs1 "
339 "-device virtio-net,bus=root1,id=primary0,"
340 "failover_pair_id=standby1,netdev=hs1,mac="MAC_PRIMARY0,
343 check_one_card(qts, true, "standby0", MAC_STANDBY0);
344 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
346 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
348 check_one_card(qts, true, "standby0", MAC_STANDBY0);
349 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
351 qos_object_destroy((QOSGraphObject *)vdev);
352 machine_stop(qts);
355 static void test_off(void)
357 QTestState *qts;
358 QVirtioPCIDevice *vdev;
360 qts = machine_start(BASE_MACHINE
361 "-netdev user,id=hs0 "
362 "-device virtio-net,bus=root0,id=standby0,"
363 "failover=off,netdev=hs0,mac="MAC_STANDBY0" "
364 "-netdev user,id=hs1 "
365 "-device virtio-net,bus=root1,id=primary0,"
366 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0,
369 check_one_card(qts, true, "standby0", MAC_STANDBY0);
370 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
372 vdev = start_virtio_net(qts, 1, 0, "standby0", false);
374 check_one_card(qts, true, "standby0", MAC_STANDBY0);
375 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
377 qos_object_destroy((QOSGraphObject *)vdev);
378 machine_stop(qts);
381 static void test_enabled(void)
383 QTestState *qts;
384 QVirtioPCIDevice *vdev;
386 qts = machine_start(BASE_MACHINE
387 "-netdev user,id=hs0 "
388 "-device virtio-net,bus=root0,id=standby0,"
389 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
390 "-netdev user,id=hs1 "
391 "-device virtio-net,bus=root1,id=primary0,"
392 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
395 check_one_card(qts, true, "standby0", MAC_STANDBY0);
396 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
398 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
400 check_one_card(qts, true, "standby0", MAC_STANDBY0);
401 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
403 qos_object_destroy((QOSGraphObject *)vdev);
404 machine_stop(qts);
407 static void test_guest_off(void)
409 QTestState *qts;
410 QVirtioPCIDevice *vdev;
411 uint64_t features;
413 qts = machine_start(BASE_MACHINE
414 "-netdev user,id=hs0 "
415 "-device virtio-net,bus=root0,id=standby0,"
416 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
417 "-netdev user,id=hs1 "
418 "-device virtio-net,bus=root1,id=primary0,"
419 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
422 check_one_card(qts, true, "standby0", MAC_STANDBY0);
423 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
425 features = ~(QVIRTIO_F_BAD_FEATURE |
426 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
427 (1ull << VIRTIO_RING_F_EVENT_IDX) |
428 (1ull << VIRTIO_NET_F_STANDBY));
430 vdev = start_virtio_net_internal(qts, 1, 0, &features);
432 check_one_card(qts, true, "standby0", MAC_STANDBY0);
433 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
435 qos_object_destroy((QOSGraphObject *)vdev);
436 machine_stop(qts);
439 static void test_hotplug_1(void)
441 QTestState *qts;
442 QVirtioPCIDevice *vdev;
444 qts = machine_start(BASE_MACHINE
445 "-netdev user,id=hs0 "
446 "-device virtio-net,bus=root0,id=standby0,"
447 "failover=on,netdev=hs0,mac="MAC_STANDBY0" "
448 "-netdev user,id=hs1 ", 2);
450 check_one_card(qts, true, "standby0", MAC_STANDBY0);
451 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
453 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
455 check_one_card(qts, true, "standby0", MAC_STANDBY0);
456 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
458 qtest_qmp_device_add(qts, "virtio-net", "primary0",
459 "{'bus': 'root1',"
460 "'failover_pair_id': 'standby0',"
461 "'netdev': 'hs1',"
462 "'mac': '"MAC_PRIMARY0"'}");
464 check_one_card(qts, true, "standby0", MAC_STANDBY0);
465 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
467 qos_object_destroy((QOSGraphObject *)vdev);
468 machine_stop(qts);
471 static void test_hotplug_1_reverse(void)
473 QTestState *qts;
474 QVirtioPCIDevice *vdev;
476 qts = machine_start(BASE_MACHINE
477 "-netdev user,id=hs0 "
478 "-netdev user,id=hs1 "
479 "-device virtio-net,bus=root1,id=primary0,"
480 "failover_pair_id=standby0,netdev=hs1,mac="MAC_PRIMARY0" ",
483 check_one_card(qts, false, "standby0", MAC_STANDBY0);
484 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
486 qtest_qmp_device_add(qts, "virtio-net", "standby0",
487 "{'bus': 'root0',"
488 "'failover': 'on',"
489 "'netdev': 'hs0',"
490 "'mac': '"MAC_STANDBY0"'}");
492 check_one_card(qts, true, "standby0", MAC_STANDBY0);
493 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
495 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
497 check_one_card(qts, true, "standby0", MAC_STANDBY0);
498 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
500 qos_object_destroy((QOSGraphObject *)vdev);
501 machine_stop(qts);
504 static void test_hotplug_2(void)
506 QTestState *qts;
507 QVirtioPCIDevice *vdev;
509 qts = machine_start(BASE_MACHINE
510 "-netdev user,id=hs0 "
511 "-netdev user,id=hs1 ",
514 check_one_card(qts, false, "standby0", MAC_STANDBY0);
515 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
517 qtest_qmp_device_add(qts, "virtio-net", "standby0",
518 "{'bus': 'root0',"
519 "'failover': 'on',"
520 "'netdev': 'hs0',"
521 "'mac': '"MAC_STANDBY0"'}");
523 check_one_card(qts, true, "standby0", MAC_STANDBY0);
524 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
526 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
528 check_one_card(qts, true, "standby0", MAC_STANDBY0);
529 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
531 qtest_qmp_device_add(qts, "virtio-net", "primary0",
532 "{'bus': 'root1',"
533 "'failover_pair_id': 'standby0',"
534 "'netdev': 'hs1',"
535 "'mac': '"MAC_PRIMARY0"'}");
537 check_one_card(qts, true, "standby0", MAC_STANDBY0);
538 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
540 qos_object_destroy((QOSGraphObject *)vdev);
541 machine_stop(qts);
544 static void test_hotplug_2_reverse(void)
546 QTestState *qts;
547 QVirtioPCIDevice *vdev;
549 qts = machine_start(BASE_MACHINE
550 "-netdev user,id=hs0 "
551 "-netdev user,id=hs1 ",
554 check_one_card(qts, false, "standby0", MAC_STANDBY0);
555 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
557 qtest_qmp_device_add(qts, "virtio-net", "primary0",
558 "{'bus': 'root1',"
559 "'failover_pair_id': 'standby0',"
560 "'netdev': 'hs1',"
561 "'mac': '"MAC_PRIMARY0"'}");
563 check_one_card(qts, false, "standby0", MAC_STANDBY0);
564 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
566 qtest_qmp_device_add(qts, "virtio-net", "standby0",
567 "{'bus': 'root0',"
568 "'failover': 'on',"
569 "'netdev': 'hs0',"
570 "'rombar': 0,"
571 "'romfile': '',"
572 "'mac': '"MAC_STANDBY0"'}");
575 * XXX: sounds like a bug:
576 * The primary should be hidden until the virtio-net driver
577 * negotiates the VIRTIO_NET_F_STANDBY feature by start_virtio_net()
579 check_one_card(qts, true, "standby0", MAC_STANDBY0);
580 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
582 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
584 check_one_card(qts, true, "standby0", MAC_STANDBY0);
585 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
587 qos_object_destroy((QOSGraphObject *)vdev);
588 machine_stop(qts);
591 #ifndef _WIN32
592 static QDict *migrate_status(QTestState *qts)
594 QDict *resp, *ret;
596 resp = qtest_qmp(qts, "{ 'execute': 'query-migrate' }");
597 g_assert(qdict_haskey(resp, "return"));
599 ret = qdict_get_qdict(resp, "return");
600 g_assert(qdict_haskey(ret, "status"));
601 qobject_ref(ret);
602 qobject_unref(resp);
604 return ret;
607 static QDict *get_unplug_primary_event(QTestState *qts)
609 QDict *resp;
610 QDict *data;
612 resp = qtest_qmp_eventwait_ref(qts, "UNPLUG_PRIMARY");
613 g_assert(qdict_haskey(resp, "data"));
615 data = qdict_get_qdict(resp, "data");
616 g_assert(qdict_haskey(data, "device-id"));
617 qobject_ref(data);
618 qobject_unref(resp);
620 return data;
623 static void test_migrate_out(gconstpointer opaque)
625 QTestState *qts;
626 QDict *resp, *args, *ret;
627 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
628 const gchar *status;
629 QVirtioPCIDevice *vdev;
631 qts = machine_start(BASE_MACHINE
632 "-netdev user,id=hs0 "
633 "-netdev user,id=hs1 ",
636 check_one_card(qts, false, "standby0", MAC_STANDBY0);
637 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
639 qtest_qmp_device_add(qts, "virtio-net", "standby0",
640 "{'bus': 'root0',"
641 "'failover': 'on',"
642 "'netdev': 'hs0',"
643 "'mac': '"MAC_STANDBY0"'}");
645 check_one_card(qts, true, "standby0", MAC_STANDBY0);
646 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
648 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
650 check_one_card(qts, true, "standby0", MAC_STANDBY0);
651 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
653 qtest_qmp_device_add(qts, "virtio-net", "primary0",
654 "{'bus': 'root1',"
655 "'failover_pair_id': 'standby0',"
656 "'netdev': 'hs1',"
657 "'rombar': 0,"
658 "'romfile': '',"
659 "'mac': '"MAC_PRIMARY0"'}");
661 check_one_card(qts, true, "standby0", MAC_STANDBY0);
662 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
664 args = qdict_from_jsonf_nofail("{}");
665 g_assert_nonnull(args);
666 qdict_put_str(args, "uri", uri);
668 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
669 g_assert(qdict_haskey(resp, "return"));
670 qobject_unref(resp);
672 /* the event is sent when QEMU asks the OS to unplug the card */
673 resp = get_unplug_primary_event(qts);
674 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
675 qobject_unref(resp);
677 /* wait the end of the migration setup phase */
678 while (true) {
679 ret = migrate_status(qts);
681 status = qdict_get_str(ret, "status");
682 if (strcmp(status, "wait-unplug") == 0) {
683 qobject_unref(ret);
684 break;
687 /* The migration must not start if the card is not ejected */
688 g_assert_cmpstr(status, !=, "active");
689 g_assert_cmpstr(status, !=, "completed");
690 g_assert_cmpstr(status, !=, "failed");
691 g_assert_cmpstr(status, !=, "cancelling");
692 g_assert_cmpstr(status, !=, "cancelled");
694 qobject_unref(ret);
697 if (g_test_slow()) {
698 /* check we stay in wait-unplug while the card is not ejected */
699 for (int i = 0; i < 5; i++) {
700 sleep(1);
701 ret = migrate_status(qts);
702 status = qdict_get_str(ret, "status");
703 g_assert_cmpstr(status, ==, "wait-unplug");
704 qobject_unref(ret);
708 /* OS unplugs the cards, QEMU can move from wait-unplug state */
709 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
711 while (true) {
712 ret = migrate_status(qts);
714 status = qdict_get_str(ret, "status");
715 if (strcmp(status, "completed") == 0) {
716 qobject_unref(ret);
717 break;
719 g_assert_cmpstr(status, !=, "failed");
720 g_assert_cmpstr(status, !=, "cancelling");
721 g_assert_cmpstr(status, !=, "cancelled");
722 qobject_unref(ret);
725 qtest_qmp_eventwait(qts, "STOP");
728 * in fact, the card is ejected from the point of view of kernel
729 * but not really from QEMU to be able to hotplug it back if
730 * migration fails. So we can't check that:
731 * check_one_card(qts, true, "standby0", MAC_STANDBY0);
732 * check_one_card(qts, false, "primary0", MAC_PRIMARY0);
735 qos_object_destroy((QOSGraphObject *)vdev);
736 machine_stop(qts);
739 static QDict *get_migration_event(QTestState *qts)
741 QDict *resp;
742 QDict *data;
744 resp = qtest_qmp_eventwait_ref(qts, "MIGRATION");
745 g_assert(qdict_haskey(resp, "data"));
747 data = qdict_get_qdict(resp, "data");
748 g_assert(qdict_haskey(data, "status"));
749 qobject_ref(data);
750 qobject_unref(resp);
752 return data;
755 static void test_migrate_in(gconstpointer opaque)
757 QTestState *qts;
758 QDict *resp, *args, *ret;
759 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
761 qts = machine_start(BASE_MACHINE
762 "-netdev user,id=hs0 "
763 "-netdev user,id=hs1 "
764 "-incoming defer ",
767 check_one_card(qts, false, "standby0", MAC_STANDBY0);
768 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
770 qtest_qmp_device_add(qts, "virtio-net", "standby0",
771 "{'bus': 'root0',"
772 "'failover': 'on',"
773 "'netdev': 'hs0',"
774 "'mac': '"MAC_STANDBY0"'}");
776 check_one_card(qts, true, "standby0", MAC_STANDBY0);
777 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
779 qtest_qmp_device_add(qts, "virtio-net", "primary0",
780 "{'bus': 'root1',"
781 "'failover_pair_id': 'standby0',"
782 "'netdev': 'hs1',"
783 "'rombar': 0,"
784 "'romfile': '',"
785 "'mac': '"MAC_PRIMARY0"'}");
787 check_one_card(qts, true, "standby0", MAC_STANDBY0);
788 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
790 args = qdict_from_jsonf_nofail("{}");
791 g_assert_nonnull(args);
792 qdict_put_str(args, "uri", uri);
794 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
795 args);
796 g_assert(qdict_haskey(resp, "return"));
797 qobject_unref(resp);
799 resp = get_migration_event(qts);
800 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
801 qobject_unref(resp);
803 resp = get_failover_negociated_event(qts);
804 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
805 qobject_unref(resp);
807 check_one_card(qts, true, "standby0", MAC_STANDBY0);
808 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
810 qtest_qmp_eventwait(qts, "RESUME");
812 ret = migrate_status(qts);
813 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
814 qobject_unref(ret);
816 machine_stop(qts);
819 static void test_off_migrate_out(gconstpointer opaque)
821 QTestState *qts;
822 QDict *resp, *args, *ret;
823 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
824 const gchar *status;
825 QVirtioPCIDevice *vdev;
827 qts = machine_start(BASE_MACHINE
828 "-netdev user,id=hs0 "
829 "-netdev user,id=hs1 ",
832 check_one_card(qts, false, "standby0", MAC_STANDBY0);
833 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
835 qtest_qmp_device_add(qts, "virtio-net", "standby0",
836 "{'bus': 'root0',"
837 "'failover': 'off',"
838 "'netdev': 'hs0',"
839 "'mac': '"MAC_STANDBY0"'}");
841 check_one_card(qts, true, "standby0", MAC_STANDBY0);
842 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
844 qtest_qmp_device_add(qts, "virtio-net", "primary0",
845 "{'bus': 'root1',"
846 "'failover_pair_id': 'standby0',"
847 "'netdev': 'hs1',"
848 "'rombar': 0,"
849 "'romfile': '',"
850 "'mac': '"MAC_PRIMARY0"'}");
852 check_one_card(qts, true, "standby0", MAC_STANDBY0);
853 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
855 vdev = start_virtio_net(qts, 1, 0, "standby0", false);
857 check_one_card(qts, true, "standby0", MAC_STANDBY0);
858 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
860 args = qdict_from_jsonf_nofail("{}");
861 g_assert_nonnull(args);
862 qdict_put_str(args, "uri", uri);
864 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
865 g_assert(qdict_haskey(resp, "return"));
866 qobject_unref(resp);
868 while (true) {
869 ret = migrate_status(qts);
871 status = qdict_get_str(ret, "status");
872 if (strcmp(status, "completed") == 0) {
873 qobject_unref(ret);
874 break;
876 g_assert_cmpstr(status, !=, "failed");
877 g_assert_cmpstr(status, !=, "cancelling");
878 g_assert_cmpstr(status, !=, "cancelled");
879 qobject_unref(ret);
882 qtest_qmp_eventwait(qts, "STOP");
884 qos_object_destroy((QOSGraphObject *)vdev);
885 machine_stop(qts);
888 static void test_off_migrate_in(gconstpointer opaque)
890 QTestState *qts;
891 QDict *resp, *args, *ret;
892 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
894 qts = machine_start(BASE_MACHINE
895 "-netdev user,id=hs0 "
896 "-netdev user,id=hs1 "
897 "-incoming defer ",
900 check_one_card(qts, false, "standby0", MAC_STANDBY0);
901 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
903 qtest_qmp_device_add(qts, "virtio-net", "standby0",
904 "{'bus': 'root0',"
905 "'failover': 'off',"
906 "'netdev': 'hs0',"
907 "'mac': '"MAC_STANDBY0"'}");
909 check_one_card(qts, true, "standby0", MAC_STANDBY0);
910 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
912 qtest_qmp_device_add(qts, "virtio-net", "primary0",
913 "{'bus': 'root1',"
914 "'failover_pair_id': 'standby0',"
915 "'netdev': 'hs1',"
916 "'rombar': 0,"
917 "'romfile': '',"
918 "'mac': '"MAC_PRIMARY0"'}");
920 check_one_card(qts, true, "standby0", MAC_STANDBY0);
921 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
923 args = qdict_from_jsonf_nofail("{}");
924 g_assert_nonnull(args);
925 qdict_put_str(args, "uri", uri);
927 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
928 args);
929 g_assert(qdict_haskey(resp, "return"));
930 qobject_unref(resp);
932 resp = get_migration_event(qts);
933 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
934 qobject_unref(resp);
936 check_one_card(qts, true, "standby0", MAC_STANDBY0);
937 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
939 qtest_qmp_eventwait(qts, "RESUME");
941 ret = migrate_status(qts);
942 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
943 qobject_unref(ret);
945 machine_stop(qts);
948 static void test_guest_off_migrate_out(gconstpointer opaque)
950 QTestState *qts;
951 QDict *resp, *args, *ret;
952 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
953 const gchar *status;
954 QVirtioPCIDevice *vdev;
955 uint64_t features;
957 qts = machine_start(BASE_MACHINE
958 "-netdev user,id=hs0 "
959 "-netdev user,id=hs1 ",
962 check_one_card(qts, false, "standby0", MAC_STANDBY0);
963 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
965 qtest_qmp_device_add(qts, "virtio-net", "standby0",
966 "{'bus': 'root0',"
967 "'failover': 'on',"
968 "'netdev': 'hs0',"
969 "'mac': '"MAC_STANDBY0"'}");
971 check_one_card(qts, true, "standby0", MAC_STANDBY0);
972 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
974 qtest_qmp_device_add(qts, "virtio-net", "primary0",
975 "{'bus': 'root1',"
976 "'failover_pair_id': 'standby0',"
977 "'netdev': 'hs1',"
978 "'rombar': 0,"
979 "'romfile': '',"
980 "'mac': '"MAC_PRIMARY0"'}");
982 check_one_card(qts, true, "standby0", MAC_STANDBY0);
983 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
985 features = ~(QVIRTIO_F_BAD_FEATURE |
986 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
987 (1ull << VIRTIO_RING_F_EVENT_IDX) |
988 (1ull << VIRTIO_NET_F_STANDBY));
990 vdev = start_virtio_net_internal(qts, 1, 0, &features);
992 check_one_card(qts, true, "standby0", MAC_STANDBY0);
993 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
995 args = qdict_from_jsonf_nofail("{}");
996 g_assert_nonnull(args);
997 qdict_put_str(args, "uri", uri);
999 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1000 g_assert(qdict_haskey(resp, "return"));
1001 qobject_unref(resp);
1003 while (true) {
1004 ret = migrate_status(qts);
1006 status = qdict_get_str(ret, "status");
1007 if (strcmp(status, "completed") == 0) {
1008 qobject_unref(ret);
1009 break;
1011 g_assert_cmpstr(status, !=, "failed");
1012 g_assert_cmpstr(status, !=, "cancelling");
1013 g_assert_cmpstr(status, !=, "cancelled");
1014 qobject_unref(ret);
1017 qtest_qmp_eventwait(qts, "STOP");
1019 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1020 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1022 qos_object_destroy((QOSGraphObject *)vdev);
1023 machine_stop(qts);
1026 static void test_guest_off_migrate_in(gconstpointer opaque)
1028 QTestState *qts;
1029 QDict *resp, *args, *ret;
1030 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
1032 qts = machine_start(BASE_MACHINE
1033 "-netdev user,id=hs0 "
1034 "-netdev user,id=hs1 "
1035 "-incoming defer ",
1038 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1039 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1041 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1042 "{'bus': 'root0',"
1043 "'failover': 'on',"
1044 "'netdev': 'hs0',"
1045 "'mac': '"MAC_STANDBY0"'}");
1047 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1048 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1050 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1051 "{'bus': 'root1',"
1052 "'failover_pair_id': 'standby0',"
1053 "'netdev': 'hs1',"
1054 "'rombar': 0,"
1055 "'romfile': '',"
1056 "'mac': '"MAC_PRIMARY0"'}");
1058 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1059 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1061 args = qdict_from_jsonf_nofail("{}");
1062 g_assert_nonnull(args);
1063 qdict_put_str(args, "uri", uri);
1065 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
1066 args);
1067 g_assert(qdict_haskey(resp, "return"));
1068 qobject_unref(resp);
1070 resp = get_migration_event(qts);
1071 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
1072 qobject_unref(resp);
1074 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1075 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1077 qtest_qmp_eventwait(qts, "RESUME");
1079 ret = migrate_status(qts);
1080 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
1081 qobject_unref(ret);
1083 machine_stop(qts);
1086 static void test_migrate_guest_off_abort(gconstpointer opaque)
1088 QTestState *qts;
1089 QDict *resp, *args, *ret;
1090 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1091 const gchar *status;
1092 QVirtioPCIDevice *vdev;
1093 uint64_t features;
1095 qts = machine_start(BASE_MACHINE
1096 "-netdev user,id=hs0 "
1097 "-netdev user,id=hs1 ",
1100 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1101 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1103 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1104 "{'bus': 'root0',"
1105 "'failover': 'on',"
1106 "'netdev': 'hs0',"
1107 "'mac': '"MAC_STANDBY0"'}");
1109 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1110 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1112 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1113 "{'bus': 'root1',"
1114 "'failover_pair_id': 'standby0',"
1115 "'netdev': 'hs1',"
1116 "'rombar': 0,"
1117 "'romfile': '',"
1118 "'mac': '"MAC_PRIMARY0"'}");
1120 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1121 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1123 features = ~(QVIRTIO_F_BAD_FEATURE |
1124 (1ull << VIRTIO_RING_F_INDIRECT_DESC) |
1125 (1ull << VIRTIO_RING_F_EVENT_IDX) |
1126 (1ull << VIRTIO_NET_F_STANDBY));
1128 vdev = start_virtio_net_internal(qts, 1, 0, &features);
1130 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1131 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1133 args = qdict_from_jsonf_nofail("{}");
1134 g_assert_nonnull(args);
1135 qdict_put_str(args, "uri", uri);
1137 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1138 g_assert(qdict_haskey(resp, "return"));
1139 qobject_unref(resp);
1141 while (true) {
1142 ret = migrate_status(qts);
1144 status = qdict_get_str(ret, "status");
1145 if (strcmp(status, "completed") == 0) {
1146 g_test_skip("Failed to cancel the migration");
1147 qobject_unref(ret);
1148 goto out;
1150 if (strcmp(status, "active") == 0) {
1151 qobject_unref(ret);
1152 break;
1154 g_assert_cmpstr(status, !=, "failed");
1155 qobject_unref(ret);
1158 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1159 g_assert(qdict_haskey(resp, "return"));
1160 qobject_unref(resp);
1162 while (true) {
1163 ret = migrate_status(qts);
1164 status = qdict_get_str(ret, "status");
1165 if (strcmp(status, "completed") == 0) {
1166 g_test_skip("Failed to cancel the migration");
1167 qobject_unref(ret);
1168 goto out;
1170 if (strcmp(status, "cancelled") == 0) {
1171 qobject_unref(ret);
1172 break;
1174 g_assert_cmpstr(status, !=, "failed");
1175 g_assert_cmpstr(status, !=, "active");
1176 qobject_unref(ret);
1179 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1180 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1182 out:
1183 qos_object_destroy((QOSGraphObject *)vdev);
1184 machine_stop(qts);
1187 static void test_migrate_abort_wait_unplug(gconstpointer opaque)
1189 QTestState *qts;
1190 QDict *resp, *args, *ret;
1191 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1192 const gchar *status;
1193 QVirtioPCIDevice *vdev;
1195 qts = machine_start(BASE_MACHINE
1196 "-netdev user,id=hs0 "
1197 "-netdev user,id=hs1 ",
1200 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1201 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1203 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1204 "{'bus': 'root0',"
1205 "'failover': 'on',"
1206 "'netdev': 'hs0',"
1207 "'mac': '"MAC_STANDBY0"'}");
1209 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1210 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1212 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
1214 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1215 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1217 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1218 "{'bus': 'root1',"
1219 "'failover_pair_id': 'standby0',"
1220 "'netdev': 'hs1',"
1221 "'rombar': 0,"
1222 "'romfile': '',"
1223 "'mac': '"MAC_PRIMARY0"'}");
1225 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1226 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1228 args = qdict_from_jsonf_nofail("{}");
1229 g_assert_nonnull(args);
1230 qdict_put_str(args, "uri", uri);
1232 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1233 g_assert(qdict_haskey(resp, "return"));
1234 qobject_unref(resp);
1236 /* the event is sent when QEMU asks the OS to unplug the card */
1237 resp = get_unplug_primary_event(qts);
1238 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
1239 qobject_unref(resp);
1241 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1242 g_assert(qdict_haskey(resp, "return"));
1243 qobject_unref(resp);
1245 /* migration has been cancelled while the unplug was in progress */
1247 /* while the card is not ejected, we must be in "cancelling" state */
1248 ret = migrate_status(qts);
1250 status = qdict_get_str(ret, "status");
1251 g_assert_cmpstr(status, ==, "cancelling");
1252 qobject_unref(ret);
1254 /* OS unplugs the cards, QEMU can move from wait-unplug state */
1255 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1257 while (true) {
1258 ret = migrate_status(qts);
1260 status = qdict_get_str(ret, "status");
1261 if (strcmp(status, "cancelled") == 0) {
1262 qobject_unref(ret);
1263 break;
1265 g_assert_cmpstr(status, ==, "cancelling");
1266 qobject_unref(ret);
1269 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1270 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1272 qos_object_destroy((QOSGraphObject *)vdev);
1273 machine_stop(qts);
1276 static void test_migrate_abort_active(gconstpointer opaque)
1278 QTestState *qts;
1279 QDict *resp, *args, *ret;
1280 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1281 const gchar *status;
1282 QVirtioPCIDevice *vdev;
1284 qts = machine_start(BASE_MACHINE
1285 "-netdev user,id=hs0 "
1286 "-netdev user,id=hs1 ",
1289 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1290 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1292 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1293 "{'bus': 'root0',"
1294 "'failover': 'on',"
1295 "'netdev': 'hs0',"
1296 "'mac': '"MAC_STANDBY0"'}");
1298 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1299 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1301 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
1303 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1304 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1306 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1307 "{'bus': 'root1',"
1308 "'failover_pair_id': 'standby0',"
1309 "'netdev': 'hs1',"
1310 "'rombar': 0,"
1311 "'romfile': '',"
1312 "'mac': '"MAC_PRIMARY0"'}");
1314 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1315 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1317 args = qdict_from_jsonf_nofail("{}");
1318 g_assert_nonnull(args);
1319 qdict_put_str(args, "uri", uri);
1321 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1322 g_assert(qdict_haskey(resp, "return"));
1323 qobject_unref(resp);
1325 /* the event is sent when QEMU asks the OS to unplug the card */
1326 resp = get_unplug_primary_event(qts);
1327 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
1328 qobject_unref(resp);
1330 /* OS unplugs the cards, QEMU can move from wait-unplug state */
1331 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1333 while (true) {
1334 ret = migrate_status(qts);
1336 status = qdict_get_str(ret, "status");
1337 g_assert_cmpstr(status, !=, "failed");
1338 if (strcmp(status, "wait-unplug") != 0) {
1339 qobject_unref(ret);
1340 break;
1342 qobject_unref(ret);
1345 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1346 g_assert(qdict_haskey(resp, "return"));
1347 qobject_unref(resp);
1349 while (true) {
1350 ret = migrate_status(qts);
1352 status = qdict_get_str(ret, "status");
1353 if (strcmp(status, "completed") == 0) {
1354 g_test_skip("Failed to cancel the migration");
1355 qobject_unref(ret);
1356 goto out;
1358 if (strcmp(status, "cancelled") == 0) {
1359 qobject_unref(ret);
1360 break;
1362 g_assert_cmpstr(status, !=, "failed");
1363 g_assert_cmpstr(status, !=, "active");
1364 qobject_unref(ret);
1367 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1368 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1370 out:
1371 qos_object_destroy((QOSGraphObject *)vdev);
1372 machine_stop(qts);
1375 static void test_migrate_off_abort(gconstpointer opaque)
1377 QTestState *qts;
1378 QDict *resp, *args, *ret;
1379 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1380 const gchar *status;
1381 QVirtioPCIDevice *vdev;
1383 qts = machine_start(BASE_MACHINE
1384 "-netdev user,id=hs0 "
1385 "-netdev user,id=hs1 ",
1388 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1389 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1391 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1392 "{'bus': 'root0',"
1393 "'failover': 'off',"
1394 "'netdev': 'hs0',"
1395 "'mac': '"MAC_STANDBY0"'}");
1397 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1398 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1400 vdev = start_virtio_net(qts, 1, 0, "standby0", false);
1402 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1403 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1405 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1406 "{'bus': 'root1',"
1407 "'failover_pair_id': 'standby0',"
1408 "'netdev': 'hs1',"
1409 "'rombar': 0,"
1410 "'romfile': '',"
1411 "'mac': '"MAC_PRIMARY0"'}");
1413 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1414 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1416 args = qdict_from_jsonf_nofail("{}");
1417 g_assert_nonnull(args);
1418 qdict_put_str(args, "uri", uri);
1420 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1421 g_assert(qdict_haskey(resp, "return"));
1422 qobject_unref(resp);
1424 while (true) {
1425 ret = migrate_status(qts);
1427 status = qdict_get_str(ret, "status");
1428 if (strcmp(status, "active") == 0) {
1429 qobject_unref(ret);
1430 break;
1432 g_assert_cmpstr(status, !=, "failed");
1433 qobject_unref(ret);
1436 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1437 g_assert(qdict_haskey(resp, "return"));
1438 qobject_unref(resp);
1440 while (true) {
1441 ret = migrate_status(qts);
1443 status = qdict_get_str(ret, "status");
1444 if (strcmp(status, "completed") == 0) {
1445 g_test_skip("Failed to cancel the migration");
1446 qobject_unref(ret);
1447 goto out;
1449 if (strcmp(status, "cancelled") == 0) {
1450 qobject_unref(ret);
1451 break;
1453 g_assert_cmpstr(status, !=, "failed");
1454 g_assert_cmpstr(status, !=, "active");
1455 qobject_unref(ret);
1458 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1459 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1461 out:
1462 qos_object_destroy((QOSGraphObject *)vdev);
1463 machine_stop(qts);
1466 static void test_migrate_abort_timeout(gconstpointer opaque)
1468 QTestState *qts;
1469 QDict *resp, *args, *ret;
1470 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1471 const gchar *status;
1472 int total;
1473 QVirtioPCIDevice *vdev;
1475 qts = machine_start(BASE_MACHINE
1476 "-netdev user,id=hs0 "
1477 "-netdev user,id=hs1 ",
1480 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1481 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1483 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1484 "{'bus': 'root0',"
1485 "'failover': 'on',"
1486 "'netdev': 'hs0',"
1487 "'mac': '"MAC_STANDBY0"'}");
1489 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1490 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1492 vdev = start_virtio_net(qts, 1, 0, "standby0", true);
1494 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1495 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1497 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1498 "{'bus': 'root1',"
1499 "'failover_pair_id': 'standby0',"
1500 "'netdev': 'hs1',"
1501 "'rombar': 0,"
1502 "'romfile': '',"
1503 "'mac': '"MAC_PRIMARY0"'}");
1505 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1506 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1508 args = qdict_from_jsonf_nofail("{}");
1509 g_assert_nonnull(args);
1510 qdict_put_str(args, "uri", uri);
1512 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1513 g_assert(qdict_haskey(resp, "return"));
1514 qobject_unref(resp);
1516 /* the event is sent when QEMU asks the OS to unplug the card */
1517 resp = get_unplug_primary_event(qts);
1518 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0");
1519 qobject_unref(resp);
1521 resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }");
1522 g_assert(qdict_haskey(resp, "return"));
1523 qobject_unref(resp);
1525 /* migration has been cancelled while the unplug was in progress */
1527 /* while the card is not ejected, we must be in "cancelling" state */
1529 total = 0;
1530 while (true) {
1531 ret = migrate_status(qts);
1533 status = qdict_get_str(ret, "status");
1534 if (strcmp(status, "cancelled") == 0) {
1535 qobject_unref(ret);
1536 break;
1538 g_assert_cmpstr(status, ==, "cancelling");
1539 g_assert(qdict_haskey(ret, "total-time"));
1540 total = qdict_get_int(ret, "total-time");
1541 qobject_unref(ret);
1545 * migration timeout in this case is 30 seconds
1546 * check we exit on the timeout (ms)
1548 g_assert_cmpint(total, >, 30000);
1550 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1551 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1553 qos_object_destroy((QOSGraphObject *)vdev);
1554 machine_stop(qts);
1557 static void test_multi_out(gconstpointer opaque)
1559 QTestState *qts;
1560 QDict *resp, *args, *ret;
1561 g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque);
1562 const gchar *status, *expected;
1563 QVirtioPCIDevice *vdev0, *vdev1;
1565 qts = machine_start(BASE_MACHINE
1566 "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
1567 "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
1568 "-netdev user,id=hs0 "
1569 "-netdev user,id=hs1 "
1570 "-netdev user,id=hs2 "
1571 "-netdev user,id=hs3 ",
1574 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1575 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1576 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1577 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1579 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1580 "{'bus': 'root0',"
1581 "'failover': 'on',"
1582 "'netdev': 'hs0',"
1583 "'mac': '"MAC_STANDBY0"'}");
1585 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1586 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1587 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1588 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1590 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1591 "{'bus': 'root1',"
1592 "'failover_pair_id': 'standby0',"
1593 "'netdev': 'hs1',"
1594 "'rombar': 0,"
1595 "'romfile': '',"
1596 "'mac': '"MAC_PRIMARY0"'}");
1598 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1599 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1600 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1601 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1603 vdev0 = start_virtio_net(qts, 1, 0, "standby0", true);
1605 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1606 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1607 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1608 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1610 qtest_qmp_device_add(qts, "virtio-net", "standby1",
1611 "{'bus': 'root2',"
1612 "'failover': 'on',"
1613 "'netdev': 'hs2',"
1614 "'mac': '"MAC_STANDBY1"'}");
1616 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1617 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1618 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1619 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1621 qtest_qmp_device_add(qts, "virtio-net", "primary1",
1622 "{'bus': 'root3',"
1623 "'failover_pair_id': 'standby1',"
1624 "'netdev': 'hs3',"
1625 "'rombar': 0,"
1626 "'romfile': '',"
1627 "'mac': '"MAC_PRIMARY1"'}");
1629 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1630 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1631 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1632 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1634 vdev1 = start_virtio_net(qts, 3, 0, "standby1", true);
1636 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1637 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1638 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1639 check_one_card(qts, true, "primary1", MAC_PRIMARY1);
1641 args = qdict_from_jsonf_nofail("{}");
1642 g_assert_nonnull(args);
1643 qdict_put_str(args, "uri", uri);
1645 resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args);
1646 g_assert(qdict_haskey(resp, "return"));
1647 qobject_unref(resp);
1649 /* the event is sent when QEMU asks the OS to unplug the card */
1650 resp = get_unplug_primary_event(qts);
1651 if (strcmp(qdict_get_str(resp, "device-id"), "primary0") == 0) {
1652 expected = "primary1";
1653 } else if (strcmp(qdict_get_str(resp, "device-id"), "primary1") == 0) {
1654 expected = "primary0";
1655 } else {
1656 g_assert_not_reached();
1658 qobject_unref(resp);
1660 resp = get_unplug_primary_event(qts);
1661 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, expected);
1662 qobject_unref(resp);
1664 /* wait the end of the migration setup phase */
1665 while (true) {
1666 ret = migrate_status(qts);
1668 status = qdict_get_str(ret, "status");
1669 if (strcmp(status, "wait-unplug") == 0) {
1670 qobject_unref(ret);
1671 break;
1674 /* The migration must not start if the card is not ejected */
1675 g_assert_cmpstr(status, !=, "active");
1676 g_assert_cmpstr(status, !=, "completed");
1677 g_assert_cmpstr(status, !=, "failed");
1678 g_assert_cmpstr(status, !=, "cancelling");
1679 g_assert_cmpstr(status, !=, "cancelled");
1681 qobject_unref(ret);
1684 /* OS unplugs primary1, but we must wait the second */
1685 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1687 ret = migrate_status(qts);
1688 status = qdict_get_str(ret, "status");
1689 g_assert_cmpstr(status, ==, "wait-unplug");
1690 qobject_unref(ret);
1692 if (g_test_slow()) {
1693 /* check we stay in wait-unplug while the card is not ejected */
1694 for (int i = 0; i < 5; i++) {
1695 sleep(1);
1696 ret = migrate_status(qts);
1697 status = qdict_get_str(ret, "status");
1698 g_assert_cmpstr(status, ==, "wait-unplug");
1699 qobject_unref(ret);
1703 /* OS unplugs primary0, QEMU can move from wait-unplug state */
1704 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_SEL_BASE, 2);
1705 qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1);
1707 while (true) {
1708 ret = migrate_status(qts);
1710 status = qdict_get_str(ret, "status");
1711 if (strcmp(status, "completed") == 0) {
1712 qobject_unref(ret);
1713 break;
1715 g_assert_cmpstr(status, !=, "failed");
1716 g_assert_cmpstr(status, !=, "cancelling");
1717 g_assert_cmpstr(status, !=, "cancelled");
1718 qobject_unref(ret);
1721 qtest_qmp_eventwait(qts, "STOP");
1723 qos_object_destroy((QOSGraphObject *)vdev0);
1724 qos_object_destroy((QOSGraphObject *)vdev1);
1725 machine_stop(qts);
1728 static void test_multi_in(gconstpointer opaque)
1730 QTestState *qts;
1731 QDict *resp, *args, *ret;
1732 g_autofree gchar *uri = g_strdup_printf("exec: cat %s", (gchar *)opaque);
1734 qts = machine_start(BASE_MACHINE
1735 "-device pcie-root-port,id=root2,addr=0x3,bus=pcie.0,chassis=3 "
1736 "-device pcie-root-port,id=root3,addr=0x4,bus=pcie.0,chassis=4 "
1737 "-netdev user,id=hs0 "
1738 "-netdev user,id=hs1 "
1739 "-netdev user,id=hs2 "
1740 "-netdev user,id=hs3 "
1741 "-incoming defer ",
1744 check_one_card(qts, false, "standby0", MAC_STANDBY0);
1745 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1746 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1747 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1749 qtest_qmp_device_add(qts, "virtio-net", "standby0",
1750 "{'bus': 'root0',"
1751 "'failover': 'on',"
1752 "'netdev': 'hs0',"
1753 "'mac': '"MAC_STANDBY0"'}");
1755 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1756 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1757 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1758 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1760 qtest_qmp_device_add(qts, "virtio-net", "primary0",
1761 "{'bus': 'root1',"
1762 "'failover_pair_id': 'standby0',"
1763 "'netdev': 'hs1',"
1764 "'rombar': 0,"
1765 "'romfile': '',"
1766 "'mac': '"MAC_PRIMARY0"'}");
1768 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1769 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1770 check_one_card(qts, false, "standby1", MAC_STANDBY1);
1771 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1773 qtest_qmp_device_add(qts, "virtio-net", "standby1",
1774 "{'bus': 'root2',"
1775 "'failover': 'on',"
1776 "'netdev': 'hs2',"
1777 "'mac': '"MAC_STANDBY1"'}");
1779 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1780 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1781 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1782 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1784 qtest_qmp_device_add(qts, "virtio-net", "primary1",
1785 "{'bus': 'root3',"
1786 "'failover_pair_id': 'standby1',"
1787 "'netdev': 'hs3',"
1788 "'rombar': 0,"
1789 "'romfile': '',"
1790 "'mac': '"MAC_PRIMARY1"'}");
1792 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1793 check_one_card(qts, false, "primary0", MAC_PRIMARY0);
1794 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1795 check_one_card(qts, false, "primary1", MAC_PRIMARY1);
1797 args = qdict_from_jsonf_nofail("{}");
1798 g_assert_nonnull(args);
1799 qdict_put_str(args, "uri", uri);
1801 resp = qtest_qmp(qts, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
1802 args);
1803 g_assert(qdict_haskey(resp, "return"));
1804 qobject_unref(resp);
1806 resp = get_migration_event(qts);
1807 g_assert_cmpstr(qdict_get_str(resp, "status"), ==, "setup");
1808 qobject_unref(resp);
1810 resp = get_failover_negociated_event(qts);
1811 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0");
1812 qobject_unref(resp);
1814 resp = get_failover_negociated_event(qts);
1815 g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby1");
1816 qobject_unref(resp);
1818 check_one_card(qts, true, "standby0", MAC_STANDBY0);
1819 check_one_card(qts, true, "primary0", MAC_PRIMARY0);
1820 check_one_card(qts, true, "standby1", MAC_STANDBY1);
1821 check_one_card(qts, true, "primary1", MAC_PRIMARY1);
1823 qtest_qmp_eventwait(qts, "RESUME");
1825 ret = migrate_status(qts);
1826 g_assert_cmpstr(qdict_get_str(ret, "status"), ==, "completed");
1827 qobject_unref(ret);
1829 machine_stop(qts);
1831 #endif /* _WIN32 */
1833 int main(int argc, char **argv)
1835 gchar *tmpfile;
1836 int ret;
1838 g_test_init(&argc, &argv, NULL);
1840 ret = g_file_open_tmp("failover_test_migrate-XXXXXX", &tmpfile, NULL);
1841 g_assert_true(ret >= 0);
1842 close(ret);
1844 /* parameters tests */
1845 qtest_add_func("failover-virtio-net/params/error/id", test_error_id);
1846 qtest_add_func("failover-virtio-net/params/error/pcie", test_error_pcie);
1847 qtest_add_func("failover-virtio-net/params/on", test_on);
1848 qtest_add_func("failover-virtio-net/params/on_mismatch",
1849 test_on_mismatch);
1850 qtest_add_func("failover-virtio-net/params/off", test_off);
1851 qtest_add_func("failover-virtio-net/params/enabled", test_enabled);
1852 qtest_add_func("failover-virtio-net/params/guest_off", test_guest_off);
1854 /* hotplug tests */
1855 qtest_add_func("failover-virtio-net/hotplug/1", test_hotplug_1);
1856 qtest_add_func("failover-virtio-net/hotplug/1_reverse",
1857 test_hotplug_1_reverse);
1858 qtest_add_func("failover-virtio-net/hotplug/2", test_hotplug_2);
1859 qtest_add_func("failover-virtio-net/hotplug/2_reverse",
1860 test_hotplug_2_reverse);
1862 #ifndef _WIN32
1864 * These migration tests cases use the exec migration protocol,
1865 * which is unsupported on Windows.
1867 qtest_add_data_func("failover-virtio-net/migrate/on/out", tmpfile,
1868 test_migrate_out);
1869 qtest_add_data_func("failover-virtio-net/migrate/on/in", tmpfile,
1870 test_migrate_in);
1871 qtest_add_data_func("failover-virtio-net/migrate/off/out", tmpfile,
1872 test_off_migrate_out);
1873 qtest_add_data_func("failover-virtio-net/migrate/off/in", tmpfile,
1874 test_off_migrate_in);
1875 qtest_add_data_func("failover-virtio-net/migrate/off/abort", tmpfile,
1876 test_migrate_off_abort);
1877 qtest_add_data_func("failover-virtio-net/migrate/guest_off/out", tmpfile,
1878 test_guest_off_migrate_out);
1879 qtest_add_data_func("failover-virtio-net/migrate/guest_off/in", tmpfile,
1880 test_guest_off_migrate_in);
1881 qtest_add_data_func("failover-virtio-net/migrate/guest_off/abort", tmpfile,
1882 test_migrate_guest_off_abort);
1883 qtest_add_data_func("failover-virtio-net/migrate/abort/wait-unplug",
1884 tmpfile, test_migrate_abort_wait_unplug);
1885 qtest_add_data_func("failover-virtio-net/migrate/abort/active", tmpfile,
1886 test_migrate_abort_active);
1887 if (g_test_slow()) {
1888 qtest_add_data_func("failover-virtio-net/migrate/abort/timeout",
1889 tmpfile, test_migrate_abort_timeout);
1891 qtest_add_data_func("failover-virtio-net/migrate/multi/out",
1892 tmpfile, test_multi_out);
1893 qtest_add_data_func("failover-virtio-net/migrate/multi/in",
1894 tmpfile, test_multi_in);
1895 #endif /* _WIN32 */
1897 ret = g_test_run();
1899 unlink(tmpfile);
1900 g_free(tmpfile);
1902 return ret;