Patchwork [v2,3/3] qtest: add migrate-test

login
register
mail settings
Submitter Jason Baron
Date Dec. 20, 2012, 5:14 p.m.
Message ID <7a022db1073fdf4759944b0e73f02456315464ca.1356022464.git.jbaron@redhat.com>
Download mbox | patch
Permalink /patch/207688/
State New
Headers show

Comments

Jason Baron - Dec. 20, 2012, 5:14 p.m.
From: Jason Baron <jbaron@redhat.com>

Tests a single 'pc' machine migration on the same host.

Would be nice to extend the test matrix to various machine versions, but that
requires building multiple qemu binaries, which is a bit awkward in the
context of qtest. Testing migration between different machine versions with the
same binary doesn't seem too useful.

Signed-off-by: Jason Baron <jbaron@redhat.com>
---
 tests/Makefile       |    2 +
 tests/libqtest.c     |   98 ++++++++++++++++++++++++++++++++++++++++++++++++-
 tests/libqtest.h     |   15 +++++++-
 tests/migrate-test.c |   68 ++++++++++++++++++++++++++++++++++
 4 files changed, 179 insertions(+), 4 deletions(-)
 create mode 100644 tests/migrate-test.c
Blue Swirl - Dec. 20, 2012, 8:17 p.m.
On Thu, Dec 20, 2012 at 5:14 PM, Jason Baron <jbaron@redhat.com> wrote:
> From: Jason Baron <jbaron@redhat.com>
>
> Tests a single 'pc' machine migration on the same host.
>
> Would be nice to extend the test matrix to various machine versions, but that
> requires building multiple qemu binaries, which is a bit awkward in the
> context of qtest. Testing migration between different machine versions with the
> same binary doesn't seem too useful.

We could test migration between TCG, Xen and KVM if two of those are available.

