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