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 .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, 102 }; 103 104 if (migrate_start(&from, &to, uri, &args)) { 105 return; 106 } 107 108 migrate_ensure_non_converge(from); 109 migrate_prepare_for_dirty_mem(from); 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 QObject *channels; 209 210 if (migrate_start(&from, &to, args->listen_uri, &args->start)) { 211 return; 212 } 213 214 /* Wait for the first serial output from the source */ 215 wait_for_serial("src_serial"); 216 217 /* 218 * 'uri' and 'channels' validation is checked even before the migration 219 * starts. 220 */ 221 channels = args->connect_channels ? 222 qobject_from_json(args->connect_channels, &error_abort) : 223 NULL; 224 migrate_qmp_fail(from, args->connect_uri, channels, "{}"); 225 226 migrate_end(from, to, false); 227 } 228 229 static void test_validate_uri_channels_both_set(void) 230 { 231 MigrateCommon args = { 232 .start = { 233 .hide_stderr = true, 234 }, 235 .listen_uri = "defer", 236 .connect_uri = "tcp:127.0.0.1:0", 237 .connect_channels = ("[ { ""'channel-type': 'main'," 238 " 'addr': { 'transport': 'socket'," 239 " 'type': 'inet'," 240 " 'host': '127.0.0.1'," 241 " 'port': '0' } } ]"), 242 }; 243 244 do_test_validate_uri_channel(&args); 245 } 246 247 static void test_validate_uri_channels_none_set(void) 248 { 249 MigrateCommon args = { 250 .start = { 251 .hide_stderr = true, 252 }, 253 .listen_uri = "defer", 254 }; 255 256 do_test_validate_uri_channel(&args); 257 } 258 259 static void migration_test_add_misc_smoke(MigrationTestEnv *env) 260 { 261 #ifndef _WIN32 262 migration_test_add("/migration/analyze-script", test_analyze_script); 263 #endif 264 } 265 266 void migration_test_add_misc(MigrationTestEnv *env) 267 { 268 tmpfs = env->tmpfs; 269 270 migration_test_add_misc_smoke(env); 271 272 if (!env->full_set) { 273 return; 274 } 275 276 migration_test_add("/migration/bad_dest", test_baddest); 277 278 /* 279 * Our CI system has problems with shared memory. 280 * Don't run this test until we find a workaround. 281 */ 282 if (getenv("QEMU_TEST_FLAKY_TESTS")) { 283 migration_test_add("/migration/ignore-shared", test_ignore_shared); 284 } 285 286 migration_test_add("/migration/validate_uuid", test_validate_uuid); 287 migration_test_add("/migration/validate_uuid_error", 288 test_validate_uuid_error); 289 migration_test_add("/migration/validate_uuid_src_not_set", 290 test_validate_uuid_src_not_set); 291 migration_test_add("/migration/validate_uuid_dst_not_set", 292 test_validate_uuid_dst_not_set); 293 migration_test_add("/migration/validate_uri/channels/both_set", 294 test_validate_uri_channels_both_set); 295 migration_test_add("/migration/validate_uri/channels/none_set", 296 test_validate_uri_channels_none_set); 297 } 298