>
> Signed-off-by: Jason Baron <jbaron@redhat.com>
> ---
>  tests/Makefile       |    2 +
>  tests/libqtest.c     |   98 ++++++++++++++++++++++++++++++++++++++++++++++++-
>  tests/libqtest.h     |   15 +++++++-
>  tests/migrate-test.c |   68 ++++++++++++++++++++++++++++++++++
>  4 files changed, 179 insertions(+), 4 deletions(-)
>  create mode 100644 tests/migrate-test.c
>
> diff --git a/tests/Makefile b/tests/Makefile
> index 1756b47..3f0c5a2 100644
> --- a/tests/Makefile
> +++ b/tests/Makefile
> @@ -25,6 +25,7 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
>  check-qtest-i386-y = tests/fdc-test$(EXESUF)
>  check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
>  check-qtest-i386-y += tests/rtc-test$(EXESUF)
> +check-qtest-i386-y += tests/migrate-test$(EXESUF)
>  check-qtest-x86_64-y = $(check-qtest-i386-y)
>  check-qtest-sparc-y = tests/m48t59-test$(EXESUF)
>  check-qtest-sparc64-y = tests/m48t59-test$(EXESUF)
> @@ -78,6 +79,7 @@ tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y)
>  tests/m48t59-test$(EXESUF): tests/m48t59-test.o $(trace-obj-y)
>  tests/fdc-test$(EXESUF): tests/fdc-test.o tests/libqtest.o $(trace-obj-y)
>  tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/libqtest.o $(trace-obj-y)
> +tests/migrate-test$(EXESUF): tests/migrate-test.o
>
>  # QTest rules
>
> diff --git a/tests/libqtest.c b/tests/libqtest.c
> index 994cd2f..28cbea9 100644
> --- a/tests/libqtest.c
> +++ b/tests/libqtest.c
> @@ -28,6 +28,9 @@
>
>  #include "compiler.h"
>  #include "osdep.h"
> +#include "qjson.h"
> +#include "qdict.h"
> +#include "qbool.h"
>
>  #define MAX_IRQ 256
>
> @@ -42,6 +45,8 @@ struct QTestState
>      gchar *pid_file;
>      char *socket_path, *qmp_socket_path;
>      char *tmp_dir;
> +    /* uri to use for incoming migration */
> +    char *incoming_uri;
>  };
>
>  #define g_assert_no_errno(ret) do { \
> @@ -102,7 +107,7 @@ static pid_t qtest_qemu_pid(QTestState *s)
>      return pid;
>  }
>
> -QTestState *qtest_init(const char *extra_args)
> +QTestState *qtest_init(const char *extra_args, const char *incoming_uri)
>  {
>      QTestState *s;
>      int sock, qmpsock, ret, i;
> @@ -113,13 +118,14 @@ QTestState *qtest_init(const char *extra_args)
>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>      g_assert(qemu_binary != NULL);
>
> -    s = g_malloc(sizeof(*s));
> +    s = g_malloc0(sizeof(*s));
>
>      s->tmp_dir = g_strdup_printf("/tmp/qtest-%d-XXXXXX", getpid());
>      g_assert(mkdtemp(s->tmp_dir) != NULL);
>      s->socket_path = g_strdup_printf("%s/%s", s->tmp_dir, "sock");
>      s->qmp_socket_path = g_strdup_printf("%s/%s", s->tmp_dir, "qmp");
>      s->pid_file = g_strdup_printf("%s/%s", s->tmp_dir, "pid");
> +    s->incoming_uri = g_strdup(incoming_uri);
>
>      sock = init_socket(s->socket_path);
>      qmpsock = init_socket(s->qmp_socket_path);
> @@ -178,6 +184,7 @@ void qtest_quit(QTestState *s)
>      g_free(s->socket_path);
>      g_free(s->qmp_socket_path);
>      g_free(s->tmp_dir);
> +    g_free(s->incoming_uri);
>  }
>
>  static void socket_sendf(int fd, const char *fmt, va_list ap)
> @@ -482,3 +489,90 @@ void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
>      qtest_sendf(s, "\n");
>      qtest_rsp(s, 0);
>  }
> +
> +/**
> + * is_running:
> + * @mch: QTestState instance to check.
> + *
> + * Returns 1, if mch is running, 0 if its not running. -1 means retry.
> + */
> +static int is_running(QTestState *mch)
> +{
> +    QString *resp = qstring_new();
> +    QObject *resp_obj;
> +    QObject *ret_obj;
> +    QObject *run_obj;
> +    int ret;
> +
> +    resp = qstring_new();
> +    qtest_qmp_resp(mch, resp, "{ 'execute': 'query-status' }", NULL);
> +
> +    resp_obj = qobject_from_json(qstring_get_str(resp));
> +    if ((!resp_obj) || (qobject_type(resp_obj) != QTYPE_QDICT)) {
> +        ret = -1;
> +        goto out;
> +    }
> +
> +    ret_obj = qdict_get(qobject_to_qdict(resp_obj), "return");
> +    if ((!ret_obj) || (qobject_type(ret_obj) != QTYPE_QDICT)) {
> +        ret = -1;
> +        goto out;
> +    }
> +
> +    run_obj = qdict_get(qobject_to_qdict(ret_obj), "running");
> +    if ((!run_obj) || (qobject_type(run_obj) != QTYPE_QBOOL)) {
> +        ret = -1;
> +        goto out;
> +    }
> +    ret = qbool_get_int(qobject_to_qbool(run_obj));
> +
> +out:
> +    qobject_decref(resp_obj);
> +    QDECREF(resp);
> +    return ret;
> +}
> +
> +#define SLEEP_INTERVAL 2
> +/* Abort after 2 minutes */
> +#define SLEEP_MAX (60 * 2)
> +
> +int test_migrate(QTestState *mach_src, QTestState *mach_dst)
> +{
> +    int src_run = 0;
> +    int dst_run = 0;
> +    int iter = 0;
> +    gchar *migrate_str;
> +
> +    if (!mach_dst->incoming_uri) {
> +        fprintf(stderr, "do_migrate: Error: mach_dst->incoming_uri not set\n");
> +        return -1;
> +    }
> +
> +    /* is running on mach_src ? */
> +    if (is_running(mach_src) != 1) {
> +        fprintf(stderr, "do_migrate: Error: not running on src\n");
> +        return -1;
> +    }
> +
> +    /* do migrate */
> +    migrate_str = g_strdup_printf("{ 'execute': 'migrate',"
> +                                  "'arguments': { 'uri': '%s' } }",
> +                                  mach_dst->incoming_uri);
> +    qtest_qmp(mach_src, migrate_str, NULL);
> +    g_free(migrate_str);
> +
> +    while (iter < SLEEP_MAX) {
> +        src_run = is_running(mach_src);
> +        dst_run = is_running(mach_dst);
> +        if ((src_run == 0) && (dst_run == 1)) {
> +            break;
> +        }
> +        sleep(SLEEP_INTERVAL);
> +        iter += SLEEP_INTERVAL;
> +    }
> +    if ((src_run != 0) || (dst_run != 1)) {
> +        fprintf(stderr, "do_migrate: Error: migration failed\n");
> +        return -1;
> +    }
> +    return 0;
> +}
> diff --git a/tests/libqtest.h b/tests/libqtest.h
> index 972ba5d..47d189b 100644
> --- a/tests/libqtest.h
> +++ b/tests/libqtest.h
> @@ -27,8 +27,9 @@ extern QTestState *global_qtest;
>  /**
>   * qtest_init:
>   * @extra_args: other arguments to pass to QEMU.
> + * @incoming_uri: used for incoming migration.
>   */
> -QTestState *qtest_init(const char *extra_args);
> +QTestState *qtest_init(const char *extra_args, const char *incoming_uri);
>
>  /**
>   * qtest_quit:
> @@ -207,6 +208,16 @@ const char *qtest_get_arch(void);
>  void qtest_add_func(const char *str, void (*fn));
>
>  /**
> + * test_migrate:
> + * @mach_src: QTestState source.
> + * @mach_dst: QTestState destination.
> + *
> + * Migrate a VM from mach_src to mach_dst.
> + * Returns 0 on success, -1 on failure.
> + */
> +int test_migrate(QTestState *mach_src, QTestState *mach_dst);
> +
> +/**
>   * qtest_start:
>   * @args: other arguments to pass to QEMU
>   *
> @@ -214,7 +225,7 @@ void qtest_add_func(const char *str, void (*fn));
>   * The global variable is used by "shortcut" macros documented below.
>   */
>  #define qtest_start(args) (            \
> -    global_qtest = qtest_init((args)) \
> +    global_qtest = qtest_init((args), NULL) \
>          )
>
>  /**
> diff --git a/tests/migrate-test.c b/tests/migrate-test.c
> new file mode 100644
> index 0000000..80ef815
> --- /dev/null
> +++ b/tests/migrate-test.c
> @@ -0,0 +1,68 @@
> +/*
> + * Migration tests
> + *
> + * Copyright Red Hat, Inc. 2012
> + *
> + * Authors:
> + *  Jason Baron   <jbaron@redhat.com>
> + *
> + * 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 "libqtest.h"
> +
> +#include <glib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +
> +static QTestState *mach_src;
> +static QTestState *mach_dst;
> +
> +static void migrate_cleanup(void)
> +{
> +    if (mach_src) {
> +        qtest_quit(mach_src);
> +    }
> +    if (mach_dst) {
> +        qtest_quit(mach_dst);
> +    }
> +}
> +
> +static void test_migration(void)
> +{
> +    int ret;
> +
> +    ret = test_migrate(mach_src, mach_dst);
> +    if (ret) {
> +        migrate_cleanup();
> +        g_assert(false);
> +    }
> +}
> +
> +int main(int argc, char **argv)
> +{
> +    int ret;
> +    gchar *src_cmdline;
> +    gchar *dst_cmdline;
> +    const char *dst_uri = "tcp:0:4444";

Maybe use a random port and localhost.

> +
> +    g_test_init(&argc, &argv, NULL);
> +
> +    src_cmdline = g_strdup("-display none -machine pc");
> +    mach_src = qtest_init(src_cmdline, NULL);
> +
> +    dst_cmdline = g_strdup_printf("%s -incoming %s", src_cmdline, dst_uri);
> +    mach_dst = qtest_init(dst_cmdline, dst_uri);
> +
> +    g_free(src_cmdline);
> +    g_free(dst_cmdline);
> +
> +    qtest_add_func("/migrate/src-to-dst", test_migration);
> +    ret = g_test_run();
> +
> +    migrate_cleanup();
> +    return ret;
> +}
> --
> 1.7.1
>

Patch

diff --git a/tests/Makefile b/tests/Makefile
index 1756b47..3f0c5a2 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -25,6 +25,7 @@  check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
 check-qtest-i386-y = tests/fdc-test$(EXESUF)
 check-qtest-i386-y += tests/hd-geo-test$(EXESUF)
 check-qtest-i386-y += tests/rtc-test$(EXESUF)
+check-qtest-i386-y += tests/migrate-test$(EXESUF)
 check-qtest-x86_64-y = $(check-qtest-i386-y)
 check-qtest-sparc-y = tests/m48t59-test$(EXESUF)
 check-qtest-sparc64-y = tests/m48t59-test$(EXESUF)
@@ -78,6 +79,7 @@  tests/rtc-test$(EXESUF): tests/rtc-test.o $(trace-obj-y)
 tests/m48t59-test$(EXESUF): tests/m48t59-test.o $(trace-obj-y)
 tests/fdc-test$(EXESUF): tests/fdc-test.o tests/libqtest.o $(trace-obj-y)
 tests/hd-geo-test$(EXESUF): tests/hd-geo-test.o tests/libqtest.o $(trace-obj-y)
+tests/migrate-test$(EXESUF): tests/migrate-test.o
 
 # QTest rules
 
diff --git a/tests/libqtest.c b/tests/libqtest.c
index 994cd2f..28cbea9 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -28,6 +28,9 @@ 
 
 #include "compiler.h"
 #include "osdep.h"
+#include "qjson.h"
+#include "qdict.h"
+#include "qbool.h"
 
 #define MAX_IRQ 256
 
@@ -42,6 +45,8 @@  struct QTestState
     gchar *pid_file;
     char *socket_path, *qmp_socket_path;
     char *tmp_dir;
+    /* uri to use for incoming migration */
+    char *incoming_uri;
 };
 
 #define g_assert_no_errno(ret) do { \
