qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

migration-helpers.c (6079B)


      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 got_stop;
     27 
     28 static void check_stop_event(QTestState *who)
     29 {
     30     QDict *event = qtest_qmp_event_ref(who, "STOP");
     31     if (event) {
     32         got_stop = true;
     33         qobject_unref(event);
     34     }
     35 }
     36 
     37 #ifndef _WIN32
     38 /*
     39  * Events can get in the way of responses we are actually waiting for.
     40  */
     41 QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
     42 {
     43     va_list ap;
     44     QDict *resp, *ret;
     45 
     46     va_start(ap, command);
     47     qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
     48     va_end(ap);
     49 
     50     resp = qtest_qmp_receive(who);
     51     check_stop_event(who);
     52 
     53     g_assert(!qdict_haskey(resp, "error"));
     54     g_assert(qdict_haskey(resp, "return"));
     55 
     56     ret = qdict_get_qdict(resp, "return");
     57     qobject_ref(ret);
     58     qobject_unref(resp);
     59 
     60     return ret;
     61 }
     62 #endif
     63 
     64 /*
     65  * Events can get in the way of responses we are actually waiting for.
     66  */
     67 QDict *wait_command(QTestState *who, const char *command, ...)
     68 {
     69     va_list ap;
     70     QDict *resp, *ret;
     71 
     72     va_start(ap, command);
     73     resp = qtest_vqmp(who, command, ap);
     74     va_end(ap);
     75 
     76     check_stop_event(who);
     77 
     78     g_assert(!qdict_haskey(resp, "error"));
     79     g_assert(qdict_haskey(resp, "return"));
     80 
     81     ret = qdict_get_qdict(resp, "return");
     82     qobject_ref(ret);
     83     qobject_unref(resp);
     84 
     85     return ret;
     86 }
     87 
     88 /*
     89  * Execute the qmp command only
     90  */
     91 QDict *qmp_command(QTestState *who, const char *command, ...)
     92 {
     93     va_list ap;
     94     QDict *resp, *ret;
     95 
     96     va_start(ap, command);
     97     resp = qtest_vqmp(who, command, ap);
     98     va_end(ap);
     99 
    100     g_assert(!qdict_haskey(resp, "error"));
    101     g_assert(qdict_haskey(resp, "return"));
    102 
    103     ret = qdict_get_qdict(resp, "return");
    104     qobject_ref(ret);
    105     qobject_unref(resp);
    106 
    107     return ret;
    108 }
    109 
    110 /*
    111  * Send QMP command "migrate".
    112  * Arguments are built from @fmt... (formatted like
    113  * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
    114  */
    115 void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
    116 {
    117     va_list ap;
    118     QDict *args, *rsp;
    119 
    120     va_start(ap, fmt);
    121     args = qdict_from_vjsonf_nofail(fmt, ap);
    122     va_end(ap);
    123 
    124     g_assert(!qdict_haskey(args, "uri"));
    125     qdict_put_str(args, "uri", uri);
    126 
    127     rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args);
    128 
    129     g_assert(qdict_haskey(rsp, "return"));
    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 wait_command(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 = wait_command(from, "{ 'execute': 'query-status' }");
    238     g_assert(qdict_haskey(rsp_return, "running"));
    239     g_assert(qdict_get_bool(rsp_return, "running"));
    240     qobject_unref(rsp_return);
    241 }