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