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