@samitouri / QOSamiQemu / commits / c972c3bdb2

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 */