xref: /openbmc/qemu/tests/qtest/migration-helpers.c (revision 4f2f5b694d9dec2dde87a9155b0cb674dc3e6644)
1 /*
2  * QTest migration helpers
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 "qemu/ctype.h"
15 #include "qapi/qmp/qjson.h"
16 #include "qapi/qapi-visit-sockets.h"
17 #include "qapi/qobject-input-visitor.h"
18 #include "qapi/error.h"
19 
20 #include "migration-helpers.h"
21 
22 /*
23  * Number of seconds we wait when looking for migration
24  * status changes, to avoid test suite hanging forever
25  * when things go wrong. Needs to be higher enough to
26  * avoid false positives on loaded hosts.
27  */
28 #define MIGRATION_STATUS_WAIT_TIMEOUT 120
29 
30 static char *SocketAddress_to_str(SocketAddress *addr)
31 {
32     switch (addr->type) {
33     case SOCKET_ADDRESS_TYPE_INET:
34         return g_strdup_printf("tcp:%s:%s",
35                                addr->u.inet.host,
36                                addr->u.inet.port);
37     case SOCKET_ADDRESS_TYPE_UNIX:
38         return g_strdup_printf("unix:%s",
39                                addr->u.q_unix.path);
40     case SOCKET_ADDRESS_TYPE_FD:
41         return g_strdup_printf("fd:%s", addr->u.fd.str);
42     case SOCKET_ADDRESS_TYPE_VSOCK:
43         return g_strdup_printf("tcp:%s:%s",
44                                addr->u.vsock.cid,
45                                addr->u.vsock.port);
46     default:
47         return g_strdup("unknown address type");
48     }
49 }
50 
51 static SocketAddress *migrate_get_socket_address(QTestState *who)
52 {
53     QDict *rsp;
54     SocketAddressList *addrs;
55     SocketAddress *addr;
56     Visitor *iv = NULL;
57     QObject *object;
58 
59     rsp = migrate_query(who);
60     object = qdict_get(rsp, "socket-address");
61 
62     iv = qobject_input_visitor_new(object);
63     visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort);
64     addr = addrs->value;
65     visit_free(iv);
66 
67     qobject_unref(rsp);
68     return addr;
69 }
70 
71 static char *
72 migrate_get_connect_uri(QTestState *who)
73 {
74     SocketAddress *addrs;
75     char *connect_uri;
76 
77     addrs = migrate_get_socket_address(who);
78     connect_uri = SocketAddress_to_str(addrs);
79 
80     qapi_free_SocketAddress(addrs);
81     return connect_uri;
82 }
83 
84 bool migrate_watch_for_events(QTestState *who, const char *name,
85                               QDict *event, void *opaque)
86 {
87     QTestMigrationState *state = opaque;
88 
89     if (g_str_equal(name, "STOP")) {
90         state->stop_seen = true;
91         return true;
92     } else if (g_str_equal(name, "SUSPEND")) {
93         state->suspend_seen = true;
94         return true;
95     } else if (g_str_equal(name, "RESUME")) {
96         state->resume_seen = true;
97         return true;
98     }
99 
100     return false;
101 }
102 
103 void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...)
104 {
105     va_list ap;
106     QDict *args, *err;
107 
108     va_start(ap, fmt);
109     args = qdict_from_vjsonf_nofail(fmt, ap);
110     va_end(ap);
111 
112     g_assert(!qdict_haskey(args, "uri"));
113     qdict_put_str(args, "uri", uri);
114 
115     err = qtest_qmp_assert_failure_ref(
116         who, "{ 'execute': 'migrate', 'arguments': %p}", args);
117 
118     g_assert(qdict_haskey(err, "desc"));
119 
120     qobject_unref(err);
121 }
122 
123 /*
124  * Send QMP command "migrate".
125  * Arguments are built from @fmt... (formatted like
126  * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
127  */
128 void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
129                  const char *fmt, ...)
130 {
131     va_list ap;
132     QDict *args;
133     g_autofree char *connect_uri = NULL;
134 
135     va_start(ap, fmt);
136     args = qdict_from_vjsonf_nofail(fmt, ap);
137     va_end(ap);
138 
139     g_assert(!qdict_haskey(args, "uri"));
140     if (!uri) {
141         connect_uri = migrate_get_connect_uri(to);
142     }
143     qdict_put_str(args, "uri", uri ? uri : connect_uri);
144 
145     qtest_qmp_assert_success(who,
146                              "{ 'execute': 'migrate', 'arguments': %p}", args);
147 }
148 
149 void migrate_set_capability(QTestState *who, const char *capability,
150                             bool value)
151 {
152     qtest_qmp_assert_success(who,
153                              "{ 'execute': 'migrate-set-capabilities',"
154                              "'arguments': { "
155                              "'capabilities': [ { "
156                              "'capability': %s, 'state': %i } ] } }",
157                              capability, value);
158 }
159 
160 void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
161 {
162     va_list ap;
163     QDict *args, *rsp, *data;
164 
165     va_start(ap, fmt);
166     args = qdict_from_vjsonf_nofail(fmt, ap);
167     va_end(ap);
168 
169     g_assert(!qdict_haskey(args, "uri"));
170     qdict_put_str(args, "uri", uri);
171 
172     migrate_set_capability(to, "events", true);
173 
174     rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
175                     args);
176 
177     if (!qdict_haskey(rsp, "return")) {
178         g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
179         g_test_message("%s", s->str);
180     }
181 
182     g_assert(qdict_haskey(rsp, "return"));
183     qobject_unref(rsp);
184 
185     rsp = qtest_qmp_eventwait_ref(to, "MIGRATION");
186     g_assert(qdict_haskey(rsp, "data"));
187 
188     data = qdict_get_qdict(rsp, "data");
189     g_assert(qdict_haskey(data, "status"));
190     g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup");
191 
192     qobject_unref(rsp);
193 }
194 
195 /*
196  * Note: caller is responsible to free the returned object via
197  * qobject_unref() after use
198  */
199 QDict *migrate_query(QTestState *who)
200 {
201     return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
202 }
203 
204 QDict *migrate_query_not_failed(QTestState *who)
205 {
206     const char *status;
207     QDict *rsp = migrate_query(who);
208     status = qdict_get_str(rsp, "status");
209     if (g_str_equal(status, "failed")) {
210         g_printerr("query-migrate shows failed migration: %s\n",
211                    qdict_get_str(rsp, "error-desc"));
212     }
213     g_assert(!g_str_equal(status, "failed"));
214     return rsp;
215 }
216 
217 /*
218  * Note: caller is responsible to free the returned object via
219  * g_free() after use
220  */
221 static gchar *migrate_query_status(QTestState *who)
222 {
223     QDict *rsp_return = migrate_query(who);
224     gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
225 
226     g_assert(status);
227     qobject_unref(rsp_return);
228 
229     return status;
230 }
231 
232 static bool check_migration_status(QTestState *who, const char *goal,
233                                    const char **ungoals)
234 {
235     bool ready;
236     char *current_status;
237     const char **ungoal;
238 
239     current_status = migrate_query_status(who);
240     ready = strcmp(current_status, goal) == 0;
241     if (!ungoals) {
242         g_assert_cmpstr(current_status, !=, "failed");
243         /*
244          * If looking for a state other than completed,
245          * completion of migration would cause the test to
246          * hang.
247          */
248         if (strcmp(goal, "completed") != 0) {
249             g_assert_cmpstr(current_status, !=, "completed");
250         }
251     } else {
252         for (ungoal = ungoals; *ungoal; ungoal++) {
253             g_assert_cmpstr(current_status, !=,  *ungoal);
254         }
255     }
256     g_free(current_status);
257     return ready;
258 }
259 
260 void wait_for_migration_status(QTestState *who,
261                                const char *goal, const char **ungoals)
262 {
263     g_test_timer_start();
264     while (!check_migration_status(who, goal, ungoals)) {
265         usleep(1000);
266 
267         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
268     }
269 }
270 
271 void wait_for_migration_complete(QTestState *who)
272 {
273     wait_for_migration_status(who, "completed", NULL);
274 }
275 
276 void wait_for_migration_fail(QTestState *from, bool allow_active)
277 {
278     g_test_timer_start();
279     QDict *rsp_return;
280     char *status;
281     bool failed;
282 
283     do {
284         status = migrate_query_status(from);
285         bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
286             (allow_active && !strcmp(status, "active"));
287         if (!result) {
288             fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
289                     __func__, status, allow_active);
290         }
291         g_assert(result);
292         failed = !strcmp(status, "failed");
293         g_free(status);
294 
295         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
296     } while (!failed);
297 
298     /* Is the machine currently running? */
299     rsp_return = qtest_qmp_assert_success_ref(from,
300                                               "{ 'execute': 'query-status' }");
301     g_assert(qdict_haskey(rsp_return, "running"));
302     g_assert(qdict_get_bool(rsp_return, "running"));
303     qobject_unref(rsp_return);
304 }
305 
306 char *find_common_machine_version(const char *mtype, const char *var1,
307                                   const char *var2)
308 {
309     g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype);
310     g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype);
311 
312     g_assert(type1 && type2);
313 
314     if (g_str_equal(type1, type2)) {
315         /* either can be used */
316         return g_strdup(type1);
317     }
318 
319     if (qtest_has_machine_with_env(var2, type1)) {
320         return g_strdup(type1);
321     }
322 
323     if (qtest_has_machine_with_env(var1, type2)) {
324         return g_strdup(type2);
325     }
326 
327     g_test_message("No common machine version for machine type '%s' between "
328                    "binaries %s and %s", mtype, getenv(var1), getenv(var2));
329     g_assert_not_reached();
330 }
331 
332 char *resolve_machine_version(const char *alias, const char *var1,
333                               const char *var2)
334 {
335     const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE");
336     g_autofree char *machine_name = NULL;
337 
338     if (mname) {
339         const char *dash = strrchr(mname, '-');
340         const char *dot = strrchr(mname, '.');
341 
342         machine_name = g_strdup(mname);
343 
344         if (dash && dot) {
345             assert(qtest_has_machine(machine_name));
346             return g_steal_pointer(&machine_name);
347         }
348         /* else: probably an alias, let it be resolved below */
349     } else {
350         /* use the hardcoded alias */
351         machine_name = g_strdup(alias);
352     }
353 
354     return find_common_machine_version(machine_name, var1, var2);
355 }
356 
357 typedef struct {
358     char *name;
359     void (*func)(void);
360 } MigrationTest;
361 
362 static void migration_test_destroy(gpointer data)
363 {
364     MigrationTest *test = (MigrationTest *)data;
365 
366     g_free(test->name);
367     g_free(test);
368 }
369 
370 static void migration_test_wrapper(const void *data)
371 {
372     MigrationTest *test = (MigrationTest *)data;
373 
374     g_test_message("Running /%s%s", qtest_get_arch(), test->name);
375     test->func();
376 }
377 
378 void migration_test_add(const char *path, void (*fn)(void))
379 {
380     MigrationTest *test = g_new0(MigrationTest, 1);
381 
382     test->func = fn;
383     test->name = g_strdup(path);
384 
385     qtest_add_data_func_full(path, test, migration_test_wrapper,
386                              migration_test_destroy);
387 }
388