migration-test: Add COLO migration unit test
Add a COLO migration test for COLO migration and failover. Reviewed-by: Fabiano Rosas <farosas@suse.de> Tested-by: Fabiano Rosas <farosas@suse.de> Reviewed-by: Peter Xu <peterx@redhat.com> Signed-off-by: Lukas Straub <lukasstraub2@web.de> Link: https://lore.kernel.org/qemu-devel/20260302-colo_unit_test_multifd-v11-13-d653fb3b1d80@web.de [remove license boilerplate] Signed-off-by: Fabiano Rosas <farosas@suse.de>
Lukas Straub committed
Mar 2, 2026 at 12:45 UTC
c972c3bdb20693d3ad08ab1108530ae15dc3d7df
5 files changed
+207
-1
MAINTAINERS
+1
@@ -3886,6 +3886,7 @@ F: migration/colo*
3886
F: migration/multifd-colo.*
3887
F: include/migration/colo.h
3888
F: include/migration/failover.h
3889
+F: tests/qtest/migration/colo-tests.c
3890
F: docs/COLO-FT.txt
3891
3892
COLO Proxy
tests/qtest/meson.build
+6
-1
@@ -372,6 +372,11 @@ if gnutls.found()
372
endif
373
endif
374
375
+migration_colo_files = []
376
+if get_option('replication').allowed()
377
+ migration_colo_files = [files('migration/colo-tests.c')]
378
+endif
379
+
380
qtests = {
381
'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'),
382
'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'),
@@ -383,7 +388,7 @@ qtests = {
388
'migration/migration-util.c') + dbus_vmstate1,
389
'erst-test': files('erst-test.c'),
390
'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'],
386
- 'migration-test': test_migration_files + migration_tls_files,
391
+ 'migration-test': test_migration_files + migration_tls_files + migration_colo_files,
392
'pxe-test': files('boot-sector.c'),
393
'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c',
394
'pnv-xive2-nvpg_bar.c'),
tests/qtest/migration-test.c
+1
@@ -55,6 +55,7 @@ int main(int argc, char **argv)
55
migration_test_add_precopy(env);
56
migration_test_add_cpr(env);
57
migration_test_add_misc(env);
58
+ migration_test_add_colo(env);
59
60
ret = g_test_run();
61
tests/qtest/migration/colo-tests.c
new
+194
@@ -0,0 +1,194 @@
1
+/*
2
+ * SPDX-License-Identifier: GPL-2.0-or-later
3
+ *
4
+ * QTest testcases for COLO migration
5
+ *
6
+ * Copyright (c) 2025 Lukas Straub <lukasstraub2@web.de>
7
+ */
8
+
9
+#include "qemu/osdep.h"
10
+#include "libqtest.h"
11
+#include "migration/framework.h"
12
+#include "migration/migration-qmp.h"
13
+#include "migration/migration-util.h"
14
+#include "qemu/module.h"
15
+
16
+static int test_colo_common(MigrateCommon *args,
17
+ bool failover_during_checkpoint,
18
+ bool primary_failover)
19
+{
20
+ QTestState *from, *to;
21
+ void *data_hook = NULL;
22
+
23
+ /*
24
+ * For the COLO test, both VMs will run in parallel. Thus both VMs want to
25
+ * open the image read/write at the same time. Using read-only=on is not
26
+ * possible here, because ide-hd does not support read-only backing image.
27
+ *
28
+ * So use -snapshot, where each qemu instance creates its own writable
29
+ * snapshot internally while leaving the real image read-only.
30
+ */
31
+ args->start.opts_source = "-snapshot";
32
+ args->start.opts_target = "-snapshot";
33
+
34
+ /*
35
+ * COLO migration code logs many errors when the migration socket
36
+ * is shut down, these are expected so we hide them here.
37
+ */
38
+ args->start.hide_stderr = true;
39
+
40
+ /*
41
+ * Test with yank with out of band capability since that is how it is
42
+ * used in production.
43
+ */
44
+ args->start.oob = true;
45
+ args->start.caps[MIGRATION_CAPABILITY_X_COLO] = true;
46
+
47
+ if (migrate_start(&from, &to, args->listen_uri, &args->start)) {
48
+ return -1;
49
+ }
50
+
51
+ migrate_set_parameter_int(from, "x-checkpoint-delay", 300);
52
+
53
+ if (args->start_hook) {
54
+ data_hook = args->start_hook(from, to);
55
+ }
56
+
57
+ migrate_ensure_converge(from);
58
+ wait_for_serial("src_serial");
59
+
60
+ migrate_qmp(from, to, args->connect_uri, NULL, "{}");
61
+
62
+ wait_for_migration_status(from, "colo", NULL);
63
+ wait_for_resume(to, get_dst());
64
+
65
+ wait_for_serial("src_serial");
66
+ wait_for_serial("dest_serial");
67
+
68
+ /* wait for 3 checkpoints */
69
+ for (int i = 0; i < 3; i++) {
70
+ qtest_qmp_eventwait(to, "RESUME");
71
+ wait_for_serial("src_serial");
72
+ wait_for_serial("dest_serial");
73
+ }
74
+
75
+ if (failover_during_checkpoint) {
76
+ qtest_qmp_eventwait(to, "STOP");
77
+ }
78
+ if (primary_failover) {
79
+ qtest_qmp_assert_success(from, "{'exec-oob': 'yank', 'id': 'yank-cmd', "
80
+ "'arguments': {'instances':"
81
+ "[{'type': 'migration'}]}}");
82
+ qtest_qmp_assert_success(from, "{'execute': 'x-colo-lost-heartbeat'}");
83
+ wait_for_serial("src_serial");
84
+ } else {
85
+ qtest_qmp_assert_success(to, "{'exec-oob': 'yank', 'id': 'yank-cmd', "
86
+ "'arguments': {'instances':"
87
+ "[{'type': 'migration'}]}}");
88
+ qtest_qmp_assert_success(to, "{'execute': 'x-colo-lost-heartbeat'}");
89
+ wait_for_serial("dest_serial");
90
+ }
91
+
92
+ if (args->end_hook) {
93
+ args->end_hook(from, to, data_hook);
94
+ }
95
+
96
+ migrate_end(from, to, !primary_failover);
97
+
98
+ return 0;
99
+}
100
+
101
+static void test_colo_plain_common(MigrateCommon *args,
102
+ bool failover_during_checkpoint,
103
+ bool primary_failover)
104
+{
105
+ args->listen_uri = "tcp:127.0.0.1:0";
106
+ test_colo_common(args, failover_during_checkpoint, primary_failover);
107
+}
108
+
109
+static void *hook_start_multifd(QTestState *from, QTestState *to)
110
+{
111
+ return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none");
112
+}
113
+
114
+static void test_colo_multifd_common(MigrateCommon *args,
115
+ bool failover_during_checkpoint,
116
+ bool primary_failover)
117
+{
118
+ args->listen_uri = "defer";
119
+ args->start_hook = hook_start_multifd;
120
+ args->start.caps[MIGRATION_CAPABILITY_MULTIFD] = true;
121
+ test_colo_common(args, failover_during_checkpoint, primary_failover);
122
+}
123
+
124
+static void test_colo_plain_primary_failover(char *name, MigrateCommon *args)
125
+{
126
+ test_colo_plain_common(args, false, true);
127
+}
128
+
129
+static void test_colo_plain_secondary_failover(char *name, MigrateCommon *args)
130
+{
131
+ test_colo_plain_common(args, false, false);
132
+}
133
+
134
+static void test_colo_multifd_primary_failover(char *name, MigrateCommon *args)
135
+{
136
+ test_colo_multifd_common(args, false, true);
137
+}
138
+
139
+static void test_colo_multifd_secondary_failover(char *name,
140
+ MigrateCommon *args)
141
+{
142
+ test_colo_multifd_common(args, false, false);
143
+}
144
+
145
+static void test_colo_plain_primary_failover_checkpoint(char *name,
146
+ MigrateCommon *args)
147
+{
148
+ test_colo_plain_common(args, true, true);
149
+}
150
+
151
+static void test_colo_plain_secondary_failover_checkpoint(char *name,
152
+ MigrateCommon *args)
153
+{
154
+ test_colo_plain_common(args, true, false);
155
+}
156
+
157
+static void test_colo_multifd_primary_failover_checkpoint(char *name,
158
+ MigrateCommon *args)
159
+{
160
+ test_colo_multifd_common(args, true, true);
161
+}
162
+
163
+static void test_colo_multifd_secondary_failover_checkpoint(char *name,
164
+ MigrateCommon *args)
165
+{
166
+ test_colo_multifd_common(args, true, false);
167
+}
168
+
169
+void migration_test_add_colo(MigrationTestEnv *env)
170
+{
171
+ if (!env->full_set) {
172
+ return;
173
+ }
174
+
175
+ migration_test_add("/migration/colo/plain/primary_failover",
176
+ test_colo_plain_primary_failover);
177
+ migration_test_add("/migration/colo/plain/secondary_failover",
178
+ test_colo_plain_secondary_failover);
179
+
180
+ migration_test_add("/migration/colo/multifd/primary_failover",
181
+ test_colo_multifd_primary_failover);
182
+ migration_test_add("/migration/colo/multifd/secondary_failover",
183
+ test_colo_multifd_secondary_failover);
184
+
185
+ migration_test_add("/migration/colo/plain/primary_failover_checkpoint",
186
+ test_colo_plain_primary_failover_checkpoint);
187
+ migration_test_add("/migration/colo/plain/secondary_failover_checkpoint",
188
+ test_colo_plain_secondary_failover_checkpoint);
189
+
190
+ migration_test_add("/migration/colo/multifd/primary_failover_checkpoint",
191
+ test_colo_multifd_primary_failover_checkpoint);
192
+ migration_test_add("/migration/colo/multifd/secondary_failover_checkpoint",
193
+ test_colo_multifd_secondary_failover_checkpoint);
194
+}
tests/qtest/migration/framework.h
+5
@@ -264,5 +264,10 @@ void migration_test_add_file(MigrationTestEnv *env);
264
void migration_test_add_precopy(MigrationTestEnv *env);
265
void migration_test_add_cpr(MigrationTestEnv *env);
266
void migration_test_add_misc(MigrationTestEnv *env);
267
+#ifdef CONFIG_REPLICATION
268
+void migration_test_add_colo(MigrationTestEnv *env);
269
+#else
270
+static inline void migration_test_add_colo(MigrationTestEnv *env) {};
271
+#endif
272
273
#endif /* TEST_FRAMEWORK_H */