blob: 516093b39a9e79f06a02ede440802ebe75729047 [file] [log] [blame]
Marc-André Lureaud77799c2019-12-16 14:59:44 +04001/*
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 "qapi/qmp/qjson.h"
15
16#include "migration-helpers.h"
17
18bool got_stop;
19
20static void stop_cb(void *opaque, const char *name, QDict *data)
21{
22 if (!strcmp(name, "STOP")) {
23 got_stop = true;
24 }
25}
26
27/*
28 * Events can get in the way of responses we are actually waiting for.
29 */
30QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
31{
32 va_list ap;
33
34 va_start(ap, command);
35 qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
36 va_end(ap);
37
38 return qtest_qmp_receive_success(who, stop_cb, NULL);
39}
40
41/*
42 * Events can get in the way of responses we are actually waiting for.
43 */
44QDict *wait_command(QTestState *who, const char *command, ...)
45{
46 va_list ap;
47
48 va_start(ap, command);
49 qtest_qmp_vsend(who, command, ap);
50 va_end(ap);
51
52 return qtest_qmp_receive_success(who, stop_cb, NULL);
53}
54
55/*
56 * Send QMP command "migrate".
57 * Arguments are built from @fmt... (formatted like
58 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
59 */
60void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
61{
62 va_list ap;
63 QDict *args, *rsp;
64
65 va_start(ap, fmt);
66 args = qdict_from_vjsonf_nofail(fmt, ap);
67 va_end(ap);
68
69 g_assert(!qdict_haskey(args, "uri"));
70 qdict_put_str(args, "uri", uri);
71
72 rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args);
73
74 g_assert(qdict_haskey(rsp, "return"));
75 qobject_unref(rsp);
76}
77
78/*
79 * Note: caller is responsible to free the returned object via
80 * qobject_unref() after use
81 */
82QDict *migrate_query(QTestState *who)
83{
84 return wait_command(who, "{ 'execute': 'query-migrate' }");
85}
86
87/*
88 * Note: caller is responsible to free the returned object via
89 * g_free() after use
90 */
91static gchar *migrate_query_status(QTestState *who)
92{
93 QDict *rsp_return = migrate_query(who);
94 gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
95
96 g_assert(status);
97 qobject_unref(rsp_return);
98
99 return status;
100}
101
102static bool check_migration_status(QTestState *who, const char *goal,
103 const char **ungoals)
104{
105 bool ready;
106 char *current_status;
107 const char **ungoal;
108
109 current_status = migrate_query_status(who);
110 ready = strcmp(current_status, goal) == 0;
111 if (!ungoals) {
112 g_assert_cmpstr(current_status, !=, "failed");
113 /*
114 * If looking for a state other than completed,
115 * completion of migration would cause the test to
116 * hang.
117 */
118 if (strcmp(goal, "completed") != 0) {
119 g_assert_cmpstr(current_status, !=, "completed");
120 }
121 } else {
122 for (ungoal = ungoals; *ungoal; ungoal++) {
123 g_assert_cmpstr(current_status, !=, *ungoal);
124 }
125 }
126 g_free(current_status);
127 return ready;
128}
129
130void wait_for_migration_status(QTestState *who,
131 const char *goal, const char **ungoals)
132{
133 while (!check_migration_status(who, goal, ungoals)) {
134 usleep(1000);
135 }
136}
137
138void wait_for_migration_complete(QTestState *who)
139{
140 wait_for_migration_status(who, "completed", NULL);
141}
142
143void wait_for_migration_fail(QTestState *from, bool allow_active)
144{
145 QDict *rsp_return;
146 char *status;
147 bool failed;
148
149 do {
150 status = migrate_query_status(from);
151 bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
152 (allow_active && !strcmp(status, "active"));
153 if (!result) {
154 fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
155 __func__, status, allow_active);
156 }
157 g_assert(result);
158 failed = !strcmp(status, "failed");
159 g_free(status);
160 } while (!failed);
161
162 /* Is the machine currently running? */
163 rsp_return = wait_command(from, "{ 'execute': 'query-status' }");
164 g_assert(qdict_haskey(rsp_return, "running"));
165 g_assert(qdict_get_bool(rsp_return, "running"));
166 qobject_unref(rsp_return);
167}