qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [RFC PATCH 1/1] qtest/migration: Support more than one QEMU binary


From: Philippe Mathieu-Daudé
Subject: Re: [RFC PATCH 1/1] qtest/migration: Support more than one QEMU binary
Date: Tue, 3 Oct 2023 17:24:50 +0200
User-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Thunderbird/102.15.1

Hi Fabiano,

[+Alex & Daniel]

On 3/10/23 16:19, Fabiano Rosas wrote:
We have strict rules around migration compatibility between different
QEMU versions but not a single test that validates the migration state
between different binaries.

Add some infrastructure to allow running the migration tests with two
different QEMU binaries as migration source and destination.

The code now recognizes two new environment variables QTEST_QEMU_SRC
and QTEST_QEMU_DST. Only one of the two is expected to be used along
with the existing QTEST_QEMU_BINARY, which will automatically be used
for the other side of migration.

Usage:
QTEST_QEMU_DST=./build-8.2.0/qemu-system-x86_64 \
QTEST_QEMU_BINARY=../build-8.1.0/qemu-system-x86_64 \
./tests/qtest/migration-test

I like it as a first step, but I'd rather run $QTEST_QEMU_SRC
directly from a docker image, i.e.:

$ docker run -it opensuse/leap
# zypper update -y && zypper install -y qemu-x86
$ docker run opensuse/leap qemu-system-x86_64 ...

This code also works for when debugging with GDB to pass the same
binary, but different GDB options for src and dst.

Signed-off-by: Fabiano Rosas <farosas@suse.de>
---
  tests/qtest/migration-helpers.c | 168 ++++++++++++++++++++++++++++++++
  tests/qtest/migration-helpers.h |   3 +
  tests/qtest/migration-test.c    |  52 ++++++++--
  3 files changed, 216 insertions(+), 7 deletions(-)

diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c
index be00c52d00..e84360c3b3 100644
--- a/tests/qtest/migration-helpers.c
+++ b/tests/qtest/migration-helpers.c
@@ -12,6 +12,8 @@
#include "qemu/osdep.h"
  #include "qapi/qmp/qjson.h"
+#include "qapi/qmp/qlist.h"
+#include "qapi/qmp/qstring.h"
#include "migration-helpers.h" @@ -180,3 +182,169 @@ void wait_for_migration_fail(QTestState *from, bool allow_active)
      g_assert(qdict_get_bool(rsp_return, "running"));
      qobject_unref(rsp_return);
  }
