]>
Commit | Line | Data |
---|---|---|
586ca6ba MAL |
1 | #include "qemu/osdep.h" |
2 | #include <glib/gstdio.h> | |
3 | #include <gio/gio.h> | |
a2ce7dbd | 4 | #include "libqos/libqtest.h" |
586ca6ba MAL |
5 | #include "qemu-common.h" |
6 | #include "dbus-vmstate1.h" | |
7 | #include "migration-helpers.h" | |
8 | ||
9 | static char *workdir; | |
10 | ||
11 | typedef struct TestServerId { | |
12 | const char *name; | |
13 | const char *data; | |
14 | size_t size; | |
15 | } TestServerId; | |
16 | ||
17 | static const TestServerId idA = { | |
18 | "idA", "I'am\0idA!", sizeof("I'am\0idA!") | |
19 | }; | |
20 | ||
21 | static const TestServerId idB = { | |
22 | "idB", "I'am\0idB!", sizeof("I'am\0idB!") | |
23 | }; | |
24 | ||
25 | typedef struct TestServer { | |
26 | const TestServerId *id; | |
27 | bool save_called; | |
28 | bool load_called; | |
29 | } TestServer; | |
30 | ||
31 | typedef struct Test { | |
32 | const char *id_list; | |
33 | bool migrate_fail; | |
34 | bool without_dst_b; | |
35 | TestServer srcA; | |
36 | TestServer dstA; | |
37 | TestServer srcB; | |
38 | TestServer dstB; | |
39 | GMainLoop *loop; | |
40 | QTestState *src_qemu; | |
41 | } Test; | |
42 | ||
43 | static gboolean | |
44 | vmstate_load(VMState1 *object, GDBusMethodInvocation *invocation, | |
45 | const gchar *arg_data, gpointer user_data) | |
46 | { | |
47 | TestServer *h = user_data; | |
48 | g_autoptr(GVariant) var = NULL; | |
49 | GVariant *args; | |
50 | const uint8_t *data; | |
51 | size_t size; | |
52 | ||
53 | args = g_dbus_method_invocation_get_parameters(invocation); | |
54 | var = g_variant_get_child_value(args, 0); | |
55 | data = g_variant_get_fixed_array(var, &size, sizeof(char)); | |
56 | g_assert_cmpuint(size, ==, h->id->size); | |
57 | g_assert(!memcmp(data, h->id->data, h->id->size)); | |
58 | h->load_called = true; | |
59 | ||
60 | g_dbus_method_invocation_return_value(invocation, g_variant_new("()")); | |
61 | return TRUE; | |
62 | } | |
63 | ||
64 | static gboolean | |
65 | vmstate_save(VMState1 *object, GDBusMethodInvocation *invocation, | |
66 | gpointer user_data) | |
67 | { | |
68 | TestServer *h = user_data; | |
69 | GVariant *var; | |
70 | ||
71 | var = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, | |
72 | h->id->data, h->id->size, sizeof(char)); | |
73 | g_dbus_method_invocation_return_value(invocation, | |
74 | g_variant_new("(@ay)", var)); | |
75 | h->save_called = true; | |
76 | ||
77 | return TRUE; | |
78 | } | |
79 | ||
80 | typedef struct WaitNamed { | |
81 | GMainLoop *loop; | |
82 | bool named; | |
83 | } WaitNamed; | |
84 | ||
85 | static void | |
86 | named_cb(GDBusConnection *connection, | |
87 | const gchar *name, | |
88 | gpointer user_data) | |
89 | { | |
90 | WaitNamed *t = user_data; | |
91 | ||
92 | t->named = true; | |
93 | g_main_loop_quit(t->loop); | |
94 | } | |
95 | ||
96 | static GDBusConnection * | |
97 | get_connection(Test *test, guint *ownid) | |
98 | { | |
99 | g_autofree gchar *addr = NULL; | |
100 | WaitNamed *wait; | |
101 | GError *err = NULL; | |
102 | GDBusConnection *c; | |
103 | ||
104 | wait = g_new0(WaitNamed, 1); | |
105 | wait->loop = test->loop; | |
106 | addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &err); | |
107 | g_assert_no_error(err); | |
108 | ||
109 | c = g_dbus_connection_new_for_address_sync( | |
110 | addr, | |
111 | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION | | |
112 | G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT, | |
113 | NULL, NULL, &err); | |
114 | g_assert_no_error(err); | |
115 | *ownid = g_bus_own_name_on_connection(c, "org.qemu.VMState1", | |
116 | G_BUS_NAME_OWNER_FLAGS_NONE, | |
117 | named_cb, named_cb, wait, g_free); | |
118 | if (!wait->named) { | |
119 | g_main_loop_run(wait->loop); | |
120 | } | |
121 | ||
122 | return c; | |
123 | } | |
124 | ||
125 | static GDBusObjectManagerServer * | |
126 | get_server(GDBusConnection *conn, TestServer *s, const TestServerId *id) | |
127 | { | |
128 | g_autoptr(GDBusObjectSkeleton) sk = NULL; | |
129 | g_autoptr(VMState1Skeleton) v = NULL; | |
130 | GDBusObjectManagerServer *os; | |
131 | ||
132 | s->id = id; | |
133 | os = g_dbus_object_manager_server_new("/org/qemu"); | |
134 | sk = g_dbus_object_skeleton_new("/org/qemu/VMState1"); | |
135 | ||
136 | v = VMSTATE1_SKELETON(vmstate1_skeleton_new()); | |
137 | g_object_set(v, "id", id->name, NULL); | |
138 | ||
139 | g_signal_connect(v, "handle-load", G_CALLBACK(vmstate_load), s); | |
140 | g_signal_connect(v, "handle-save", G_CALLBACK(vmstate_save), s); | |
141 | ||
142 | g_dbus_object_skeleton_add_interface(sk, G_DBUS_INTERFACE_SKELETON(v)); | |
143 | g_dbus_object_manager_server_export(os, sk); | |
144 | g_dbus_object_manager_server_set_connection(os, conn); | |
145 | ||
146 | return os; | |
147 | } | |
148 | ||
149 | static void | |
150 | set_id_list(Test *test, QTestState *s) | |
151 | { | |
152 | if (!test->id_list) { | |
153 | return; | |
154 | } | |
155 | ||
156 | g_assert(!qmp_rsp_is_err(qtest_qmp(s, | |
157 | "{ 'execute': 'qom-set', 'arguments': " | |
158 | "{ 'path': '/objects/dv', 'property': 'id-list', 'value': %s } }", | |
159 | test->id_list))); | |
160 | } | |
161 | ||
162 | static gpointer | |
163 | dbus_vmstate_thread(gpointer data) | |
164 | { | |
165 | GMainLoop *loop = data; | |
166 | ||
167 | g_main_loop_run(loop); | |
168 | ||
169 | return NULL; | |
170 | } | |
171 | ||
172 | static void | |
173 | test_dbus_vmstate(Test *test) | |
174 | { | |
175 | g_autofree char *src_qemu_args = NULL; | |
176 | g_autofree char *dst_qemu_args = NULL; | |
177 | g_autoptr(GTestDBus) srcbus = NULL; | |
178 | g_autoptr(GTestDBus) dstbus = NULL; | |
179 | g_autoptr(GDBusConnection) srcconnA = NULL; | |
180 | g_autoptr(GDBusConnection) srcconnB = NULL; | |
181 | g_autoptr(GDBusConnection) dstconnA = NULL; | |
182 | g_autoptr(GDBusConnection) dstconnB = NULL; | |
183 | g_autoptr(GDBusObjectManagerServer) srcserverA = NULL; | |
184 | g_autoptr(GDBusObjectManagerServer) srcserverB = NULL; | |
185 | g_autoptr(GDBusObjectManagerServer) dstserverA = NULL; | |
186 | g_autoptr(GDBusObjectManagerServer) dstserverB = NULL; | |
187 | g_auto(GStrv) srcaddr = NULL; | |
188 | g_auto(GStrv) dstaddr = NULL; | |
189 | g_autoptr(GThread) thread = NULL; | |
190 | g_autoptr(GMainLoop) loop = NULL; | |
191 | g_autofree char *uri = NULL; | |
192 | QTestState *src_qemu = NULL, *dst_qemu = NULL; | |
193 | guint ownsrcA, ownsrcB, owndstA, owndstB; | |
194 | ||
195 | uri = g_strdup_printf("unix:%s/migsocket", workdir); | |
196 | ||
197 | loop = g_main_loop_new(NULL, FALSE); | |
198 | test->loop = loop; | |
199 | ||
200 | srcbus = g_test_dbus_new(G_TEST_DBUS_NONE); | |
201 | g_test_dbus_up(srcbus); | |
202 | srcconnA = get_connection(test, &ownsrcA); | |
203 | srcserverA = get_server(srcconnA, &test->srcA, &idA); | |
204 | srcconnB = get_connection(test, &ownsrcB); | |
205 | srcserverB = get_server(srcconnB, &test->srcB, &idB); | |
206 | ||
207 | /* remove ,guid=foo part */ | |
208 | srcaddr = g_strsplit(g_test_dbus_get_bus_address(srcbus), ",", 2); | |
209 | src_qemu_args = | |
210 | g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s", srcaddr[0]); | |
211 | ||
212 | dstbus = g_test_dbus_new(G_TEST_DBUS_NONE); | |
213 | g_test_dbus_up(dstbus); | |
214 | dstconnA = get_connection(test, &owndstA); | |
215 | dstserverA = get_server(dstconnA, &test->dstA, &idA); | |
216 | if (!test->without_dst_b) { | |
217 | dstconnB = get_connection(test, &owndstB); | |
218 | dstserverB = get_server(dstconnB, &test->dstB, &idB); | |
219 | } | |
220 | ||
221 | dstaddr = g_strsplit(g_test_dbus_get_bus_address(dstbus), ",", 2); | |
222 | dst_qemu_args = | |
223 | g_strdup_printf("-object dbus-vmstate,id=dv,addr=%s -incoming %s", | |
224 | dstaddr[0], uri); | |
225 | ||
226 | src_qemu = qtest_init(src_qemu_args); | |
227 | dst_qemu = qtest_init(dst_qemu_args); | |
228 | set_id_list(test, src_qemu); | |
229 | set_id_list(test, dst_qemu); | |
230 | ||
231 | thread = g_thread_new("dbus-vmstate-thread", dbus_vmstate_thread, loop); | |
232 | ||
233 | migrate_qmp(src_qemu, uri, "{}"); | |
234 | test->src_qemu = src_qemu; | |
235 | if (test->migrate_fail) { | |
236 | wait_for_migration_fail(src_qemu, true); | |
237 | qtest_set_expected_status(dst_qemu, 1); | |
238 | } else { | |
239 | wait_for_migration_complete(src_qemu); | |
240 | } | |
241 | ||
242 | qtest_quit(dst_qemu); | |
243 | qtest_quit(src_qemu); | |
244 | g_bus_unown_name(ownsrcA); | |
245 | g_bus_unown_name(ownsrcB); | |
246 | g_bus_unown_name(owndstA); | |
247 | if (!test->without_dst_b) { | |
248 | g_bus_unown_name(owndstB); | |
249 | } | |
250 | ||
251 | g_main_loop_quit(test->loop); | |
252 | } | |
253 | ||
254 | static void | |
255 | check_not_migrated(TestServer *s, TestServer *d) | |
256 | { | |
257 | assert(!s->save_called); | |
258 | assert(!s->load_called); | |
259 | assert(!d->save_called); | |
260 | assert(!d->load_called); | |
261 | } | |
262 | ||
263 | static void | |
264 | check_migrated(TestServer *s, TestServer *d) | |
265 | { | |
266 | assert(s->save_called); | |
267 | assert(!s->load_called); | |
268 | assert(!d->save_called); | |
269 | assert(d->load_called); | |
270 | } | |
271 | ||
272 | static void | |
273 | test_dbus_vmstate_without_list(void) | |
274 | { | |
275 | Test test = { 0, }; | |
276 | ||
277 | test_dbus_vmstate(&test); | |
278 | ||
279 | check_migrated(&test.srcA, &test.dstA); | |
280 | check_migrated(&test.srcB, &test.dstB); | |
281 | } | |
282 | ||
283 | static void | |
284 | test_dbus_vmstate_with_list(void) | |
285 | { | |
286 | Test test = { .id_list = "idA,idB" }; | |
287 | ||
288 | test_dbus_vmstate(&test); | |
289 | ||
290 | check_migrated(&test.srcA, &test.dstA); | |
291 | check_migrated(&test.srcB, &test.dstB); | |
292 | } | |
293 | ||
294 | static void | |
295 | test_dbus_vmstate_only_a(void) | |
296 | { | |
297 | Test test = { .id_list = "idA" }; | |
298 | ||
299 | test_dbus_vmstate(&test); | |
300 | ||
301 | check_migrated(&test.srcA, &test.dstA); | |
302 | check_not_migrated(&test.srcB, &test.dstB); | |
303 | } | |
304 | ||
305 | static void | |
306 | test_dbus_vmstate_missing_src(void) | |
307 | { | |
308 | Test test = { .id_list = "idA,idC", .migrate_fail = true }; | |
309 | ||
310 | /* run in subprocess to silence QEMU error reporting */ | |
311 | if (g_test_subprocess()) { | |
312 | test_dbus_vmstate(&test); | |
313 | check_not_migrated(&test.srcA, &test.dstA); | |
314 | check_not_migrated(&test.srcB, &test.dstB); | |
315 | return; | |
316 | } | |
317 | ||
318 | g_test_trap_subprocess(NULL, 0, 0); | |
319 | g_test_trap_assert_passed(); | |
320 | } | |
321 | ||
322 | static void | |
323 | test_dbus_vmstate_missing_dst(void) | |
324 | { | |
325 | Test test = { .id_list = "idA,idB", | |
326 | .without_dst_b = true, | |
327 | .migrate_fail = true }; | |
328 | ||
329 | /* run in subprocess to silence QEMU error reporting */ | |
330 | if (g_test_subprocess()) { | |
331 | test_dbus_vmstate(&test); | |
332 | assert(test.srcA.save_called); | |
333 | assert(test.srcB.save_called); | |
334 | assert(!test.dstB.save_called); | |
335 | return; | |
336 | } | |
337 | ||
338 | g_test_trap_subprocess(NULL, 0, 0); | |
339 | g_test_trap_assert_passed(); | |
340 | } | |
341 | ||
342 | int | |
343 | main(int argc, char **argv) | |
344 | { | |
345 | GError *err = NULL; | |
346 | g_autofree char *dbus_daemon = NULL; | |
347 | int ret; | |
348 | ||
349 | dbus_daemon = g_build_filename(G_STRINGIFY(SRCDIR), | |
350 | "tests", | |
351 | "dbus-vmstate-daemon.sh", | |
352 | NULL); | |
353 | g_setenv("G_TEST_DBUS_DAEMON", dbus_daemon, true); | |
354 | ||
355 | g_test_init(&argc, &argv, NULL); | |
356 | ||
357 | workdir = g_dir_make_tmp("dbus-vmstate-test-XXXXXX", &err); | |
358 | if (!workdir) { | |
359 | g_error("Unable to create temporary dir: %s\n", err->message); | |
360 | exit(1); | |
361 | } | |
362 | ||
363 | g_setenv("DBUS_VMSTATE_TEST_TMPDIR", workdir, true); | |
364 | ||
365 | qtest_add_func("/dbus-vmstate/without-list", | |
366 | test_dbus_vmstate_without_list); | |
367 | qtest_add_func("/dbus-vmstate/with-list", | |
368 | test_dbus_vmstate_with_list); | |
369 | qtest_add_func("/dbus-vmstate/only-a", | |
370 | test_dbus_vmstate_only_a); | |
371 | qtest_add_func("/dbus-vmstate/missing-src", | |
372 | test_dbus_vmstate_missing_src); | |
373 | qtest_add_func("/dbus-vmstate/missing-dst", | |
374 | test_dbus_vmstate_missing_dst); | |
375 | ||
376 | ret = g_test_run(); | |
377 | ||
378 | rmdir(workdir); | |
379 | g_free(workdir); | |
380 | ||
381 | return ret; | |
382 | } |