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 stop_cb(void *opaque, const char *name, QDict *data)
  21{
  22    if (!strcmp(name, "STOP")) {
  23        got_stop = true;
  24    }
  25}
  26
  27/*
  28 * Events can get in the way of responses we are actually waiting for.
  29 */
  30QDict *wait_command_fd(QTestState *who, int fd, const char *command, ...)
  31{
  32    va_list ap;
  33
  34    va_start(ap, command);
  35    qtest_qmp_vsend_fds(who, &fd, 1, command, ap);
  36    va_end(ap);
  37
  38    return qtest_qmp_receive_success(who, stop_cb, NULL);
  39}
  40
  41/*
  42 * Events can get in the way of responses we are actually waiting for.
  43 */
  44QDict *wait_command(QTestState *who, const char *command, ...)
  45{
  46    va_list ap;
  47
  48    va_start(ap, command);
  49    qtest_qmp_vsend(who, command, ap);
  50    va_end(ap);
  51
  52    return qtest_qmp_receive_success(who, stop_cb, NULL);
  53}
  54
  55/*
  56 * Send QMP command "migrate".
  57 * Arguments are built from @fmt... (formatted like
  58 * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
  59 */
  60void migrate_qmp(QTestState *who, const char *uri, const char *fmt, ...)
  61{
  62    va_list ap;
  63    QDict *args, *rsp;
  64
  65    va_start(ap, fmt);
  66    args = qdict_from_vjsonf_nofail(fmt, ap);
  67    va_end(ap);
  68
  69    g_assert(!qdict_haskey(args, "uri"));
  70    qdict_put_str(args, "uri", uri);
  71
  72    rsp = qtest_qmp(who, "{ 'execute': 'migrate', 'arguments': %p}", args);
  73
  74    g_assert(qdict_haskey(rsp, "return"));
  75    qobject_unref(rsp);
  76}
  77
  78/*
  79 * Note: caller is responsible to free the returned object via
  80 * qobject_unref() after use
  81 */
  82QDict *migrate_query(QTestState *who)
  83{
  84    return wait_command(who, "{ 'execute': 'query-migrate' }");
  85}
  86
  87/*
  88 * Note: caller is responsible to free the returned object via
  89 * g_free() after use
  90 */
  91static gchar *migrate_query_status(QTestState *who)
  92{
  93    QDict *rsp_return = migrate_query(who);
  94    gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
  95
  96    g_assert(status);
  97    qobject_unref(rsp_return);
  98
  99    return status;
 100}
 101
 102static bool check_migration_status(QTestState *who, const char *goal,
 103                                   const char **ungoals)
 104{
 105    bool ready;
 106    char *current_status;
 107    const char **ungoal;
 108
 109    current_status = migrate_query_status(who);
 110    ready = strcmp(current_status, goal) == 0;
 111    if (!ungoals) {
 112        g_assert_cmpstr(current_status, !=, "failed");
 113        /*
 114         * If looking for a state other than completed,
 115         * completion of migration would cause the test to
 116         * hang.
 117         */
 118        if (strcmp(goal, "completed") != 0) {
 119            g_assert_cmpstr(current_status, !=, "completed");
 120        }
 121    } else {
 122        for (ungoal = ungoals; *ungoal; ungoal++) {
 123            g_assert_cmpstr(current_status, !=,  *ungoal);
 124        }
 125    }
 126    g_free(current_status);
 127    return ready;
 128}
 129
 130void wait_for_migration_status(QTestState *who,
 131                               const char *goal, const char **ungoals)
 132{
 133    while (!check_migration_status(who, goal, ungoals)) {
 134        usleep(1000);
 135    }
 136}
 137
 138void wait_for_migration_complete(QTestState *who)
 139{
 140    wait_for_migration_status(who, "completed", NULL);
 141}
 142
 143void wait_for_migration_fail(QTestState *from, bool allow_active)
 144{
 145    QDict *rsp_return;
 146    char *status;
 147    bool failed;
 148
 149    do {
 150        status = migrate_query_status(from);
 151        bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
 152            (allow_active && !strcmp(status, "active"));
 153        if (!result) {
 154            fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
 155                    __func__, status, allow_active);
 156        }
 157        g_assert(result);
 158        failed = !strcmp(status, "failed");
 159        g_free(status);
 160    } while (!failed);
 161
 162    /* Is the machine currently running? */
 163    rsp_return = wait_command(from, "{ 'execute': 'query-status' }");
 164    g_assert(qdict_haskey(rsp_return, "running"));
 165    g_assert(qdict_get_bool(rsp_return, "running"));
 166    qobject_unref(rsp_return);
 167}
 168