+
+static char *query_pkg_version(QTestState *who)
+{
+    QDict *rsp;
+    char *pkg;
+
+    rsp = qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-version' }");
+    g_assert(rsp);
+
+    pkg = g_strdup(qdict_get_str(rsp, "package"));
+    qobject_unref(rsp);
+
+    return pkg;
+}
+
+static QList *query_machines(void)
+{
+    QDict *response;
+    QList *list;
+    QTestState *qts;
+
+    qts = qtest_init("-machine none");
+    response = qtest_qmp(qts, "{ 'execute': 'query-machines' }");
+    g_assert(response);
+    list = qdict_get_qlist(response, "return");
+    g_assert(list);
+
+    qtest_quit(qts);
+    return list;
+}
+
+static char *get_default_machine(QList *list)
+{
+    QDict *info;
+    QListEntry *entry;
+    QString *qstr;
+    char *name = NULL;
+
+    QLIST_FOREACH_ENTRY(list, entry) {
+        info = qobject_to(QDict, qlist_entry_obj(entry));
+        g_assert(info);
+
+        if (qdict_get(info, "is-default")) {
+            qstr = qobject_to(QString, qdict_get(info, "name"));
+            g_assert(qstr);
+            name = g_strdup(qstring_get_str(qstr));
+            break;
+        }
+    }
+
+    g_assert(name);
+    return name;
+}
+
+static bool search_default_machine(QList *list, const char *theirs)
+{
+    QDict *info;
+    QListEntry *entry;
+    QString *qstr;
+
+    if (!theirs) {
+        return false;
+    }
+
+    QLIST_FOREACH_ENTRY(list, entry) {
+        info = qobject_to(QDict, qlist_entry_obj(entry));
+        g_assert(info);
+
+        qstr = qobject_to(QString, qdict_get(info, "name"));
+        g_assert(qstr);
+
+        if (g_str_equal(qstring_get_str(qstr), theirs)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * We need to ensure that both QEMU instances set via the QTEST_QEMU_*
+ * vars will use the same machine type. Use a custom query_machines
+ * function because the generic one in libqtest has a cache that would
+ * return the same machines for both binaries.
+ */
+char *find_common_machine_type(const char *bin)
+{
+    QList *m1, *m2;
+    g_autofree char *def1 = NULL;
+    g_autofree char *def2 = NULL;
+    const char *qemu_bin = getenv("QTEST_QEMU_BINARY");
+
+    m1 = query_machines();
+
+    g_setenv("QTEST_QEMU_BINARY", bin, true);
+    m2 = query_machines();
+    g_setenv("QTEST_QEMU_BINARY", qemu_bin, true);
+
+    def1 = get_default_machine(m1);
+    def2 = get_default_machine(m2);
+
+    if (g_str_equal(def1, def2)) {
+        /* either can be used */
+        return g_strdup(def1);
+    }
+
+    if (search_default_machine(m1, def2)) {
+        return g_strdup(def2);
+    }
+
+    if (search_default_machine(m2, def1)) {
+        return g_strdup(def1);
+    }
+
+    g_assert_not_reached();
+}
+
+/*
+ * Init a guest for migration tests using an alternate QEMU binary for
+ * either the source or destination, depending on @var. The other
+ * binary should be set as usual via QTEST_QEMU_BINARY.
+ *
+ * Expected values:
+ *   QTEST_QEMU_SRC
+ *   QTEST_QEMU_DST
+ *
+ * Warning: The generic parts of qtest could be using
+ * QTEST_QEMU_BINARY to query for properties before we reach the
+ * migration code. If the alternate binary is too dissimilar that
+ * could cause issues.
+ */
+static QTestState *init_vm(const char *extra_args, const char *var)
+{
+    const char *alt_bin = getenv(var);
+    const char *qemu_bin = getenv("QTEST_QEMU_BINARY");
+    g_autofree char *pkg = NULL;
+    bool src = !!strstr(var, "SRC");
+    QTestState *qts;
+
+    if (alt_bin) {
+        g_setenv("QTEST_QEMU_BINARY", alt_bin, true);
+    }
+
+    qts = qtest_init(extra_args);
+    pkg = query_pkg_version(qts);
+
+    g_test_message("Using %s (%s) as migration %s",
+                   alt_bin ? alt_bin : qemu_bin,
+                   pkg,
+                   src ? "source" : "destination");
+
+    if (alt_bin) {
+        /* restore the original */
+        g_setenv("QTEST_QEMU_BINARY", qemu_bin, true);
+    }
+    return qts;
+}
+
+QTestState *mig_init_src(const char *extra_args)
+{
+    return init_vm(extra_args, "QTEST_QEMU_SRC");
+}
+
+QTestState *mig_init_dst(const char *extra_args)
+{
+    return init_vm(extra_args, "QTEST_QEMU_DST");
+}
diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration-helpers.h
index 009e250e90..aabdbc7507 100644
--- a/tests/qtest/migration-helpers.h
+++ b/tests/qtest/migration-helpers.h
@@ -33,4 +33,7 @@ void wait_for_migration_complete(QTestState *who);
void wait_for_migration_fail(QTestState *from, bool allow_active); +QTestState *mig_init_src(const char *extra_args);
+QTestState *mig_init_dst(const char *extra_args);
+char *find_common_machine_type(const char *bin);
  #endif /* MIGRATION_HELPERS_H */
diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c
index 1b43df5ca7..60f0b15417 100644
--- a/tests/qtest/migration-test.c
+++ b/tests/qtest/migration-test.c
@@ -45,6 +45,7 @@ unsigned end_address;
  static bool uffd_feature_thread_id;
  static bool got_src_stop;
  static bool got_dst_resume;
+static char *common_machine_type;
/*
   * An initial 3 MB offset is used as that corresponds
@@ -712,6 +713,7 @@ static int test_migrate_start(QTestState **from, QTestState 
**to,
      g_autofree char *shmem_path = NULL;
      const char *arch = qtest_get_arch();
      const char *memory_size;
+    g_autofree char *machine = NULL;
if (args->use_shmem) {
          if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) {
@@ -723,18 +725,30 @@ static int test_migrate_start(QTestState **from, 
QTestState **to,
      got_src_stop = false;
      got_dst_resume = false;
      bootpath = g_strdup_printf("%s/bootsect", tmpfs);
+
      if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) {
          /* the assembled x86 boot sector should be exactly one sector large */
          assert(sizeof(x86_bootsect) == 512);
          init_bootfile(bootpath, x86_bootsect, sizeof(x86_bootsect));
          memory_size = "150M";
-        arch_opts = g_strdup_printf("-drive file=%s,format=raw", bootpath);
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("-machine %s", common_machine_type);
+        }
+
+        arch_opts = g_strdup_printf("%s -drive file=%s,format=raw",
+                                    machine, bootpath);
          start_address = X86_TEST_MEM_START;
          end_address = X86_TEST_MEM_END;
      } else if (g_str_equal(arch, "s390x")) {
          init_bootfile(bootpath, s390x_elf, sizeof(s390x_elf));
          memory_size = "128M";
-        arch_opts = g_strdup_printf("-bios %s", bootpath);
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("-machine %s", common_machine_type);
+        }
+
+        arch_opts = g_strdup_printf("%s -bios %s", machine, bootpath);
          start_address = S390_TEST_MEM_START;
          end_address = S390_TEST_MEM_END;
      } else if (strcmp(arch, "ppc64") == 0) {
@@ -745,12 +759,24 @@ static int test_migrate_start(QTestState **from, 
QTestState **to,
                                        "'nvramrc=hex .\" _\" begin %x %x "
                                        "do i c@ 1 + i c! 1000 +loop .\" B\" 0 "
                                        "until'", end_address, start_address);
-        arch_opts = g_strdup("-nodefaults -machine vsmt=8");
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("%s,", common_machine_type);
+        }
+
+        arch_opts = g_strdup_printf("-nodefaults -machine %svsmt=8", machine);
      } else if (strcmp(arch, "aarch64") == 0) {
          init_bootfile(bootpath, aarch64_kernel, sizeof(aarch64_kernel));
          memory_size = "150M";
-        arch_opts = g_strdup_printf("-machine virt,gic-version=max -cpu max "
-                                    "-kernel %s", bootpath);
+
+        if (common_machine_type) {
+            machine = g_strdup_printf("%s", common_machine_type);
+        } else {
+            machine = g_strdup("virt");
+        }
+
+        arch_opts = g_strdup_printf("-machine %s,gic-version=max -cpu max "
+                                    "-kernel %s", machine, bootpath);
          start_address = ARM_TEST_MEM_START;
          end_address = ARM_TEST_MEM_END;
@@ -799,7 +825,7 @@ static int test_migrate_start(QTestState **from, QTestState **to,
                                   args->opts_source ? args->opts_source : "",
                                   ignore_stderr);
      if (!args->only_target) {
-        *from = qtest_init(cmd_source);
+        *from = mig_init_src(cmd_source);
          qtest_qmp_set_event_callback(*from,
                                       migrate_watch_for_stop,
                                       &got_src_stop);
@@ -819,7 +845,7 @@ static int test_migrate_start(QTestState **from, QTestState 
**to,
                                   shmem_opts,
                                   args->opts_target ? args->opts_target : "",
                                   ignore_stderr);
-    *to = qtest_init(cmd_target);
+    *to = mig_init_dst(cmd_target);
      qtest_qmp_set_event_callback(*to,
                                   migrate_watch_for_resume,
                                   &got_dst_resume);
@@ -2769,6 +2795,8 @@ int main(int argc, char **argv)
      const char *arch;
      g_autoptr(GError) err = NULL;
      int ret;
+    const char *qemu_src = getenv("QTEST_QEMU_SRC");
+    const char *qemu_dst = getenv("QTEST_QEMU_DST");
g_test_init(&argc, &argv, NULL); @@ -2780,6 +2808,16 @@ int main(int argc, char **argv)
          return 0;
      }
+ if (qemu_src || qemu_dst) {
+        if (qemu_src && qemu_dst) {
+            g_test_message("Only one of QTEST_QEMU_SRC, QTEST_QEMU_DST is 
allowed.");
+            exit(1);
+        }
+        common_machine_type = find_common_machine_type(qemu_src ? qemu_src : 
qemu_dst);
+        g_test_message("Using two different QEMU binaries. Common machine type: 
%s",
+                       common_machine_type);
+    }
+
      has_uffd = ufd_version_check();
      arch = qtest_get_arch();




reply via email to

[Prev in Thread] Current Thread [Next in Thread]