| /* | |
| * 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(char *name, MigrateCommon *args) | |
| { | |
| QTestState *from, *to; | |
| args->start.hide_stderr = true; | |
| if (migrate_start(&from, &to, &args->start)) { | |
| return; | |
| } | |
| migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); | |
| 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(char *name, MigrateCommon *args) | |
| { | |
| QTestState *from, *to; | |
| g_autofree char *uri = NULL; | |
| g_autofree char *file = NULL; | |
| int pid, wstatus; | |
| const char *python = g_getenv("PYTHON"); | |
| args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; | |
| if (!python) { | |
| g_test_skip("PYTHON variable not set"); | |
| return; | |
| } | |
| if (migrate_start(&from, &to, &args->start)) { | |
| 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_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); | |
| 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 ignore_shared_assert_skipped(QTestState *from, QTestState *to, | |
| void *data) | |
| { | |
| /* Check whether shared RAM has been really skipped */ | |
| g_assert_cmpint( | |
| read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); | |
| } | |
| static void test_ignore_shared(char *name, MigrateCommon *args) | |
| { | |
| args->live = true; | |
| args->start.mem_type = MEM_TYPE_SHMEM; | |
| args->start.caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true; | |
| args->end_hook = ignore_shared_assert_skipped; | |
| test_precopy_unix_common(args); | |
| } | |
| 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, 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_incoming_qmp(to, uri, NULL, "{}"); | |
| migrate_qmp(from, to, uri, NULL, "{}"); | |
| if (should_fail) { | |
| wait_for_migration_fail(from, true); | |
| } else { | |
| wait_for_migration_complete(from); | |
| } | |
| migrate_end(from, to, false); | |
| } | |
| static void test_validate_uuid(char *name, MigrateCommon *args) | |
| { | |
| args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; | |
| args->start.opts_target = "-uuid 11111111-1111-1111-1111-111111111111"; | |
| do_test_validate_uuid(&args->start, false); | |
| } | |
| static void test_validate_uuid_error(char *name, MigrateCommon *args) | |
| { | |
| args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; | |
| args->start.opts_target = "-uuid 22222222-2222-2222-2222-222222222222"; | |
| args->start.hide_stderr = true; | |
| do_test_validate_uuid(&args->start, true); | |
| } | |
| static void test_validate_uuid_src_not_set(char *name, MigrateCommon *args) | |
| { | |
| args->start.opts_target = "-uuid 22222222-2222-2222-2222-222222222222"; | |
| args->start.hide_stderr = true; | |
| do_test_validate_uuid(&args->start, false); | |
| } | |
| static void test_validate_uuid_dst_not_set(char *name, MigrateCommon *args) | |
| { | |
| args->start.opts_source = "-uuid 11111111-1111-1111-1111-111111111111"; | |
| args->start.hide_stderr = true; | |
| do_test_validate_uuid(&args->start, false); | |
| } | |
| static void do_test_validate_uri_channel(MigrateCommon *args) | |
| { | |
| QTestState *from, *to; | |
| QObject *channels; | |
| if (migrate_start(&from, &to, &args->start)) { | |
| return; | |
| } | |
| /* Wait for the first serial output from the source */ | |
| wait_for_serial("src_serial"); | |
| migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); | |
| /* | |
| * '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->uri, channels, "{}"); | |
| migrate_end(from, to, false); | |
| } | |
| static void test_validate_uri_channels_both_set(char *name, MigrateCommon *args) | |
| { | |
| args->uri = "tcp:127.0.0.1:0", | |
| args->connect_channels = ("[ { ""'channel-type': 'main'," | |
| " 'addr': { 'transport': 'socket'," | |
| " 'type': 'inet'," | |
| " 'host': '127.0.0.1'," | |
| " 'port': '0' } } ]"), | |
| args->start.hide_stderr = true; | |
| do_test_validate_uri_channel(args); | |
| } | |
| static void test_validate_uri_channels_none_set(char *name, MigrateCommon *args) | |
| { | |
| args->start.hide_stderr = true; | |
| do_test_validate_uri_channel(args); | |
| } | |
| static void migration_test_add_misc_smoke(MigrationTestEnv *env) | |
| { | |
| #ifndef _WIN32 | |
| migration_test_add("/migration/analyze-script", test_analyze_script); | |
| #endif | |
| } | |
| void migration_test_add_misc(MigrationTestEnv *env) | |
| { | |
| tmpfs = env->tmpfs; | |
| migration_test_add_misc_smoke(env); | |
| if (!env->full_set) { | |
| return; | |
| } | |
| migration_test_add("/migration/bad_dest", test_baddest); | |
| /* | |
| * 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); | |
| } |