@@ -102,7 +107,7 @@  static pid_t qtest_qemu_pid(QTestState *s)
     return pid;
 }
 
-QTestState *qtest_init(const char *extra_args)
+QTestState *qtest_init(const char *extra_args, const char *incoming_uri)
 {
     QTestState *s;
     int sock, qmpsock, ret, i;
@@ -113,13 +118,14 @@  QTestState *qtest_init(const char *extra_args)
     qemu_binary = getenv("QTEST_QEMU_BINARY");
     g_assert(qemu_binary != NULL);
 
-    s = g_malloc(sizeof(*s));
+    s = g_malloc0(sizeof(*s));
 
     s->tmp_dir = g_strdup_printf("/tmp/qtest-%d-XXXXXX", getpid());
     g_assert(mkdtemp(s->tmp_dir) != NULL);
     s->socket_path = g_strdup_printf("%s/%s", s->tmp_dir, "sock");
     s->qmp_socket_path = g_strdup_printf("%s/%s", s->tmp_dir, "qmp");
     s->pid_file = g_strdup_printf("%s/%s", s->tmp_dir, "pid");
+    s->incoming_uri = g_strdup(incoming_uri);
 
     sock = init_socket(s->socket_path);
     qmpsock = init_socket(s->qmp_socket_path);
@@ -178,6 +184,7 @@  void qtest_quit(QTestState *s)
     g_free(s->socket_path);
     g_free(s->qmp_socket_path);
     g_free(s->tmp_dir);
+    g_free(s->incoming_uri);
 }
 
 static void socket_sendf(int fd, const char *fmt, va_list ap)
