All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Fabiano Rosas <farosas@suse.de>
Cc: qemu-devel@nongnu.org, "Peter Xu" <peterx@redhat.com>,
	"Peter Maydell" <peter.maydell@linaro.org>,
	"Alex Bennée" <alex.bennee@linaro.org>,
	"Laurent Vivier" <lvivier@redhat.com>,
	"Paolo Bonzini" <pbonzini@redhat.com>
Subject: Re: [PATCH 06/22] tests/qtest/migration: Move qmp helpers to a separate file
Date: Wed, 6 Nov 2024 10:52:45 +0000	[thread overview]
Message-ID: <ZytKfYirP-R1mRNV@redhat.com> (raw)
In-Reply-To: <20241105180837.5990-7-farosas@suse.de>

On Tue, Nov 05, 2024 at 03:08:21PM -0300, Fabiano Rosas wrote:
> We current have a bunch of non-test functions in migration-test.c and
> some others in migration-helpers.c. In order to split migration-test.c
> into separate test binaries, these helpers need to go somewhere
> else.
> 
> To avoid making migration-helpers even larger, move all QMP-related
> functions into a new migration-qmp.c file and put it under the
> qtest/migration/ directory.
> 
> The new file holds everything that has as its main responsibility to
> call into QMP.
> 
> Signed-off-by: Fabiano Rosas <farosas@suse.de>
> ---
>  tests/qtest/meson.build                |   1 +
>  tests/qtest/migration-helpers.c        | 250 +------------
>  tests/qtest/migration-helpers.h        |  18 +-
>  tests/qtest/migration-test.c           | 237 +-----------
>  tests/qtest/migration/migration-qmp.c  | 485 +++++++++++++++++++++++++
>  tests/qtest/migration/migration-util.h |  45 +++

I would have expected the header to be named 'migration-qmp.h' to match
the source file naming, and not merge all of migration-helpers.h into
it in the next patch which seems to partially defeat the point of splitting
up the files.

