| /* |
| * QTest testcases for migration |
| * |
| * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates |
| * based on the vhost-user-test.c that is: |
| * Copyright (c) 2014 Virtual Open Systems Sarl. |
| * |
| * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| * See the COPYING file in the top-level directory. |
| * |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qapi/error.h" |
| #include "qobject/qjson.h" |
| #include "libqtest.h" |
| #include "migration/framework.h" |
| #include "migration/migration-qmp.h" |
| #include "migration/migration-util.h" |
| |
| #define ANALYZE_SCRIPT "scripts/analyze-migration.py" |
| |
| static char *tmpfs; |
| |
| static void test_baddest(void) |
| { |
| MigrateStart args = { |
| .hide_stderr = true |
| }; |
| QTestState *from, *to; |
| |
| if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { |
| return; |
| } |
| migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); |
| wait_for_migration_fail(from, false); |
| migrate_end(from, to, false); |
| } |
| |
| #ifndef _WIN32 |
| static void test_analyze_script(void) |
| { |
| MigrateStart args = { |
| .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", |
| }; |
| QTestState *from, *to; |
| g_autofree char *uri = NULL; |
| g_autofree char *file = NULL; |
| int pid, wstatus; |
| const char *python = g_getenv("PYTHON"); |
| |
| if (!python) { |
| g_test_skip("PYTHON variable not set"); |
| return; |
| } |
| |
| /* dummy url */ |
| if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { |
| return; |
| } |
| |
| /* |
| * Setting these two capabilities causes the "configuration" |
| * vmstate to include subsections for them. The script needs to |
| * parse those subsections properly. |
| */ |
| migrate_set_capability(from, "validate-uuid", true); |
| migrate_set_capability(from, "x-ignore-shared", true); |
| |
| file = g_strdup_printf("%s/migfile", tmpfs); |
| uri = g_strdup_printf("exec:cat > %s", file); |
| |
| migrate_ensure_converge(from); |
| migrate_qmp(from, to, uri, NULL, "{}"); |
| wait_for_migration_complete(from); |
| |
| pid = fork(); |
| if (!pid) { |
| close(1); |
| open("/dev/null", O_WRONLY); |
| execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); |
| g_assert_not_reached(); |
| } |
| |
| g_assert(waitpid(pid, &wstatus, 0) == pid); |
| if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { |
| g_test_message("Failed to analyze the migration stream"); |
| g_test_fail(); |
| } |
| migrate_end(from, to, false); |
| unlink(file); |
| } |
| #endif |
| |
| static void test_ignore_shared(void) |
| { |
| g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
| QTestState *from, *to; |
| MigrateStart args = { |
| .use_shmem = true, |
| }; |
| |
| if (migrate_start(&from, &to, uri, &args)) { |
| return; |
| } |
| |
| migrate_ensure_non_converge(from); |
| migrate_prepare_for_dirty_mem(from); |
| |
| migrate_set_capability(from, "x-ignore-shared", true); |
| migrate_set_capability(to, "x-ignore-shared", true); |
| |
| /* Wait for the first serial output from the source */ |
| wait_for_serial("src_serial"); |
| |
| migrate_qmp(from, to, uri, NULL, "{}"); |
| |
| migrate_wait_for_dirty_mem(from, to); |
| |
| wait_for_stop(from, get_src()); |
| |
| qtest_qmp_eventwait(to, "RESUME"); |
| |
| wait_for_serial("dest_serial"); |
| wait_for_migration_complete(from); |
| |
| /* Check whether shared RAM has been really skipped */ |
| g_assert_cmpint( |
| read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); |
| |
| migrate_end(from, to, true); |
| } |
| |
| static void do_test_validate_uuid(MigrateStart *args, bool should_fail) |
| { |
| g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); |
| QTestState *from, *to; |
| |
| if (migrate_start(&from, &to, uri, args)) { |
| return; |
| } |
| |
| /* |
| * UUID validation is at the begin of migration. So, the main process of |
| * migration is not interesting for us here. Thus, set huge downtime for |
| * very fast migration. |
| */ |
| migrate_set_parameter_int(from, "downtime-limit", 1000000); |
| migrate_set_capability(from, "validate-uuid", true); |
| |
| /* Wait for the first serial output from the source */ |
| wait_for_serial("src_serial"); |
| |
| migrate_qmp(from, to, uri, NULL, "{}"); |
| |
| if (should_fail) { |
| qtest_set_expected_status(to, EXIT_FAILURE); |
| wait_for_migration_fail(from, true); |
| } else { |
| wait_for_migration_complete(from); |
| } |
| |
| migrate_end(from, to, false); |
| } |
| |
| static void test_validate_uuid(void) |
| { |
| MigrateStart args = { |
| .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", |
| .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", |
| }; |
| |
| do_test_validate_uuid(&args, false); |
| } |
| |
| static void test_validate_uuid_error(void) |
| { |
| MigrateStart args = { |
| .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", |
| .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", |
| .hide_stderr = true, |
| }; |
| |
| do_test_validate_uuid(&args, true); |
| } |
| |
| static void test_validate_uuid_src_not_set(void) |
| { |
| MigrateStart args = { |
| .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", |
| .hide_stderr = true, |
| }; |
| |
| do_test_validate_uuid(&args, false); |
| } |
| |
| static void test_validate_uuid_dst_not_set(void) |
| { |
| MigrateStart args = { |
| .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", |
| .hide_stderr = true, |
| }; |
| |
| do_test_validate_uuid(&args, false); |
| } |
| |
| static void do_test_validate_uri_channel(MigrateCommon *args) |
| { |
| QTestState *from, *to; |
| QObject *channels; |
| |
| if (migrate_start(&from, &to, args->listen_uri, &args->start)) { |
| return; |
| } |
| |
| /* Wait for the first serial output from the source */ |
| wait_for_serial("src_serial"); |
| |
| /* |
| * 'uri' and 'channels' validation is checked even before the migration |
| * starts. |
| */ |
| channels = args->connect_channels ? |
| qobject_from_json(args->connect_channels, &error_abort) : |
| NULL; |
| migrate_qmp_fail(from, args->connect_uri, channels, "{}"); |
| |
| migrate_end(from, to, false); |
| } |
| |
| static void test_validate_uri_channels_both_set(void) |
| { |
| MigrateCommon args = { |
| .start = { |
| .hide_stderr = true, |
| }, |
| .listen_uri = "defer", |
| .connect_uri = "tcp:127.0.0.1:0", |
| .connect_channels = ("[ { ""'channel-type': 'main'," |
| " 'addr': { 'transport': 'socket'," |
| " 'type': 'inet'," |
| " 'host': '127.0.0.1'," |
| " 'port': '0' } } ]"), |
| }; |
| |
| do_test_validate_uri_channel(&args); |
| } |
| |
| static void test_validate_uri_channels_none_set(void) |
| { |
| MigrateCommon args = { |
| .start = { |
| .hide_stderr = true, |
| }, |
| .listen_uri = "defer", |
| }; |
| |
| do_test_validate_uri_channel(&args); |
| } |
| |
| void migration_test_add_misc(MigrationTestEnv *env) |
| { |
| tmpfs = env->tmpfs; |
| |
| migration_test_add("/migration/bad_dest", test_baddest); |
| #ifndef _WIN32 |
| migration_test_add("/migration/analyze-script", test_analyze_script); |
| #endif |
| |
| /* |
| * Our CI system has problems with shared memory. |
| * Don't run this test until we find a workaround. |
| */ |
| if (getenv("QEMU_TEST_FLAKY_TESTS")) { |
| migration_test_add("/migration/ignore-shared", test_ignore_shared); |
| } |
| |
| migration_test_add("/migration/validate_uuid", test_validate_uuid); |
| migration_test_add("/migration/validate_uuid_error", |
| test_validate_uuid_error); |
| migration_test_add("/migration/validate_uuid_src_not_set", |
| test_validate_uuid_src_not_set); |
| migration_test_add("/migration/validate_uuid_dst_not_set", |
| test_validate_uuid_dst_not_set); |
| migration_test_add("/migration/validate_uri/channels/both_set", |
| test_validate_uri_channels_both_set); |
| migration_test_add("/migration/validate_uri/channels/none_set", |
| test_validate_uri_channels_none_set); |
| } |