From 1e2077e22332c4d2867043bbea2337d331106bfd Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 8 Dec 2021 14:03:49 +0100 Subject: [PATCH] tests/libqtest: add some virtio-net failover migration cancelling tests Add some tests to check the state of the machine if the migration is cancelled while we are using virtio-net failover. Signed-off-by: Laurent Vivier Acked-by: Thomas Huth Message-Id: <20211208130350.10178-4-lvivier@redhat.com> Signed-off-by: Thomas Huth --- tests/qtest/virtio-net-failover.c | 282 ++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index fd7821deaf..e998a546b0 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -752,6 +752,280 @@ static void test_migrate_in(gconstpointer opaque) machine_stop(qts); } +static void test_migrate_abort_wait_unplug(gconstpointer opaque) +{ + QTestState *qts; + QDict *resp, *args, *ret; + g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque); + const gchar *status; + QVirtioPCIDevice *vdev; + + qts = machine_start(BASE_MACHINE + "-netdev user,id=hs0 " + "-netdev user,id=hs1 ", + 2); + + check_one_card(qts, false, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + qtest_qmp_device_add(qts, "virtio-net", "standby0", + "{'bus': 'root0'," + "'failover': 'on'," + "'netdev': 'hs0'," + "'mac': '"MAC_STANDBY0"'}"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + vdev = start_virtio_net(qts, 1, 0, "standby0"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + qtest_qmp_device_add(qts, "virtio-net", "primary0", + "{'bus': 'root1'," + "'failover_pair_id': 'standby0'," + "'netdev': 'hs1'," + "'rombar': 0," + "'romfile': ''," + "'mac': '"MAC_PRIMARY0"'}"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, true, "primary0", MAC_PRIMARY0); + + args = qdict_from_jsonf_nofail("{}"); + g_assert_nonnull(args); + qdict_put_str(args, "uri", uri); + + resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* the event is sent when QEMU asks the OS to unplug the card */ + resp = get_unplug_primary_event(qts); + g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0"); + qobject_unref(resp); + + resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* migration has been cancelled while the unplug was in progress */ + + /* while the card is not ejected, we must be in "cancelling" state */ + ret = migrate_status(qts); + + status = qdict_get_str(ret, "status"); + g_assert_cmpstr(status, ==, "cancelling"); + qobject_unref(ret); + + /* OS unplugs the cards, QEMU can move from wait-unplug state */ + qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1); + + while (true) { + ret = migrate_status(qts); + + status = qdict_get_str(ret, "status"); + if (strcmp(status, "cancelled") == 0) { + qobject_unref(ret); + break; + } + g_assert_cmpstr(status, !=, "failed"); + g_assert_cmpstr(status, !=, "active"); + qobject_unref(ret); + } + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, true, "primary0", MAC_PRIMARY0); + + qos_object_destroy((QOSGraphObject *)vdev); + machine_stop(qts); +} + +static void test_migrate_abort_active(gconstpointer opaque) +{ + QTestState *qts; + QDict *resp, *args, *ret; + g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque); + const gchar *status; + QVirtioPCIDevice *vdev; + + qts = machine_start(BASE_MACHINE + "-netdev user,id=hs0 " + "-netdev user,id=hs1 ", + 2); + + check_one_card(qts, false, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + qtest_qmp_device_add(qts, "virtio-net", "standby0", + "{'bus': 'root0'," + "'failover': 'on'," + "'netdev': 'hs0'," + "'mac': '"MAC_STANDBY0"'}"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + vdev = start_virtio_net(qts, 1, 0, "standby0"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + qtest_qmp_device_add(qts, "virtio-net", "primary0", + "{'bus': 'root1'," + "'failover_pair_id': 'standby0'," + "'netdev': 'hs1'," + "'rombar': 0," + "'romfile': ''," + "'mac': '"MAC_PRIMARY0"'}"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, true, "primary0", MAC_PRIMARY0); + + args = qdict_from_jsonf_nofail("{}"); + g_assert_nonnull(args); + qdict_put_str(args, "uri", uri); + + resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* the event is sent when QEMU asks the OS to unplug the card */ + resp = get_unplug_primary_event(qts); + g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0"); + qobject_unref(resp); + + /* OS unplugs the cards, QEMU can move from wait-unplug state */ + qtest_outl(qts, ACPI_PCIHP_ADDR_ICH9 + PCI_EJ_BASE, 1); + + while (true) { + ret = migrate_status(qts); + + status = qdict_get_str(ret, "status"); + if (strcmp(status, "wait-unplug") != 0) { + qobject_unref(ret); + break; + } + g_assert_cmpstr(status, !=, "failed"); + qobject_unref(ret); + } + + resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + while (true) { + ret = migrate_status(qts); + + status = qdict_get_str(ret, "status"); + if (strcmp(status, "cancelled") == 0) { + qobject_unref(ret); + break; + } + g_assert_cmpstr(status, !=, "failed"); + g_assert_cmpstr(status, !=, "active"); + qobject_unref(ret); + } + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, true, "primary0", MAC_PRIMARY0); + + qos_object_destroy((QOSGraphObject *)vdev); + machine_stop(qts); +} + +static void test_migrate_abort_timeout(gconstpointer opaque) +{ + QTestState *qts; + QDict *resp, *args, *ret; + g_autofree gchar *uri = g_strdup_printf("exec: cat > %s", (gchar *)opaque); + const gchar *status; + int total; + QVirtioPCIDevice *vdev; + + qts = machine_start(BASE_MACHINE + "-netdev user,id=hs0 " + "-netdev user,id=hs1 ", + 2); + + check_one_card(qts, false, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + qtest_qmp_device_add(qts, "virtio-net", "standby0", + "{'bus': 'root0'," + "'failover': 'on'," + "'netdev': 'hs0'," + "'mac': '"MAC_STANDBY0"'}"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + vdev = start_virtio_net(qts, 1, 0, "standby0"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, false, "primary0", MAC_PRIMARY0); + + qtest_qmp_device_add(qts, "virtio-net", "primary0", + "{'bus': 'root1'," + "'failover_pair_id': 'standby0'," + "'netdev': 'hs1'," + "'rombar': 0," + "'romfile': ''," + "'mac': '"MAC_PRIMARY0"'}"); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, true, "primary0", MAC_PRIMARY0); + + args = qdict_from_jsonf_nofail("{}"); + g_assert_nonnull(args); + qdict_put_str(args, "uri", uri); + + resp = qtest_qmp(qts, "{ 'execute': 'migrate', 'arguments': %p}", args); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* the event is sent when QEMU asks the OS to unplug the card */ + resp = get_unplug_primary_event(qts); + g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "primary0"); + qobject_unref(resp); + + resp = qtest_qmp(qts, "{ 'execute': 'migrate_cancel' }"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + + /* migration has been cancelled while the unplug was in progress */ + + /* while the card is not ejected, we must be in "cancelling" state */ + + total = 0; + while (true) { + ret = migrate_status(qts); + + status = qdict_get_str(ret, "status"); + if (strcmp(status, "cancelled") == 0) { + qobject_unref(ret); + break; + } + g_assert_cmpstr(status, ==, "cancelling"); + g_assert(qdict_haskey(ret, "total-time")); + total = qdict_get_int(ret, "total-time"); + qobject_unref(ret); + } + + /* + * migration timeout in this case is 30 seconds + * check we exit on the timeout (ms) + */ + g_assert_cmpint(total, >, 30000); + + check_one_card(qts, true, "standby0", MAC_STANDBY0); + check_one_card(qts, true, "primary0", MAC_PRIMARY0); + + qos_object_destroy((QOSGraphObject *)vdev); + machine_stop(qts); +} + int main(int argc, char **argv) { const gchar *tmpdir = g_get_tmp_dir(); @@ -778,6 +1052,14 @@ int main(int argc, char **argv) test_migrate_out); qtest_add_data_func("failover-virtio-net/migrate/in", tmpfile, test_migrate_in); + qtest_add_data_func("failover-virtio-net/migrate/abort/wait-unplug", + tmpfile, test_migrate_abort_wait_unplug); + qtest_add_data_func("failover-virtio-net/migrate/abort/active", tmpfile, + test_migrate_abort_active); + if (g_test_slow()) { + qtest_add_data_func("failover-virtio-net/migrate/abort/timeout", + tmpfile, test_migrate_abort_timeout); + } ret = g_test_run();