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