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