@@ -482,3 +489,90 @@  void qtest_memwrite(QTestState *s, uint64_t addr, const void *data, size_t size)
     qtest_sendf(s, "\n");
     qtest_rsp(s, 0);
 }
+
+/**
+ * is_running:
+ * @mch: QTestState instance to check.
+ *
+ * Returns 1, if mch is running, 0 if its not running. -1 means retry.
+ */
+static int is_running(QTestState *mch)
+{
+    QString *resp = qstring_new();
+    QObject *resp_obj;
+    QObject *ret_obj;
+    QObject *run_obj;
+    int ret;
+
+    resp = qstring_new();
+    qtest_qmp_resp(mch, resp, "{ 'execute': 'query-status' }", NULL);
+
+    resp_obj = qobject_from_json(qstring_get_str(resp));
+    if ((!resp_obj) || (qobject_type(resp_obj) != QTYPE_QDICT)) {
+        ret = -1;
+        goto out;
+    }
+
+    ret_obj = qdict_get(qobject_to_qdict(resp_obj), "return");
+    if ((!ret_obj) || (qobject_type(ret_obj) != QTYPE_QDICT)) {
+        ret = -1;
+        goto out;
+    }
+
+    run_obj = qdict_get(qobject_to_qdict(ret_obj), "running");
+    if ((!run_obj) || (qobject_type(run_obj) != QTYPE_QBOOL)) {
+        ret = -1;
+        goto out;
+    }
+    ret = qbool_get_int(qobject_to_qbool(run_obj));
+
+out:
+    qobject_decref(resp_obj);
+    QDECREF(resp);
+    return ret;
+}
+
+#define SLEEP_INTERVAL 2
+/* Abort after 2 minutes */
+#define SLEEP_MAX (60 * 2)
+
+int test_migrate(QTestState *mach_src, QTestState *mach_dst)
+{
+    int src_run = 0;
+    int dst_run = 0;
+    int iter = 0;
+    gchar *migrate_str;
+
+    if (!mach_dst->incoming_uri) {
+        fprintf(stderr, "do_migrate: Error: mach_dst->incoming_uri not set\n");
+        return -1;
+    }
+
+    /* is running on mach_src ? */
+    if (is_running(mach_src) != 1) {
+        fprintf(stderr, "do_migrate: Error: not running on src\n");
+        return -1;
+    }
+
+    /* do migrate */
+    migrate_str = g_strdup_printf("{ 'execute': 'migrate',"
+                                  "'arguments': { 'uri': '%s' } }",
+                                  mach_dst->incoming_uri);
+    qtest_qmp(mach_src, migrate_str, NULL);
+    g_free(migrate_str);
+
+    while (iter < SLEEP_MAX) {
+        src_run = is_running(mach_src);
+        dst_run = is_running(mach_dst);
+        if ((src_run == 0) && (dst_run == 1)) {
+            break;
+        }
+        sleep(SLEEP_INTERVAL);
+        iter += SLEEP_INTERVAL;
+    }
+    if ((src_run != 0) || (dst_run != 1)) {
+        fprintf(stderr, "do_migrate: Error: migration failed\n");
+        return -1;
+    }
+    return 0;
+}
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 972ba5d..47d189b 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -27,8 +27,9 @@  extern QTestState *global_qtest;
 /**
  * qtest_init:
  * @extra_args: other arguments to pass to QEMU.
+ * @incoming_uri: used for incoming migration.
  */
