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 migrate_watch_for_stop(QTestState *who, const char *name, 27 QDict *event, void *opaque) 28 { 29 bool *seen = opaque; 30 31 if (g_str_equal(name, "STOP")) { 32 *seen = true; 33 return true; 34 } 35 36 return false; 37 } 38 39 bool migrate_watch_for_resume(QTestState *who, const char *name, 40 QDict *event, void *opaque) 41 { 42 bool *seen = opaque; 43 44 if (g_str_equal(name, "RESUME")) { 45 *seen = true; 46 return true; 47 } 48 49 return false; 50 } 51 52 void migrate_qmp_fail(QTestState *who, const char *uri, const char *fmt, ...) 53 { 54 va_list ap; 55 QDict *args, *err; 56 57 va_start(ap, fmt); 58 args = qdict_from_vjsonf_nofail(fmt, ap); 59 va_end(ap); 60 61 g_assert(!qdict_haskey(args, "uri")); 62 qdict_put_str(args, "uri", uri); 63 64 err = qtest_qmp_assert_failure_ref( 65 who, "{ 'execute': 'migrate', 'arguments': %p}", args); 66 67 g_assert(qdict_haskey(err, "desc")); 68 69 qobject_unref(err); 70 } 71 72 /* 73 * Send QMP command "migrate". 74 * Arguments are built from @fmt... (formatted like 75 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. 76 */ 77 void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...) 78 { 79 va_list ap; 80 QDict *args; 81 82 va_start(ap, fmt); 83 args = qdict_from_vjsonf_nofail(fmt, ap); 84 va_end(ap); 85 86 g_assert(!qdict_haskey(args, "uri")); 87 qdict_put_str(args, "uri", uri); 88 89 qtest_qmp_assert_success(who, 90 "{ 'execute': 'migrate', 'arguments': %p}", args); 91 } 92 93 void migrate_set_capability(QTestState *who, const char *capability, 94 bool value) 95 { 96 qtest_qmp_assert_success(who, 97 "{ 'execute': 'migrate-set-capabilities'," 98 "'arguments': { " 99 "'capabilities': [ { " 100 "'capability': %s, 'state': %i } ] } }", 101 capability, value); 102 } 103 104 void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) 105 { 106 va_list ap; 107 QDict *args, *rsp, *data; 108 109 va_start(ap, fmt); 110 args = qdict_from_vjsonf_nofail(fmt, ap); 111 va_end(ap); 112 113 g_assert(!qdict_haskey(args, "uri")); 114 qdict_put_str(args, "uri", uri); 115 116 migrate_set_capability(to, "events", true); 117 118 rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", 119 args); 120 g_assert(qdict_haskey(rsp, "return")); 121 qobject_unref(rsp); 122 123 rsp = qtest_qmp_eventwait_ref(to, "MIGRATION"); 124 g_assert(qdict_haskey(rsp, "data")); 125 126 data = qdict_get_qdict(rsp, "data"); 127 g_assert(qdict_haskey(data, "status")); 128 g_assert_cmpstr(qdict_get_str(data, "status"), ==, "setup"); 129 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 qtest_qmp_assert_success_ref(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 = qtest_qmp_assert_success_ref(from, 238 "{ 'execute': 'query-status' }"); 239 g_assert(qdict_haskey(rsp_return, "running")); 240 g_assert(qdict_get_bool(rsp_return, "running")); 241 qobject_unref(rsp_return); 242 } 243