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