-QTestState *qtest_init(const char *extra_args);
+QTestState *qtest_init(const char *extra_args, const char *incoming_uri);
 
 /**
  * qtest_quit:
@@ -207,6 +208,16 @@  const char *qtest_get_arch(void);
 void qtest_add_func(const char *str, void (*fn));
 
 /**
+ * test_migrate:
+ * @mach_src: QTestState source.
+ * @mach_dst: QTestState destination.
+ *
+ * Migrate a VM from mach_src to mach_dst.
+ * Returns 0 on success, -1 on failure.
+ */
+int test_migrate(QTestState *mach_src, QTestState *mach_dst);
+
+/**
  * qtest_start:
  * @args: other arguments to pass to QEMU
  *
@@ -214,7 +225,7 @@  void qtest_add_func(const char *str, void (*fn));
  * The global variable is used by "shortcut" macros documented below.
  */
 #define qtest_start(args) (            \
-    global_qtest = qtest_init((args)) \
+    global_qtest = qtest_init((args), NULL) \
         )
 
 /**
diff --git a/tests/migrate-test.c b/tests/migrate-test.c
new file mode 100644
index 0000000..80ef815
--- /dev/null
+++ b/tests/migrate-test.c
@@ -0,0 +1,68 @@ 
+/*
+ * Migration tests
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Jason Baron   <jbaron@redhat.com>
+ *
+ * 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 "libqtest.h"
+
+#include <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static QTestState *mach_src;
+static QTestState *mach_dst;
+
+static void migrate_cleanup(void)
+{
+    if (mach_src) {
+        qtest_quit(mach_src);
+    }
+    if (mach_dst) {
+        qtest_quit(mach_dst);
+    }
+}
+
+static void test_migration(void)
+{
+    int ret;
+
+    ret = test_migrate(mach_src, mach_dst);
+    if (ret) {
+        migrate_cleanup();
+        g_assert(false);
+    }
+}
+
+int main(int argc, char **argv)
+{
+    int ret;
+    gchar *src_cmdline;
+    gchar *dst_cmdline;
+    const char *dst_uri = "tcp:0:4444";
+
+    g_test_init(&argc, &argv, NULL);
+
+    src_cmdline = g_strdup("-display none -machine pc");
+    mach_src = qtest_init(src_cmdline, NULL);
+
+    dst_cmdline = g_strdup_printf("%s -incoming %s", src_cmdline, dst_uri);
+    mach_dst = qtest_init(dst_cmdline, dst_uri);
+
+    g_free(src_cmdline);
+    g_free(dst_cmdline);
+
+    qtest_add_func("/migrate/src-to-dst", test_migration);
+    ret = g_test_run();
+
+    migrate_cleanup();
+    return ret;
+}