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 "qapi/qmp/qjson.h"
15 
16 #include "migration-helpers.h"
17 
18 /*
19  * Number of seconds we wait when looking for migration
20  * status changes, to avoid test suite hanging forever
21  * when things go wrong. Needs to be higher enough to
22  * avoid false positives on loaded hosts.
23  */
24 #define MIGRATION_STATUS_WAIT_TIMEOUT 120
25 
26 bool got_stop;
27 
28 static void check_stop_event(QTestState *who)
29 {
30     QDict *event = qtest_qmp_event_ref(who, "STOP");
31     if (event) {
32         got_stop = true;
33         qobject_unref(event);
34     }
35 }
36 
37 #ifndef _WIN32
38 /*
39  * Events can get in the way of responses we are actually waiting for.
40  */
41 QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
42 {
43     va_list ap;
44     QDict *resp, *ret;
45 
46     va_start(ap, command);
47     qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
48     va_end(ap);
49 
50     resp = qtest_qmp_receive(who);
51     check_stop_event(who);
52 
53     g_assert(!qdict_haskey(resp, "error"));
54     g_assert(qdict_haskey(resp, "return"));
55 
56     ret = qdict_get_qdict(resp, "return");
57     qobject_ref(ret);
58     qobject_unref(resp);
59 
60     return ret;
61 }
62 #endif
63 
64 /*
65  * Events can get in the way of responses we are actually waiting for.
66  */
67 QDict *wait_command(QTestState *who, const char *command, ...)
68 {
69     va_list ap;
70     QDict *resp, *ret;
71 
72     va_start(ap, command);
73     resp = qtest_vqmp(who, command, ap);
74     va_end(ap);
75 
76     check_stop_event(who);
77 
78     g_assert(!qdict_haskey(resp, "error"));
79     g_assert(qdict_haskey(resp, "return"));
80 
81     ret = qdict_get_qdict(resp, "return");
82     qobject_ref(ret);
83     qobject_unref(resp);
84 
85     return ret;
86 }
87 
88 /*
89  * Execute the qmp command only
90  */
91 QDict *qmp_command(QTestState *who, const char *command, ...)
92 {
93     va_list ap;
94     QDict *resp, *ret;
95 
96     va_start(ap, command);
97     resp = qtest_vqmp(who, command, ap);
98     va_end(ap);
99 
100     g_assert(!qdict_haskey(resp, "error"));
101     g_assert(qdict_haskey(resp, "return"));
102 
103     ret = qdict_get_qdict(resp, "return");
104     qobject_ref(ret);
105     qobject_unref(resp);
106 
107     return ret;
108 }
109 
110 /*
111  * Send QMP command "migrate".
112  * Arguments are built from @fmt... (formatted like
113  * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
114  */
115 void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
116 {
117     va_list ap;
118     QDict *args, *rsp;
119 
120     va_start(ap, fmt);
121     args = qdict_from_vjsonf_nofail(fmt, ap);
122     va_end(ap);
123 
124     g_assert(!qdict_haskey(args, "uri"));
125     qdict_put_str(args, "uri", uri);
126 
127     rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args);
128 
129     g_assert(qdict_haskey(rsp, "return"));
130     qobject_unref(rsp);
131 }
132 
133 /*
134  * Note: caller is responsible to free the returned object via
135  * qobject_unref() after use
136  */
137 QDict *migrate_query(QTestState *who)
138 {
139     return wait_command(who, "{ 'execute': 'query-migrate' }");
140 }
141 
142 QDict *migrate_query_not_failed(QTestState *who)
143 {
144     const char *status;
145     QDict *rsp = migrate_query(who);
146     status = qdict_get_str(rsp, "status");
147     if (g_str_equal(status, "failed")) {
148         g_printerr("query-migrate shows failed migration: %s\n",
149                    qdict_get_str(rsp, "error-desc"));
150     }
151     g_assert(!g_str_equal(status, "failed"));
152     return rsp;
153 }
154 
155 /*
156  * Note: caller is responsible to free the returned object via
157  * g_free() after use
158  */
159 static gchar *migrate_query_status(QTestState *who)
160 {
161     QDict *rsp_return = migrate_query(who);
162     gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
163 
164     g_assert(status);
165     qobject_unref(rsp_return);
166 
167     return status;
168 }
169 
170 static bool check_migration_status(QTestState *who, const char *goal,
171                                    const char **ungoals)
172 {
173     bool ready;
174     char *current_status;
175     const char **ungoal;
176 
177     current_status = migrate_query_status(who);
178     ready = strcmp(current_status, goal) == 0;
179     if (!ungoals) {
180         g_assert_cmpstr(current_status, !=, "failed");
181         /*
182          * If looking for a state other than completed,
183          * completion of migration would cause the test to
184          * hang.
185          */
186         if (strcmp(goal, "completed") != 0) {
187             g_assert_cmpstr(current_status, !=, "completed");
188         }
189     } else {
190         for (ungoal = ungoals; *ungoal; ungoal++) {
191             g_assert_cmpstr(current_status, !=,  *ungoal);
192         }
193     }
194     g_free(current_status);
195     return ready;
196 }
197 
198 void wait_for_migration_status(QTestState *who,
199                                const char *goal, const char **ungoals)
200 {
201     g_test_timer_start();
202     while (!check_migration_status(who, goal, ungoals)) {
203         usleep(1000);
204 
205         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
206     }
207 }
208 
209 void wait_for_migration_complete(QTestState *who)
210 {
211     wait_for_migration_status(who, "completed", NULL);
212 }
213 
214 void wait_for_migration_fail(QTestState *from, bool allow_active)
215 {
216     g_test_timer_start();
217     QDict *rsp_return;
218     char *status;
219     bool failed;
220 
221     do {
222         status = migrate_query_status(from);
223         bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
224             (allow_active && !strcmp(status, "active"));
225         if (!result) {
226             fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
227                     __func__, status, allow_active);
228         }
229         g_assert(result);
230         failed = !strcmp(status, "failed");
231         g_free(status);
232 
233         g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
234     } while (!failed);
235 
236     /* Is the machine currently running? */
237     rsp_return = wait_command(from, "{ 'execute': 'query-status' }");
238     g_assert(qdict_haskey(rsp_return, "running"));
239     g_assert(qdict_get_bool(rsp_return, "running"));
240     qobject_unref(rsp_return);
241 }
242