>  tests/qtest/virtio-net-failover.c      |   1 +
>  7 files changed, 541 insertions(+), 496 deletions(-)
>  create mode 100644 tests/qtest/migration/migration-qmp.c
>  create mode 100644 tests/qtest/migration/migration-util.h
> 
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index f6001a0b0f..30dff3abe0 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -331,6 +331,7 @@ tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c']
>  migration_files = [files(
>    'migration-helpers.c',
>    'migration/bootfile.c',
> +  'migration/migration-qmp.c',
>  )]
>  
>  if gnutls.found()
> diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
> index 2786f9c860..218ee4e59f 100644
> --- a/tests/qtest/migration-helpers.c
> +++ b/tests/qtest/migration-helpers.c
> @@ -12,7 +12,6 @@
>  
>  #include "qemu/osdep.h"
>  #include "qemu/ctype.h"
> -#include "qapi/qmp/qjson.h"
>  #include "qapi/qapi-visit-sockets.h"
>  #include "qapi/qobject-input-visitor.h"
>  #include "qapi/error.h"
> @@ -23,14 +22,6 @@
>  #include "migration-helpers.h"
>  #include "migration/bootfile.h"
>  
> -/*
> - * Number of seconds we wait when looking for migration
> - * status changes, to avoid test suite hanging forever
> - * when things go wrong. Needs to be higher enough to
> - * avoid false positives on loaded hosts.
> - */
> -#define MIGRATION_STATUS_WAIT_TIMEOUT 120
> -
>  static char *SocketAddress_to_str(SocketAddress *addr)
>  {
>      switch (addr->type) {
> @@ -100,8 +91,7 @@ static SocketAddressList *migrate_get_socket_address(QTestState *who)
>      return addrs;
>  }
>  
> -static char *
> -migrate_get_connect_uri(QTestState *who)
> +char *migrate_get_connect_uri(QTestState *who)
>  {
>      SocketAddressList *addrs;
>      char *connect_uri;
> @@ -126,7 +116,7 @@ migrate_get_connect_qdict(QTestState *who)
>      return connect_qdict;
>  }
>  
> -static void migrate_set_ports(QTestState *to, QList *channel_list)
> +void migrate_set_ports(QTestState *to, QList *channel_list)
>  {
>      QDict *addr;
>      QListEntry *entry;
> @@ -168,222 +158,6 @@ bool migrate_watch_for_events(QTestState *who, const char *name,
>      return false;
>  }
>  
> -void migrate_qmp_fail(QTestState *who, const char *uri,
> -                      const char *channels, const char *fmt, ...)
> -{
> -    va_list ap;
> -    QDict *args, *err;
> -
> -    va_start(ap, fmt);
> -    args = qdict_from_vjsonf_nofail(fmt, ap);
> -    va_end(ap);
> -
> -    g_assert(!qdict_haskey(args, "uri"));
> -    if (uri) {
> -        qdict_put_str(args, "uri", uri);
> -    }
> -
> -    g_assert(!qdict_haskey(args, "channels"));
> -    if (channels) {
> -        QObject *channels_obj = qobject_from_json(channels, &error_abort);
> -        qdict_put_obj(args, "channels", channels_obj);
> -    }
> -
> -    err = qtest_qmp_assert_failure_ref(
> -        who, "{ 'execute': 'migrate', 'arguments': %p}", args);
> -
> -    g_assert(qdict_haskey(err, "desc"));
> -
> -    qobject_unref(err);
> -}
> -
> -/*
> - * Send QMP command "migrate".
> - * Arguments are built from @fmt... (formatted like
> - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
> - */
> -void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
> -                 const char *channels, const char *fmt, ...)
> -{
> -    va_list ap;
> -    QDict *args;
> -    g_autofree char *connect_uri = NULL;
> -
> -    va_start(ap, fmt);
> -    args = qdict_from_vjsonf_nofail(fmt, ap);
> -    va_end(ap);
> -
> -    g_assert(!qdict_haskey(args, "uri"));
> -    if (uri) {
> -        qdict_put_str(args, "uri", uri);
> -    } else if (!channels) {
> -        connect_uri = migrate_get_connect_uri(to);
> -        qdict_put_str(args, "uri", connect_uri);
> -    }
> -
> -    g_assert(!qdict_haskey(args, "channels"));
> -    if (channels) {
> -        QObject *channels_obj = qobject_from_json(channels, &error_abort);
> -        QList *channel_list = qobject_to(QList, channels_obj);
> -        migrate_set_ports(to, channel_list);
> -        qdict_put_obj(args, "channels", channels_obj);
> -    }
> -
> -    qtest_qmp_assert_success(who,
> -                             "{ 'execute': 'migrate', 'arguments': %p}", args);
> -}
> -
> -void migrate_set_capability(QTestState *who, const char *capability,
> -                            bool value)
> -{
> -    qtest_qmp_assert_success(who,
> -                             "{ 'execute': 'migrate-set-capabilities',"
> -                             "'arguments': { "
> -                             "'capabilities': [ { "
> -                             "'capability': %s, 'state': %i } ] } }",
> -                             capability, value);
> -}
> -
> -void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
> -{
> -    va_list ap;
> -    QDict *args, *rsp;
> -
> -    va_start(ap, fmt);
> -    args = qdict_from_vjsonf_nofail(fmt, ap);
> -    va_end(ap);
> -
> -    g_assert(!qdict_haskey(args, "uri"));
> -    qdict_put_str(args, "uri", uri);
> -
> -    /* This function relies on the event to work, make sure it's enabled */
> -    migrate_set_capability(to, "events", true);
> -
> -    rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
> -                    args);
> -
> -    if (!qdict_haskey(rsp, "return")) {
> -        g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
> -        g_test_message("%s", s->str);
> -    }
> -
> -    g_assert(qdict_haskey(rsp, "return"));
> -    qobject_unref(rsp);
> -
> -    migration_event_wait(to, "setup");
> -}
> -
> -/*
> - * Note: caller is responsible to free the returned object via
> - * qobject_unref() after use
> - */
> -QDict *migrate_query(QTestState *who)
> -{
> -    return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
> -}
> -
> -QDict *migrate_query_not_failed(QTestState *who)
> -{
> -    const char *status;
> -    QDict *rsp = migrate_query(who);
> -    status = qdict_get_str(rsp, "status");
> -    if (g_str_equal(status, "failed")) {
> -        g_printerr("query-migrate shows failed migration: %s\n",
> -                   qdict_get_str(rsp, "error-desc"));
> -    }
> -    g_assert(!g_str_equal(status, "failed"));
> -    return rsp;
> -}
> -
> -/*
> - * Note: caller is responsible to free the returned object via
> - * g_free() after use
> - */
> -static gchar *migrate_query_status(QTestState *who)
> -{
> -    QDict *rsp_return = migrate_query(who);
> -    gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
> -
> -    g_assert(status);
> -    qobject_unref(rsp_return);
> -
> -    return status;
> -}
> -
> -static bool check_migration_status(QTestState *who, const char *goal,
> -                                   const char **ungoals)
> -{
> -    bool ready;
> -    char *current_status;
> -    const char **ungoal;
> -
> -    current_status = migrate_query_status(who);
> -    ready = strcmp(current_status, goal) == 0;
> -    if (!ungoals) {
> -        g_assert_cmpstr(current_status, !=, "failed");
> -        /*
> -         * If looking for a state other than completed,
> -         * completion of migration would cause the test to
> -         * hang.
> -         */
> -        if (strcmp(goal, "completed") != 0) {
> -            g_assert_cmpstr(current_status, !=, "completed");
> -        }
> -    } else {
> -        for (ungoal = ungoals; *ungoal; ungoal++) {
> -            g_assert_cmpstr(current_status, !=,  *ungoal);
> -        }
> -    }
> -    g_free(current_status);
> -    return ready;
> -}
> -
> -void wait_for_migration_status(QTestState *who,
> -                               const char *goal, const char **ungoals)
> -{
> -    g_test_timer_start();
> -    while (!check_migration_status(who, goal, ungoals)) {
> -        usleep(1000);
> -
> -        g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
> -    }
> -}
> -
> -void wait_for_migration_complete(QTestState *who)
> -{
> -    wait_for_migration_status(who, "completed", NULL);
> -}
> -
> -void wait_for_migration_fail(QTestState *from, bool allow_active)
> -{
> -    g_test_timer_start();
> -    QDict *rsp_return;
> -    char *status;
> -    bool failed;
> -
> -    do {
> -        status = migrate_query_status(from);
> -        bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
> -            (allow_active && !strcmp(status, "active"));
> -        if (!result) {
> -            fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
> -                    __func__, status, allow_active);
> -        }
> -        g_assert(result);
> -        failed = !strcmp(status, "failed");
> -        g_free(status);
> -
> -        g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
> -    } while (!failed);
> -
> -    /* Is the machine currently running? */
> -    rsp_return = qtest_qmp_assert_success_ref(from,
> -                                              "{ 'execute': 'query-status' }");
> -    g_assert(qdict_haskey(rsp_return, "running"));
> -    g_assert(qdict_get_bool(rsp_return, "running"));
> -    qobject_unref(rsp_return);
> -}
> -
>  char *find_common_machine_version(const char *mtype, const char *var1,
>                                    const char *var2)
>  {
> @@ -509,23 +283,3 @@ bool probe_o_direct_support(const char *tmpfs)
>      return true;
>  }
>  #endif
> -
> -/*
> - * Wait for a "MIGRATION" event.  This is what Libvirt uses to track
> - * migration status changes.
> - */
> -void migration_event_wait(QTestState *s, const char *target)
> -{
> -    QDict *response, *data;
> -    const char *status;
> -    bool found;
> -
> -    do {
> -        response = qtest_qmp_eventwait_ref(s, "MIGRATION");
> -        data = qdict_get_qdict(response, "data");
> -        g_assert(data);
> -        status = qdict_get_str(data, "status");
> -        found = (strcmp(status, target) == 0);
> -        qobject_unref(response);
> -    } while (!found);
> -}
> diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h
> index 72dba369fb..2cb1f78d9e 100644
> --- a/tests/qtest/migration-helpers.h
> +++ b/tests/qtest/migration-helpers.h
> @@ -25,21 +25,6 @@ typedef struct QTestMigrationState {
>  bool migrate_watch_for_events(QTestState *who, const char *name,
>                                QDict *event, void *opaque);
>  
> -G_GNUC_PRINTF(5, 6)
> -void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
> -                 const char *channels, const char *fmt, ...);
> -
> -G_GNUC_PRINTF(3, 4)
> -void migrate_incoming_qmp(QTestState *who, const char *uri,
> -                          const char *fmt, ...);
> -
> -G_GNUC_PRINTF(4, 5)
> -void migrate_qmp_fail(QTestState *who, const char *uri,
> -                      const char *channels, const char *fmt, ...);
> -
> -void migrate_set_capability(QTestState *who, const char *capability,
> -                            bool value);
> -
>  QDict *migrate_query(QTestState *who);
>  QDict *migrate_query_not_failed(QTestState *who);
>  
> @@ -63,6 +48,7 @@ static inline bool probe_o_direct_support(const char *tmpfs)
>  }
>  #endif
>  void migration_test_add(const char *path, void (*fn)(void));
> -void migration_event_wait(QTestState *s, const char *target);
> +char *migrate_get_connect_uri(QTestState *who);
> +void migrate_set_ports(QTestState *to, QList *channel_list);
>  
>  #endif /* MIGRATION_HELPERS_H */
> diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
> index 599adc422c..f55423467d 100644
> --- a/tests/qtest/migration-test.c
> +++ b/tests/qtest/migration-test.c
> @@ -13,18 +13,18 @@
>  #include "qemu/osdep.h"
>  
>  #include "libqtest.h"
> -#include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qlist.h"
>  #include "qemu/module.h"
>  #include "qemu/option.h"
>  #include "qemu/range.h"
>  #include "qemu/sockets.h"
>  #include "chardev/char.h"
>  #include "crypto/tlscredspsk.h"
> -#include "qapi/qmp/qlist.h"
>  #include "ppc-util.h"
>  
>  #include "migration-helpers.h"
>  #include "migration/bootfile.h"
> +#include "migration/migration-util.h"
>  #ifdef CONFIG_GNUTLS
>  # include "tests/unit/crypto-tls-psk-helpers.h"
>  # ifdef CONFIG_TASN1
> @@ -170,89 +170,6 @@ static void wait_for_serial(const char *side)
>      } while (true);
>  }
>  
> -static void wait_for_stop(QTestState *who, QTestMigrationState *state)
> -{
> -    if (!state->stop_seen) {
> -        qtest_qmp_eventwait(who, "STOP");
> -    }
> -}
> -
> -static void wait_for_resume(QTestState *who, QTestMigrationState *state)
> -{
> -    if (!state->resume_seen) {
> -        qtest_qmp_eventwait(who, "RESUME");
> -    }
> -}
> -
> -static void wait_for_suspend(QTestState *who, QTestMigrationState *state)
> -{
> -    if (state->suspend_me && !state->suspend_seen) {
> -        qtest_qmp_eventwait(who, "SUSPEND");
> -    }
> -}
> -
> -/*
> - * It's tricky to use qemu's migration event capability with qtest,
> - * events suddenly appearing confuse the qmp()/hmp() responses.
> - */
> -
> -static int64_t read_ram_property_int(QTestState *who, const char *property)
> -{
> -    QDict *rsp_return, *rsp_ram;
> -    int64_t result;
> -
> -    rsp_return = migrate_query_not_failed(who);
> -    if (!qdict_haskey(rsp_return, "ram")) {
> -        /* Still in setup */
> -        result = 0;
> -    } else {
> -        rsp_ram = qdict_get_qdict(rsp_return, "ram");
> -        result = qdict_get_try_int(rsp_ram, property, 0);
> -    }
> -    qobject_unref(rsp_return);
> -    return result;
> -}
> -
> -static int64_t read_migrate_property_int(QTestState *who, const char *property)
> -{
> -    QDict *rsp_return;
> -    int64_t result;
> -
> -    rsp_return = migrate_query_not_failed(who);
> -    result = qdict_get_try_int(rsp_return, property, 0);
> -    qobject_unref(rsp_return);
> -    return result;
> -}
> -
> -static uint64_t get_migration_pass(QTestState *who)
> -{
> -    return read_ram_property_int(who, "dirty-sync-count");
> -}
> -
> -static void read_blocktime(QTestState *who)
> -{
> -    QDict *rsp_return;
> -
> -    rsp_return = migrate_query_not_failed(who);
> -    g_assert(qdict_haskey(rsp_return, "postcopy-blocktime"));
> -    qobject_unref(rsp_return);
> -}
> -
> -/*
> - * Wait for two changes in the migration pass count, but bail if we stop.
> - */
> -static void wait_for_migration_pass(QTestState *who)
> -{
> -    uint64_t pass, prev_pass = 0, changes = 0;
> -
> -    while (changes < 2 && !src_state.stop_seen && !src_state.suspend_seen) {
> -        usleep(1000);
> -        pass = get_migration_pass(who);
> -        changes += (pass != prev_pass);
> -        prev_pass = pass;
> -    }
> -}
> -
>  static void check_guests_ram(QTestState *who)
>  {
>      /* Our ASM test will have been incrementing one byte from each page from
> @@ -308,114 +225,6 @@ static void cleanup(const char *filename)
>      unlink(path);
>  }
>  
> -static long long migrate_get_parameter_int(QTestState *who,
> -                                           const char *parameter)
> -{
> -    QDict *rsp;
> -    long long result;
> -
> -    rsp = qtest_qmp_assert_success_ref(
> -        who, "{ 'execute': 'query-migrate-parameters' }");
> -    result = qdict_get_int(rsp, parameter);
> -    qobject_unref(rsp);
> -    return result;
> -}
> -
> -static void migrate_check_parameter_int(QTestState *who, const char *parameter,
> -                                        long long value)
> -{
> -    long long result;
> -
> -    result = migrate_get_parameter_int(who, parameter);
> -    g_assert_cmpint(result, ==, value);
> -}
> -
> -static void migrate_set_parameter_int(QTestState *who, const char *parameter,
> -                                      long long value)
> -{
> -    qtest_qmp_assert_success(who,
> -                             "{ 'execute': 'migrate-set-parameters',"
> -                             "'arguments': { %s: %lld } }",
> -                             parameter, value);
> -    migrate_check_parameter_int(who, parameter, value);
> -}
> -
> -static char *migrate_get_parameter_str(QTestState *who,
> -                                       const char *parameter)
> -{
> -    QDict *rsp;
> -    char *result;
> -
> -    rsp = qtest_qmp_assert_success_ref(
> -        who, "{ 'execute': 'query-migrate-parameters' }");
> -    result = g_strdup(qdict_get_str(rsp, parameter));
> -    qobject_unref(rsp);
> -    return result;
> -}
> -
> -static void migrate_check_parameter_str(QTestState *who, const char *parameter,
> -                                        const char *value)
> -{
> -    g_autofree char *result = migrate_get_parameter_str(who, parameter);
> -    g_assert_cmpstr(result, ==, value);
> -}
> -
> -static void migrate_set_parameter_str(QTestState *who, const char *parameter,
> -                                      const char *value)
> -{
> -    qtest_qmp_assert_success(who,
> -                             "{ 'execute': 'migrate-set-parameters',"
> -                             "'arguments': { %s: %s } }",
> -                             parameter, value);
> -    migrate_check_parameter_str(who, parameter, value);
> -}
> -
> -static long long migrate_get_parameter_bool(QTestState *who,
> -                                            const char *parameter)
> -{
> -    QDict *rsp;
> -    int result;
> -
> -    rsp = qtest_qmp_assert_success_ref(
> -        who, "{ 'execute': 'query-migrate-parameters' }");
> -    result = qdict_get_bool(rsp, parameter);
> -    qobject_unref(rsp);
> -    return !!result;
> -}
> -
> -static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
> -                                         int value)
> -{
> -    int result;
> -
> -    result = migrate_get_parameter_bool(who, parameter);
> -    g_assert_cmpint(result, ==, value);
> -}
> -
> -static void migrate_set_parameter_bool(QTestState *who, const char *parameter,
> -                                       int value)
> -{
> -    qtest_qmp_assert_success(who,
> -                             "{ 'execute': 'migrate-set-parameters',"
> -                             "'arguments': { %s: %i } }",
> -                             parameter, value);
> -    migrate_check_parameter_bool(who, parameter, value);
> -}
> -
> -static void migrate_ensure_non_converge(QTestState *who)
> -{
> -    /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */
> -    migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000);
> -    migrate_set_parameter_int(who, "downtime-limit", 1);
> -}
> -
> -static void migrate_ensure_converge(QTestState *who)
> -{
> -    /* Should converge with 30s downtime + 1 gbs bandwidth limit */
> -    migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000);
> -    migrate_set_parameter_int(who, "downtime-limit", 30 * 1000);
> -}
> -
>  /*
>   * Our goal is to ensure that we run a single full migration
>   * iteration, and also dirty memory, ensuring that at least
> @@ -506,42 +315,6 @@ static void migrate_wait_for_dirty_mem(QTestState *from,
>      } while (qtest_readb(from, watch_address) == watch_byte);
>  }
>  
> -
> -static void migrate_pause(QTestState *who)
> -{
> -    qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }");
> -}
> -
> -static void migrate_continue(QTestState *who, const char *state)
> -{
> -    qtest_qmp_assert_success(who,
> -                             "{ 'execute': 'migrate-continue',"
> -                             "  'arguments': { 'state': %s } }",
> -                             state);
> -}
> -
> -static void migrate_recover(QTestState *who, const char *uri)
> -{
> -    qtest_qmp_assert_success(who,
> -                             "{ 'execute': 'migrate-recover', "
> -                             "  'id': 'recover-cmd', "
> -                             "  'arguments': { 'uri': %s } }",
> -                             uri);
> -}
> -
> -static void migrate_cancel(QTestState *who)
> -{
> -    qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }");
> -}
> -
> -static void migrate_postcopy_start(QTestState *from, QTestState *to)
> -{
> -    qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }");
> -
> -    wait_for_stop(from, &src_state);
> -    qtest_qmp_eventwait(to, "RESUME");
> -}
> -
>  typedef struct {
>      /*
>       * QTEST_LOG=1 may override this.  When QTEST_LOG=1, we always dump errors
> @@ -1277,7 +1050,7 @@ static void test_postcopy_common(MigrateCommon *args)
>      if (migrate_postcopy_prepare(&from, &to, args)) {
>          return;
>      }
> -    migrate_postcopy_start(from, to);
> +    migrate_postcopy_start(from, to, &src_state);
>      migrate_postcopy_complete(from, to, args);
>  }
>  
> @@ -1464,7 +1237,7 @@ static void test_postcopy_recovery_common(MigrateCommon *args)
>      migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096);
>  
>      /* Now we start the postcopy */
> -    migrate_postcopy_start(from, to);
> +    migrate_postcopy_start(from, to, &src_state);
>  
>      /*
>       * Wait until postcopy is really started; we can only run the
> @@ -1704,7 +1477,7 @@ static void test_precopy_common(MigrateCommon *args)
>               * for some dirty mem before switching to converge
>               */
>              while (args->iterations > 1) {
> -                wait_for_migration_pass(from);
> +                wait_for_migration_pass(from, &src_state);
>                  args->iterations--;
>              }
>              migrate_wait_for_dirty_mem(from, to);
> diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c
> new file mode 100644
> index 0000000000..89e069e96b
> --- /dev/null
> +++ b/tests/qtest/migration/migration-qmp.c
> @@ -0,0 +1,485 @@
> +/*
> + * QTest QMP helpers for migration
> + *
> + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates
> + *   based on the vhost-user-test.c that is:
> + *      Copyright (c) 2014 Virtual Open Systems Sarl.
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or later.
> + * See the COPYING file in the top-level directory.
> + *
> + */
> +
> +#include "qemu/osdep.h"
> +#include "libqtest.h"
> +#include "migration-helpers.h"
> +#include "migration-util.h"
> +#include "qapi/error.h"
> +#include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qjson.h"
> +#include "qapi/qmp/qlist.h"
> +
> +/*
> + * Number of seconds we wait when looking for migration
> + * status changes, to avoid test suite hanging forever
> + * when things go wrong. Needs to be higher enough to
> + * avoid false positives on loaded hosts.
> + */
> +#define MIGRATION_STATUS_WAIT_TIMEOUT 120
> +
> +/*
> + * Wait for a "MIGRATION" event.  This is what Libvirt uses to track
> + * migration status changes.
> + */
> +void migration_event_wait(QTestState *s, const char *target)
> +{
> +    QDict *response, *data;
> +    const char *status;
> +    bool found;
> +
> +    do {
> +        response = qtest_qmp_eventwait_ref(s, "MIGRATION");
> +        data = qdict_get_qdict(response, "data");
> +        g_assert(data);
> +        status = qdict_get_str(data, "status");
> +        found = (strcmp(status, target) == 0);
> +        qobject_unref(response);
> +    } while (!found);
> +}
> +
> +void migrate_qmp_fail(QTestState *who, const char *uri,
> +                      const char *channels, const char *fmt, ...)
> +{
> +    va_list ap;
> +    QDict *args, *err;
> +
> +    va_start(ap, fmt);
> +    args = qdict_from_vjsonf_nofail(fmt, ap);
> +    va_end(ap);
> +
> +    g_assert(!qdict_haskey(args, "uri"));
> +    if (uri) {
> +        qdict_put_str(args, "uri", uri);
> +    }
> +
> +    g_assert(!qdict_haskey(args, "channels"));
> +    if (channels) {
> +        QObject *channels_obj = qobject_from_json(channels, &error_abort);
> +        qdict_put_obj(args, "channels", channels_obj);
> +    }
> +
> +    err = qtest_qmp_assert_failure_ref(
> +        who, "{ 'execute': 'migrate', 'arguments': %p}", args);
> +
> +    g_assert(qdict_haskey(err, "desc"));
> +
> +    qobject_unref(err);
> +}
> +
> +/*
> + * Send QMP command "migrate".
> + * Arguments are built from @fmt... (formatted like
> + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in.
> + */
> +void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
> +                 const char *channels, const char *fmt, ...)
> +{
> +    va_list ap;
> +    QDict *args;
> +    g_autofree char *connect_uri = NULL;
> +
> +    va_start(ap, fmt);
> +    args = qdict_from_vjsonf_nofail(fmt, ap);
> +    va_end(ap);
> +
> +    g_assert(!qdict_haskey(args, "uri"));
> +    if (uri) {
> +        qdict_put_str(args, "uri", uri);
> +    } else if (!channels) {
> +        connect_uri = migrate_get_connect_uri(to);
> +        qdict_put_str(args, "uri", connect_uri);
> +    }
> +
> +    g_assert(!qdict_haskey(args, "channels"));
> +    if (channels) {
> +        QObject *channels_obj = qobject_from_json(channels, &error_abort);
> +        QList *channel_list = qobject_to(QList, channels_obj);
> +        migrate_set_ports(to, channel_list);
> +        qdict_put_obj(args, "channels", channels_obj);
> +    }
> +
> +    qtest_qmp_assert_success(who,
> +                             "{ 'execute': 'migrate', 'arguments': %p}", args);
> +}
> +
> +void migrate_set_capability(QTestState *who, const char *capability,
> +                            bool value)
> +{
> +    qtest_qmp_assert_success(who,
> +                             "{ 'execute': 'migrate-set-capabilities',"
> +                             "'arguments': { "
> +                             "'capabilities': [ { "
> +                             "'capability': %s, 'state': %i } ] } }",
> +                             capability, value);
> +}
> +
> +void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...)
> +{
> +    va_list ap;
> +    QDict *args, *rsp;
> +
> +    va_start(ap, fmt);
> +    args = qdict_from_vjsonf_nofail(fmt, ap);
> +    va_end(ap);
> +
> +    g_assert(!qdict_haskey(args, "uri"));
> +    qdict_put_str(args, "uri", uri);
> +
> +    /* This function relies on the event to work, make sure it's enabled */
> +    migrate_set_capability(to, "events", true);
> +
> +    rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}",
> +                    args);
> +
> +    if (!qdict_haskey(rsp, "return")) {
> +        g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true);
> +        g_test_message("%s", s->str);
> +    }
> +
> +    g_assert(qdict_haskey(rsp, "return"));
> +    qobject_unref(rsp);
> +
> +    migration_event_wait(to, "setup");
> +}
> +
> +static bool check_migration_status(QTestState *who, const char *goal,
> +                                   const char **ungoals)
> +{
> +    bool ready;
> +    char *current_status;
> +    const char **ungoal;
> +
> +    current_status = migrate_query_status(who);
> +    ready = strcmp(current_status, goal) == 0;
> +    if (!ungoals) {
> +        g_assert_cmpstr(current_status, !=, "failed");
> +        /*
> +         * If looking for a state other than completed,
> +         * completion of migration would cause the test to
> +         * hang.
> +         */
> +        if (strcmp(goal, "completed") != 0) {
> +            g_assert_cmpstr(current_status, !=, "completed");
> +        }
> +    } else {
> +        for (ungoal = ungoals; *ungoal; ungoal++) {
> +            g_assert_cmpstr(current_status, !=,  *ungoal);
> +        }
> +    }
> +    g_free(current_status);
> +    return ready;
> +}
> +
> +void wait_for_migration_status(QTestState *who,
> +                               const char *goal, const char **ungoals)
> +{
> +    g_test_timer_start();
> +    while (!check_migration_status(who, goal, ungoals)) {
> +        usleep(1000);
> +
> +        g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
> +    }
> +}
> +
> +void wait_for_migration_complete(QTestState *who)
> +{
> +    wait_for_migration_status(who, "completed", NULL);
> +}
> +
> +void wait_for_migration_fail(QTestState *from, bool allow_active)
> +{
> +    g_test_timer_start();
> +    QDict *rsp_return;
> +    char *status;
> +    bool failed;
> +
> +    do {
> +        status = migrate_query_status(from);
> +        bool result = !strcmp(status, "setup") || !strcmp(status, "failed") ||
> +            (allow_active && !strcmp(status, "active"));
> +        if (!result) {
> +            fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n",
> +                    __func__, status, allow_active);
> +        }
> +        g_assert(result);
> +        failed = !strcmp(status, "failed");
> +        g_free(status);
> +
> +        g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT);
> +    } while (!failed);
> +
> +    /* Is the machine currently running? */
> +    rsp_return = qtest_qmp_assert_success_ref(from,
> +                                              "{ 'execute': 'query-status' }");
> +    g_assert(qdict_haskey(rsp_return, "running"));
> +    g_assert(qdict_get_bool(rsp_return, "running"));
> +    qobject_unref(rsp_return);
> +}
> +
> +void wait_for_stop(QTestState *who, QTestMigrationState *state)
> +{
> +    if (!state->stop_seen) {
> +        qtest_qmp_eventwait(who, "STOP");
> +    }
> +}
> +
> +void wait_for_resume(QTestState *who, QTestMigrationState *state)
> +{
> +    if (!state->resume_seen) {
> +        qtest_qmp_eventwait(who, "RESUME");
> +    }
> +}
> +
> +void wait_for_suspend(QTestState *who, QTestMigrationState *state)
> +{
> +    if (state->suspend_me && !state->suspend_seen) {
> +        qtest_qmp_eventwait(who, "SUSPEND");
> +    }
> +}
> +
> +/*
> + * Note: caller is responsible to free the returned object via
> + * qobject_unref() after use
> + */
> +QDict *migrate_query(QTestState *who)
> +{
> +    return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }");
> +}
> +
> +QDict *migrate_query_not_failed(QTestState *who)
> +{
> +    const char *status;
> +    QDict *rsp = migrate_query(who);
> +    status = qdict_get_str(rsp, "status");
> +    if (g_str_equal(status, "failed")) {
> +        g_printerr("query-migrate shows failed migration: %s\n",
> +                   qdict_get_str(rsp, "error-desc"));
> +    }
> +    g_assert(!g_str_equal(status, "failed"));
> +    return rsp;
> +}
> +
> +/*
> + * Note: caller is responsible to free the returned object via
> + * g_free() after use
> + */
> +gchar *migrate_query_status(QTestState *who)
> +{
> +    QDict *rsp_return = migrate_query(who);
> +    gchar *status = g_strdup(qdict_get_str(rsp_return, "status"));
> +
> +    g_assert(status);
> +    qobject_unref(rsp_return);
> +
> +    return status;
> +}
> +
> +int64_t read_ram_property_int(QTestState *who, const char *property)
> +{
> +    QDict *rsp_return, *rsp_ram;
> +    int64_t result;
> +
> +    rsp_return = migrate_query_not_failed(who);
> +    if (!qdict_haskey(rsp_return, "ram")) {
> +        /* Still in setup */
> +        result = 0;
> +    } else {
> +        rsp_ram = qdict_get_qdict(rsp_return, "ram");
> +        result = qdict_get_try_int(rsp_ram, property, 0);
> +    }
> +    qobject_unref(rsp_return);
> +    return result;
> +}
> +
> +int64_t read_migrate_property_int(QTestState *who, const char *property)
> +{
> +    QDict *rsp_return;
> +    int64_t result;
> +
> +    rsp_return = migrate_query_not_failed(who);
> +    result = qdict_get_try_int(rsp_return, property, 0);
> +    qobject_unref(rsp_return);
> +    return result;
> +}
> +
> +uint64_t get_migration_pass(QTestState *who)
> +{
> +    return read_ram_property_int(who, "dirty-sync-count");
> +}
> +
> +void read_blocktime(QTestState *who)
> +{
> +    QDict *rsp_return;
> +
> +    rsp_return = migrate_query_not_failed(who);
> +    g_assert(qdict_haskey(rsp_return, "postcopy-blocktime"));
> +    qobject_unref(rsp_return);
> +}
> +
> +/*
> + * Wait for two changes in the migration pass count, but bail if we stop.
> + */
> +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state)
> +{
> +    uint64_t pass, prev_pass = 0, changes = 0;
> +
> +    while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) {
> +        usleep(1000);
> +        pass = get_migration_pass(who);
> +        changes += (pass != prev_pass);
> +        prev_pass = pass;
> +    }
> +}
> +
> +static long long migrate_get_parameter_int(QTestState *who,
> +                                           const char *parameter)
> +{
> +    QDict *rsp;
> +    long long result;
> +
> +    rsp = qtest_qmp_assert_success_ref(
> +        who, "{ 'execute': 'query-migrate-parameters' }");
> +    result = qdict_get_int(rsp, parameter);
> +    qobject_unref(rsp);
> +    return result;
> +}
> +
> +static void migrate_check_parameter_int(QTestState *who, const char *parameter,
> +                                        long long value)
> +{
> +    long long result;
> +
> +    result = migrate_get_parameter_int(who, parameter);
> +    g_assert_cmpint(result, ==, value);
> +}
> +
> +void migrate_set_parameter_int(QTestState *who, const char *parameter,
> +                               long long value)
> +{
> +    qtest_qmp_assert_success(who,
> +                             "{ 'execute': 'migrate-set-parameters',"
> +                             "'arguments': { %s: %lld } }",
> +                             parameter, value);
> +    migrate_check_parameter_int(who, parameter, value);
> +}
> +
> +static char *migrate_get_parameter_str(QTestState *who, const char *parameter)
> +{
> +    QDict *rsp;
> +    char *result;
> +
> +    rsp = qtest_qmp_assert_success_ref(
> +        who, "{ 'execute': 'query-migrate-parameters' }");
> +    result = g_strdup(qdict_get_str(rsp, parameter));
> +    qobject_unref(rsp);
> +    return result;
> +}
> +
> +static void migrate_check_parameter_str(QTestState *who, const char *parameter,
> +                                        const char *value)
> +{
> +    g_autofree char *result = migrate_get_parameter_str(who, parameter);
> +    g_assert_cmpstr(result, ==, value);
> +}
> +
> +void migrate_set_parameter_str(QTestState *who, const char *parameter,
> +                               const char *value)
> +{
> +    qtest_qmp_assert_success(who,
> +                             "{ 'execute': 'migrate-set-parameters',"
> +                             "'arguments': { %s: %s } }",
> +                             parameter, value);
> +    migrate_check_parameter_str(who, parameter, value);
> +}
> +
> +static long long migrate_get_parameter_bool(QTestState *who,
> +                                            const char *parameter)
> +{
> +    QDict *rsp;
> +    int result;
> +
> +    rsp = qtest_qmp_assert_success_ref(
> +        who, "{ 'execute': 'query-migrate-parameters' }");
> +    result = qdict_get_bool(rsp, parameter);
> +    qobject_unref(rsp);
> +    return !!result;
> +}
> +
> +static void migrate_check_parameter_bool(QTestState *who, const char *parameter,
> +                                         int value)
> +{
> +    int result;
> +
> +    result = migrate_get_parameter_bool(who, parameter);
> +    g_assert_cmpint(result, ==, value);
> +}
> +
> +void migrate_set_parameter_bool(QTestState *who, const char *parameter,
> +                                int value)
> +{
> +    qtest_qmp_assert_success(who,
> +                             "{ 'execute': 'migrate-set-parameters',"
> +                             "'arguments': { %s: %i } }",
> +                             parameter, value);
> +    migrate_check_parameter_bool(who, parameter, value);
> +}
> +
> +void migrate_ensure_non_converge(QTestState *who)
> +{
> +    /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */
> +    migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000);
> +    migrate_set_parameter_int(who, "downtime-limit", 1);
> +}
> +
> +void migrate_ensure_converge(QTestState *who)
> +{
> +    /* Should converge with 30s downtime + 1 gbs bandwidth limit */
> +    migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000);
> +    migrate_set_parameter_int(who, "downtime-limit", 30 * 1000);
> +}
> +
> +void migrate_pause(QTestState *who)
> +{
> +    qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }");
> +}
> +
> +void migrate_continue(QTestState *who, const char *state)
> +{
> +    qtest_qmp_assert_success(who,
> +                             "{ 'execute': 'migrate-continue',"
> +                             "  'arguments': { 'state': %s } }",
> +                             state);
> +}
> +
> +void migrate_recover(QTestState *who, const char *uri)
> +{
> +    qtest_qmp_assert_success(who,
> +                             "{ 'execute': 'migrate-recover', "
> +                             "  'id': 'recover-cmd', "
> +                             "  'arguments': { 'uri': %s } }",
> +                             uri);
> +}
> +
> +void migrate_cancel(QTestState *who)
> +{
> +    qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }");
> +}
> +
> +void migrate_postcopy_start(QTestState *from, QTestState *to,
> +                            QTestMigrationState *src_state)
> +{
> +    qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }");
> +
> +    wait_for_stop(from, src_state);
> +    qtest_qmp_eventwait(to, "RESUME");
> +}
> diff --git a/tests/qtest/migration/migration-util.h b/tests/qtest/migration/migration-util.h
> new file mode 100644
> index 0000000000..e73fa5a70c
> --- /dev/null
> +++ b/tests/qtest/migration/migration-util.h
> @@ -0,0 +1,45 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +#ifndef MIGRATION_UTIL_H
> +#define MIGRATION_UTIL_H
> +
> +/* migration-qmp.c */
> +G_GNUC_PRINTF(4, 5)
> +void migrate_qmp_fail(QTestState *who, const char *uri,
> +                      const char *channels, const char *fmt, ...);
> +
> +G_GNUC_PRINTF(5, 6)
> +void migrate_qmp(QTestState *who, QTestState *to, const char *uri,
> +                 const char *channels, const char *fmt, ...);
> +
> +G_GNUC_PRINTF(3, 4)
> +void migrate_incoming_qmp(QTestState *who, const char *uri,
> +                          const char *fmt, ...);
> +
> +void migration_event_wait(QTestState *s, const char *target);
> +void migrate_set_capability(QTestState *who, const char *capability,
> +                            bool value);
> +int64_t read_ram_property_int(QTestState *who, const char *property);
> +void migrate_set_parameter_int(QTestState *who, const char *parameter,
> +                               long long value);
> +void wait_for_stop(QTestState *who, QTestMigrationState *state);
> +void wait_for_resume(QTestState *who, QTestMigrationState *state);
> +void wait_for_suspend(QTestState *who, QTestMigrationState *state);
> +gchar *migrate_query_status(QTestState *who);
> +int64_t read_migrate_property_int(QTestState *who, const char *property);
> +uint64_t get_migration_pass(QTestState *who);
> +void read_blocktime(QTestState *who);
> +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state);
> +void migrate_set_parameter_str(QTestState *who, const char *parameter,
> +                               const char *value);
> +void migrate_set_parameter_bool(QTestState *who, const char *parameter,
> +                                int value);
> +void migrate_ensure_non_converge(QTestState *who);
> +void migrate_ensure_converge(QTestState *who);
> +void migrate_pause(QTestState *who);
> +void migrate_continue(QTestState *who, const char *state);
> +void migrate_recover(QTestState *who, const char *uri);
> +void migrate_cancel(QTestState *who);
> +void migrate_postcopy_start(QTestState *from, QTestState *to,
> +                            QTestMigrationState *src_state);
> +
> +#endif /* MIGRATION_UTIL_H */
> diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c
> index 73dfabc272..a1c2c70c3a 100644
> --- a/tests/qtest/virtio-net-failover.c
> +++ b/tests/qtest/virtio-net-failover.c
> @@ -12,6 +12,7 @@
>  #include "libqos/pci.h"
>  #include "libqos/pci-pc.h"
>  #include "migration-helpers.h"
> +#include "migration/migration-util.h"
>  #include "qapi/qmp/qdict.h"
>  #include "qapi/qmp/qlist.h"
>  #include "qapi/qmp/qjson.h"
> -- 
> 2.35.3
> 

With regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|



  reply	other threads:[~2024-11-06 10:54 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-05 18:08 [PATCH 00/22] tests/qtest: migration-test refactoring Fabiano Rosas
2024-11-05 18:08 ` [PATCH 01/22] tests/qtest/migration: Fix indentations Fabiano Rosas
2024-11-06  9:24   ` Daniel P. Berrangé
2024-11-05 18:08 ` [PATCH 02/22] tests/qtest/migration: Standardize hook names Fabiano Rosas
2024-11-05 18:08 ` [PATCH 03/22] tests/qtest/migration: Stop calling everything "test" Fabiano Rosas
2024-11-06  9:29   ` Daniel P. Berrangé
2024-11-06 12:12     ` Fabiano Rosas
2024-11-05 18:08 ` [PATCH 04/22] tests/migration: Disambiguate guestperf vs. a-b Fabiano Rosas
2024-11-06  9:24   ` Daniel P. Berrangé
2024-11-06 12:11     ` Fabiano Rosas
2024-11-05 18:08 ` [PATCH 05/22] tests/qtest/migration: Move bootfile code to its own file Fabiano Rosas
2024-11-06 10:48   ` Daniel P. Berrangé
2024-11-05 18:08 ` [PATCH 06/22] tests/qtest/migration: Move qmp helpers to a separate file Fabiano Rosas
2024-11-06 10:52   ` Daniel P. Berrangé [this message]
2024-11-06 12:13     ` Fabiano Rosas
2024-11-05 18:08 ` [PATCH 07/22] tests/qtest/migration: Rename migration-helpers.c Fabiano Rosas
2024-11-05 18:08 ` [PATCH 08/22] tests/qtest/migration: Move ufd_version_check to utils Fabiano Rosas
2024-11-06 11:00   ` Daniel P. Berrangé
2024-11-05 18:08 ` [PATCH 09/22] tests/qtest/migration: Move kvm_dirty_ring_supported " Fabiano Rosas
2024-11-05 18:08 ` [PATCH 10/22] tests/qtest/migration: Isolate test initialization Fabiano Rosas
2024-11-06 11:02   ` Daniel P. Berrangé
2024-11-05 18:08 ` [PATCH 11/22] tests/qtest/migration: Move common test code Fabiano Rosas
2024-11-06 11:05   ` Daniel P. Berrangé
2024-11-06 12:26     ` Fabiano Rosas
2024-11-06 12:33       ` Daniel P. Berrangé
2024-11-05 18:08 ` [PATCH 12/22] tests/qtest/migration: Split TLS tests from migration-test.c Fabiano Rosas
2024-11-06 11:15   ` Daniel P. Berrangé
2024-11-06 13:05     ` Fabiano Rosas
2024-11-06 13:13       ` Daniel P. Berrangé
2024-11-05 18:08 ` [PATCH 13/22] tests/qtest/migration: Split compression " Fabiano Rosas
2024-11-05 18:08 ` [PATCH 14/22] tests/qtest/migration: Split postcopy tests Fabiano Rosas
2024-11-05 18:08 ` [PATCH 15/22] tests/qtest/migration: Split file tests Fabiano Rosas
2024-11-05 18:08 ` [PATCH 16/22] tests/qtest/migration: Split precopy tests Fabiano Rosas
2024-11-05 18:08 ` [PATCH 17/22] tests/qtest/migration: Split CPR tests Fabiano Rosas
2024-11-05 18:08 ` [PATCH 18/22] tests/qtest/migration: Split validation tests + misc Fabiano Rosas
2024-11-05 18:08 ` [PATCH 19/22] tests/qtest/migration: Add migration-test-smoke Fabiano Rosas
2024-11-05 18:08 ` [PATCH 20/22] tests/qtest/migration: Pick smoke tests Fabiano Rosas
2024-11-05 18:08 ` [PATCH 21/22] tests/qtest: Add support for check-qtest-<subsystem> Fabiano Rosas
2024-11-06 11:11   ` Daniel P. Berrangé
2024-11-06 12:40     ` Fabiano Rosas
2024-11-06 12:42       ` Daniel P. Berrangé
2024-11-05 18:08 ` [PATCH 22/22] docs: Add migration tests documentation Fabiano Rosas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=ZytKfYirP-R1mRNV@redhat.com \
    --to=berrange@redhat.com \
    --cc=alex.bennee@linaro.org \
    --cc=farosas@suse.de \
    --cc=lvivier@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=peterx@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.