qemu/tests/qtest/migration-helpers.c
<<
>>
Prefs
   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
  18bool got_stop;
  19
  20static 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 */
  32QDict *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 */
  57QDict *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 */
  83void 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 */
 105QDict *migrate_query(QTestState *who)
 106{
 107    return wait_command(who, "{ 'execute': 'query-migrate' }");
 108}
 109
 110/*
 111 * Note: caller is responsible to free the returned object via
 112 * g_free() after use
 113 */
 114static gchar *migrate_query_status(QTestState *who)
 115{
 116    QDict *rsp_return = migrate_query(who);
 117    gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
 118
 119    g_assert(status);
 120    qobject_unref(rsp_return);
 121
 122    return status;
 123}
 124
 125static bool check_migration_status(QTestState *who, const char *goal,
 126                                   const char **ungoals)
 127{
 128    bool ready;
 129    char *current_status;
 130    const char **ungoal;
 131
 132    current_status = migrate_query_status(who);
 133    ready = strcmp(current_status, goal) == 0;
 134    if (!ungoals) {
 135        g_assert_cmpstr(current_status, !=, "failed");
 136        /*
 137         * If looking for a state other than completed,
 138         * completion of migration would cause the test to
 139         * hang.
 140         */
 141        if (strcmp(goal, "completed") != 0) {
 142            g_assert_cmpstr(current_status, !=, "completed");
 143        }
 144    } else {
 145        for (ungoal = ungoals; *ungoal; ungoal++) {
 146            g_assert_cmpstr(current_status, !=,  *ungoal);
 147        }
 148    }
 149    g_free(current_status);
 150    return ready;
 151}
 152
 153void wait_for_migration_status(QTestState *who,
 154                               const char *goal, const char **ungoals)
 155{
 156    while (!check_migration_status(who, goal, ungoals)) {
 157        usleep(1000);
 158    }
 159}
 160
 161void wait_for_migration_complete(QTestState *who)
 162{
 163    wait_for_migration_status(who, "completed", NULL);
 164}
 165
 166void wait_for_migration_fail(QTestState *from, bool allow_active)
 167{
 168    QDict *rsp_return;
 169    char *status;
 170    bool failed;
 171
 172    do {
 173        status = migrate_query_status(from);
 174        bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
 175            (allow_active && !strcmp(status, "active"));
 176        if (!result) {
 177            fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
 178                    __func__, status, allow_active);
 179        }
 180        g_assert(result);
 181        failed = !strcmp(status, "failed");
 182        g_free(status);
 183    } while (!failed);
 184
 185    /* Is the machine currently running? */
 186    rsp_return = wait_command(from, "{ 'execute': 'query-status' }");
 187    g_assert(qdict_haskey(rsp_return, "running"));
 188    g_assert(qdict_get_bool(rsp_return, "running"));
 189    qobject_unref(rsp_return);
 190}
 191