1 /* 2 * QTest testcases for migration 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 "libqtest.h" 15 #include "migration/framework.h" 16 #include "migration/migration-qmp.h" 17 #include "migration/migration-util.h" 18 19 #define ANALYZE_SCRIPT "scripts/analyze-migration.py" 20 21 static char *tmpfs; 22 23 static void test_baddest(void) 24 { 25 MigrateStart args = { 26 .hide_stderr = true 27 }; 28 QTestState *from, *to; 29 30 if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { 31 return; 32 } 33 migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); 34 wait_for_migration_fail(from, false); 35 migrate_end(from, to, false); 36 } 37 38 #ifndef _WIN32 39 static void test_analyze_script(void) 40 { 41 MigrateStart args = { 42 .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", 43 }; 44 QTestState *from, *to; 45 g_autofree char *uri = NULL; 46 g_autofree char *file = NULL; 47 int pid, wstatus; 48 const char *python = g_getenv("PYTHON"); 49 50 if (!python) { 51 g_test_skip("PYTHON variable not set"); 52 return; 53 } 54 55 /* dummy url */ 56 if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { 57 return; 58 } 59 60 /* 61 * Setting these two capabilities causes the "configuration" 62 * vmstate to include subsections for them. The script needs to 63 * parse those subsections properly. 64 */ 65 migrate_set_capability(from, "validate-uuid", true); 66 migrate_set_capability(from, "x-ignore-shared", true); 67 68 file = g_strdup_printf("%s/migfile", tmpfs); 69 uri = g_strdup_printf("exec:cat > %s", file); 70 71 migrate_ensure_converge(from); 72 migrate_qmp(from, to, uri, NULL, "{}"); 73 wait_for_migration_complete(from); 74 75 pid = fork(); 76 if (!pid) { 77 close(1); 78 open("/dev/null", O_WRONLY); 79 execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); 80 g_assert_not_reached(); 81 } 82 83 g_assert(waitpid(pid, &wstatus, 0) == pid); 84 if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { 85 g_test_message("Failed to analyze the migration stream"); 86 g_test_fail(); 87 } 88 migrate_end(from, to, false); 89 unlink(file); 90 } 91 #endif 92 93 static void test_ignore_shared(void) 94 { 95 g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); 96 QTestState *from, *to; 97 MigrateStart args = { 98 .use_shmem = true, 99 }; 100 101 if (migrate_start(&from, &to, uri, &args)) { 102 return; 103 } 104 105 migrate_ensure_non_converge(from); 106 migrate_prepare_for_dirty_mem(from); 107 108 migrate_set_capability(from, "x-ignore-shared", true); 109 migrate_set_capability(to, "x-ignore-shared", true); 110 111 /* Wait for the first serial output from the source */ 112 wait_for_serial("src_serial"); 113 114 migrate_qmp(from, to, uri, NULL, "{}"); 115 116 migrate_wait_for_dirty_mem(from, to); 117 118 wait_for_stop(from, get_src()); 119 120 qtest_qmp_eventwait(to, "RESUME"); 121 122 wait_for_serial("dest_serial"); 123 wait_for_migration_complete(from); 124 125 /* Check whether shared RAM has been really skipped */ 126 g_assert_cmpint( 127 read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); 128 129 migrate_end(from, to, true); 130 } 131 132 static void do_test_validate_uuid(MigrateStart *args, bool should_fail) 133 { 134 g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); 135 QTestState *from, *to; 136 137 if (migrate_start(&from, &to, uri, args)) { 138 return; 139 } 140 141 /* 142 * UUID validation is at the begin of migration. So, the main process of 143 * migration is not interesting for us here. Thus, set huge downtime for 144 * very fast migration. 145 */ 146 migrate_set_parameter_int(from, "downtime-limit", 1000000); 147 migrate_set_capability(from, "validate-uuid", true); 148 149 /* Wait for the first serial output from the source */ 150 wait_for_serial("src_serial"); 151 152 migrate_qmp(from, to, uri, NULL, "{}"); 153 154 if (should_fail) { 155 qtest_set_expected_status(to, EXIT_FAILURE); 156 wait_for_migration_fail(from, true); 157 } else { 158 wait_for_migration_complete(from); 159 } 160 161 migrate_end(from, to, false); 162 } 163 164 static void test_validate_uuid(void) 165 { 166 MigrateStart args = { 167 .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", 168 .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", 169 }; 170 171 do_test_validate_uuid(&args, false); 172 } 173 174 static void test_validate_uuid_error(void) 175 { 176 MigrateStart args = { 177 .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", 178 .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", 179 .hide_stderr = true, 180 }; 181 182 do_test_validate_uuid(&args, true); 183 } 184 185 static void test_validate_uuid_src_not_set(void) 186 { 187 MigrateStart args = { 188 .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", 189 .hide_stderr = true, 190 }; 191 192 do_test_validate_uuid(&args, false); 193 } 194 195 static void test_validate_uuid_dst_not_set(void) 196 { 197 MigrateStart args = { 198 .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", 199 .hide_stderr = true, 200 }; 201 202 do_test_validate_uuid(&args, false); 203 } 204 205 static void do_test_validate_uri_channel(MigrateCommon *args) 206 { 207 QTestState *from, *to; 208 209 if (migrate_start(&from, &to, args->listen_uri, &args->start)) { 210 return; 211 } 212 213 /* Wait for the first serial output from the source */ 214 wait_for_serial("src_serial"); 215 216 /* 217 * 'uri' and 'channels' validation is checked even before the migration 218 * starts. 219 */ 220 migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); 221 migrate_end(from, to, false); 222 } 223 224 static void test_validate_uri_channels_both_set(void) 225 { 226 MigrateCommon args = { 227 .start = { 228 .hide_stderr = true, 229 }, 230 .listen_uri = "defer", 231 .connect_uri = "tcp:127.0.0.1:0", 232 .connect_channels = ("[ { ""'channel-type': 'main'," 233 " 'addr': { 'transport': 'socket'," 234 " 'type': 'inet'," 235 " 'host': '127.0.0.1'," 236 " 'port': '0' } } ]"), 237 }; 238 239 do_test_validate_uri_channel(&args); 240 } 241 242 static void test_validate_uri_channels_none_set(void) 243 { 244 MigrateCommon args = { 245 .start = { 246 .hide_stderr = true, 247 }, 248 .listen_uri = "defer", 249 }; 250 251 do_test_validate_uri_channel(&args); 252 } 253 254 void migration_test_add_misc(MigrationTestEnv *env) 255 { 256 tmpfs = env->tmpfs; 257 258 migration_test_add("/migration/bad_dest", test_baddest); 259 #ifndef _WIN32 260 migration_test_add("/migration/analyze-script", test_analyze_script); 261 #endif 262 263 /* 264 * Our CI system has problems with shared memory. 265 * Don't run this test until we find a workaround. 266 */ 267 if (getenv("QEMU_TEST_FLAKY_TESTS")) { 268 migration_test_add("/migration/ignore-shared", test_ignore_shared); 269 } 270 271 migration_test_add("/migration/validate_uuid", test_validate_uuid); 272 migration_test_add("/migration/validate_uuid_error", 273 test_validate_uuid_error); 274 migration_test_add("/migration/validate_uuid_src_not_set", 275 test_validate_uuid_src_not_set); 276 migration_test_add("/migration/validate_uuid_dst_not_set", 277 test_validate_uuid_dst_not_set); 278 migration_test_add("/migration/validate_uri/channels/both_set", 279 test_validate_uri_channels_both_set); 280 migration_test_add("/migration/validate_uri/channels/none_set", 281 test_validate_uri_channels_none_set); 282 } 283