Skip to contentSkip to conversation

qtest: add migration testing

PATCH In review Jason Baron 4 participants 10 messages Dec 20, 2012 – Jan 7, 2013

lore
Share & tools
lore
#0 lore raw

v2 cover qtest: add migration testing

Hi,

Add a basic qtest for migration testing. Currently, it just tests a migrate of machine 'pc' on the same host. Would be nice to extend to multiple machine versions, but that requires multiple binaries, which could be done, but is perhaps a bit awkward from qtest? Testing different machine versions within the same binary doesn't seem like a real world test case to me. Currently, the test aborts, if the migrate takes more than 2 minutes.

In any case, the test currently fails for q35, since ahci migration suport isn't in place. Thus, I intend to add q35 testing here, once those ahci migration patches are accepted.

Thanks,

-Jason

v2:
* move core migration functions to libqtest.c
* simplify Makefile dependencies
* use 'mkdtemp()' instead of child pid to identify qemu instances

Jason Baron (3):
qtest: Enable creation of multiple qemu instances
qtest: extend qtest_qmp() to fill in the reply
qtest: add migrate-test

 tests/Makefile       |    4 +-
 tests/libqtest.c     |  130 ++++++++++++++++++++++++++++++++++++++++++++------
 tests/libqtest.h     |   30 ++++++++++--
 tests/migrate-test.c |   68 ++++++++++++++++++++++++++
 4 files changed, 212 insertions(+), 20 deletions(-)
 create mode 100644 tests/migrate-test.c
#1 lore raw

PATCH v2 1/3 qtest: Enable creation of multiple qemu instances

From: Jason Baron <[email protected]>

Currently, the qtest harness can only spawn 1 qemu instance at a time because the parent pid is used to create the socket files. Use 'mkdtemp()' in combination with the parent pid to avoid conflicts.

---
 tests/libqtest.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)
tests/libqtest.c +9 -6
index 71b84c1..57665c9 100644
@@ -41,6 +41,7 @@ struct QTestState
     GString *rx;
     gchar *pid_file;
     char *socket_path, *qmp_socket_path;
+    char *tmp_dir;
 };
 
 #define g_assert_no_errno(ret) do { \
@@ -105,7 +106,6 @@ QTestState *qtest_init(const char *extra_args)
 {
     QTestState *s;
     int sock, qmpsock, ret, i;
-    gchar *pid_file;
     gchar *command;
     const char *qemu_binary;
     pid_t pid;
@@ -115,9 +115,11 @@ QTestState *qtest_init(const char *extra_args)
 
     s = g_malloc(sizeof(*s));
 
-    s->socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
-    s->qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
-    pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
+    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");
 
     sock = init_socket(s->socket_path);
     qmpsock = init_socket(s->qmp_socket_path);
@@ -131,7 +133,7 @@ QTestState *qtest_init(const char *extra_args)
                                   "-pidfile %s "
                                   "-machine accel=qtest "
                                   "%s", qemu_binary, s->socket_path,
-                                  s->qmp_socket_path, pid_file,
+                                  s->qmp_socket_path, s->pid_file,
                                   extra_args ?: "");
 
         ret = system(command);
@@ -143,7 +145,6 @@ QTestState *qtest_init(const char *extra_args)
     s->qmp_fd = socket_accept(qmpsock);
 
     s->rx = g_string_new("");
-    s->pid_file = pid_file;
     for (i = 0; i < MAX_IRQ; i++) {
         s->irq_level[i] = false;
     }
@@ -172,9 +173,11 @@ void qtest_quit(QTestState *s)
     unlink(s->pid_file);
     unlink(s->socket_path);
     unlink(s->qmp_socket_path);
+    unlink(s->tmp_dir);
     g_free(s->pid_file);
     g_free(s->socket_path);
     g_free(s->qmp_socket_path);
+    g_free(s->tmp_dir);
 }
 
 static void socket_sendf(int fd, const char *fmt, va_list ap)
Signature · 1 line
1.7.1
#2 lore raw

Re: [Qemu-devel] [PATCH v2 1/3] qtest: Enable creation of multiple qemu instances

Quoted 4 lines · Jason Baron

From: Jason Baron <[email protected]>

Currently, the qtest harness can only spawn 1 qemu instance at a time because the parent pid is used to create the socket files. Use 'mkdtemp()' in

But mkdtemp() is not available on Win32.

Quoted 60 lines

combination with the parent pid to avoid conflicts.

---
 tests/libqtest.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)
tests/libqtest.c +7 -6
index 71b84c1..57665c9 100644
@@ -41,6 +41,7 @@ struct QTestState
     GString *rx;
     gchar *pid_file;
     char *socket_path, *qmp_socket_path;
+    char *tmp_dir;
 };
 
 #define g_assert_no_errno(ret) do { \
@@ -105,7 +106,6 @@ QTestState *qtest_init(const char *extra_args)
 {
     QTestState *s;
     int sock, qmpsock, ret, i;
-    gchar *pid_file;
     gchar *command;
     const char *qemu_binary;
     pid_t pid;
@@ -115,9 +115,11 @@ QTestState *qtest_init(const char *extra_args)
 
     s = g_malloc(sizeof(*s));
 
-    s->socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
-    s->qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
-    pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
+    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");
 
     sock = init_socket(s->socket_path);
     qmpsock = init_socket(s->qmp_socket_path);
@@ -131,7 +133,7 @@ QTestState *qtest_init(const char *extra_args)
                                   "-pidfile %s "
                                   "-machine accel=qtest "
                                   "%s", qemu_binary, s->socket_path,
-                                  s->qmp_socket_path, pid_file,
+                                  s->qmp_socket_path, s->pid_file,
                                   extra_args ?: "");
 
         ret = system(command);
@@ -143,7 +145,6 @@ QTestState *qtest_init(const char *extra_args)
     s->qmp_fd = socket_accept(qmpsock);
 
     s->rx = g_string_new("");
-    s->pid_file = pid_file;
     for (i = 0; i < MAX_IRQ; i++) {
         s->irq_level[i] = false;
     }

tests/libqtest.c void qtest_quit(QTestState *s) · line 176

Quoted 4 lines
tests/libqtest.c +1 -0
@@ -172,9 +173,11 @@ void qtest_quit(QTestState *s)
     unlink(s->pid_file);
     unlink(s->socket_path);
     unlink(s->qmp_socket_path);
+    unlink(s->tmp_dir);

-EISDIR, rmdir() would be needed instead.

Quoted 10 lines
tests/libqtest.c +1 -1
     g_free(s->pid_file);
     g_free(s->socket_path);
     g_free(s->qmp_socket_path);
+    g_free(s->tmp_dir);
 }
 
 static void socket_sendf(int fd, const char *fmt, va_list ap)
--

1.7.1

#3 lore raw

Re: [Qemu-devel] [PATCH v2 1/3] qtest: Enable creation of multiple qemu instances

Quoted 7 lines · Blue Swirl
Quoted 4 lines · Jason Baron

From: Jason Baron <[email protected]>

Currently, the qtest harness can only spawn 1 qemu instance at a time because the parent pid is used to create the socket files. Use 'mkdtemp()' in

But mkdtemp() is not available on Win32.

So this case important even for qtest?

Quoted 55 lines
Quoted 55 lines

combination with the parent pid to avoid conflicts.

---
 tests/libqtest.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)
tests/libqtest.c +7 -5
index 71b84c1..57665c9 100644
@@ -41,6 +41,7 @@ struct QTestState
     GString *rx;
     gchar *pid_file;
     char *socket_path, *qmp_socket_path;
+    char *tmp_dir;
 };
 
 #define g_assert_no_errno(ret) do { \
@@ -105,7 +106,6 @@ QTestState *qtest_init(const char *extra_args)
 {
     QTestState *s;
     int sock, qmpsock, ret, i;
-    gchar *pid_file;
     gchar *command;
     const char *qemu_binary;
     pid_t pid;
@@ -115,9 +115,11 @@ QTestState *qtest_init(const char *extra_args)
 
     s = g_malloc(sizeof(*s));
 
-    s->socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
-    s->qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
-    pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
+    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");
 
     sock = init_socket(s->socket_path);
     qmpsock = init_socket(s->qmp_socket_path);
@@ -131,7 +133,7 @@ QTestState *qtest_init(const char *extra_args)
                                   "-pidfile %s "
                                   "-machine accel=qtest "
                                   "%s", qemu_binary, s->socket_path,
-                                  s->qmp_socket_path, pid_file,
+                                  s->qmp_socket_path, s->pid_file,
                                   extra_args ?: "");
 
         ret = system(command);
@@ -143,7 +145,6 @@ QTestState *qtest_init(const char *extra_args)
     s->qmp_fd = socket_accept(qmpsock);
 
Quoted 13 lines

tests/libqtest.c void qtest_quit(QTestState *s) · line 176

Quoted 10 lines
tests/libqtest.c +1 -1
     s->rx = g_string_new("");
-    s->pid_file = pid_file;
     for (i = 0; i < MAX_IRQ; i++) {
         s->irq_level[i] = false;
     }
@@ -172,9 +173,11 @@ void qtest_quit(QTestState *s)
     unlink(s->pid_file);
     unlink(s->socket_path);
     unlink(s->qmp_socket_path);
+    unlink(s->tmp_dir);

-EISDIR, rmdir() would be needed instead.

'unlink()' tested fine on Linux. But yes, it might not be as portable.

I looked at tempnam() and mktemp(), but they both generate linker warnings. 'mkstemp()' could be used but its awkward to delete the file right after its created so that bind() can create it. Plus, it could be a greater security risk in that the filename is easier to determine.

We could write our own random file string generator then, if mkdtemp(), isn't ok.

Quoted 10 lines
Quoted 10 lines
tests/libqtest.c +1 -1
     g_free(s->pid_file);
     g_free(s->socket_path);
     g_free(s->qmp_socket_path);
+    g_free(s->tmp_dir);
 }
 
 static void socket_sendf(int fd, const char *fmt, va_list ap)
--

1.7.1

#4 lore raw

Re: [Qemu-devel] [PATCH v2 1/3] qtest: Enable creation of multiple qemu instances

Quoted 10 lines · Jason Baron
Quoted 7 lines · Blue Swirl
Quoted 4 lines · Jason Baron

From: Jason Baron <[email protected]>

Currently, the qtest harness can only spawn 1 qemu instance at a time because the parent pid is used to create the socket files. Use 'mkdtemp()' in

But mkdtemp() is not available on Win32.

So this case important even for qtest?

Well, there's $(EXESUF) handling for qtest also. But it does not seem to build now:
LINK tests/test-thread-pool.exe
tests/test-thread-pool.o: In function `test_cancel':
/src/qemu/tests/test-thread-pool.c:177: undefined reference to `___sync_val_compare_and_swap_4'
tests/test-thread-pool.o: In function `worker_cb':
/src/qemu/tests/test-thread-pool.c:18: undefined reference to `___sync_fetch_and_add_4'

Quoted 72 lines
Quoted 56 lines
Quoted 56 lines

combination with the parent pid to avoid conflicts.

---
 tests/libqtest.c |   15 +++++++++------
 1 files changed, 9 insertions(+), 6 deletions(-)
tests/libqtest.c +7 -5
index 71b84c1..57665c9 100644
@@ -41,6 +41,7 @@ struct QTestState
     GString *rx;
     gchar *pid_file;
     char *socket_path, *qmp_socket_path;
+    char *tmp_dir;
 };
 
 #define g_assert_no_errno(ret) do { \
@@ -105,7 +106,6 @@ QTestState *qtest_init(const char *extra_args)
 {
     QTestState *s;
     int sock, qmpsock, ret, i;
-    gchar *pid_file;
     gchar *command;
     const char *qemu_binary;
     pid_t pid;
@@ -115,9 +115,11 @@ QTestState *qtest_init(const char *extra_args)
 
     s = g_malloc(sizeof(*s));
 
-    s->socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
-    s->qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
-    pid_file = g_strdup_printf("/tmp/qtest-%d.pid", getpid());
+    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");
 
     sock = init_socket(s->socket_path);
     qmpsock = init_socket(s->qmp_socket_path);
@@ -131,7 +133,7 @@ QTestState *qtest_init(const char *extra_args)
                                   "-pidfile %s "
                                   "-machine accel=qtest "
                                   "%s", qemu_binary, s->socket_path,
-                                  s->qmp_socket_path, pid_file,
+                                  s->qmp_socket_path, s->pid_file,
                                   extra_args ?: "");
 
         ret = system(command);
@@ -143,7 +145,6 @@ QTestState *qtest_init(const char *extra_args)
     s->qmp_fd = socket_accept(qmpsock);
 
Quoted 13 lines

tests/libqtest.c void qtest_quit(QTestState *s) · line 176

Quoted 10 lines
tests/libqtest.c +1 -1
     s->rx = g_string_new("");
-    s->pid_file = pid_file;
     for (i = 0; i < MAX_IRQ; i++) {
         s->irq_level[i] = false;
     }
@@ -172,9 +173,11 @@ void qtest_quit(QTestState *s)
     unlink(s->pid_file);
     unlink(s->socket_path);
     unlink(s->qmp_socket_path);
+    unlink(s->tmp_dir);

-EISDIR, rmdir() would be needed instead.

'unlink()' tested fine on Linux. But yes, it might not be as portable.

Quoted 7 lines

I looked at tempnam() and mktemp(), but they both generate linker warnings. 'mkstemp()' could be used but its awkward to delete the file right after its created so that bind() can create it. Plus, it could be a greater security risk in that the filename is easier to determine.

We could write our own random file string generator then, if mkdtemp(), isn't ok.

Or introduce qemu_mkdtemp() which resolves either to mkdtemp() or to a custom implementation:

http://stackoverflow.com/questions/278439/creating-a-temporary-directory-in-windows

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
tests/libqtest.c +1 -1
     g_free(s->pid_file);
     g_free(s->socket_path);
     g_free(s->qmp_socket_path);
+    g_free(s->tmp_dir);
 }
 
 static void socket_sendf(int fd, const char *fmt, va_list ap)
--

1.7.1

#5 lore raw

Re: [Qemu-devel] [PATCH v2 1/3] qtest: Enable creation of multiple qemu instances

In reply to Jason Baron
Quoted 3 lines · Jason Baron
Quoted 2 lines · Blue Swirl
Quoted 1 line · Jason Baron

From: Jason Baron <[email protected]>

[...]

Quoted 10 lines
Quoted 8 lines

tests/libqtest.c void qtest_quit(QTestState *s) · line 176

Quoted 5 lines
tests/libqtest.c +1 -0
@@ -172,9 +173,11 @@ void qtest_quit(QTestState *s)
     unlink(s->pid_file);
     unlink(s->socket_path);
     unlink(s->qmp_socket_path);
+    unlink(s->tmp_dir);

-EISDIR, rmdir() would be needed instead.

'unlink()' tested fine on Linux. But yes, it might not be as portable.

s/might not be as/isn't/

SUSv7:

[EPERM]
The file named by path is a directory, and either the calling process does not have appropriate privileges, or the implementation prohibits using unlink() on directories.

[...]

#6 lore raw

PATCH v2 2/3 qtest: extend qtest_qmp() to fill in the reply

In reply to Jason Baron

From: Jason Baron <[email protected]>

Introduce:

Add void qtest_qmp_resp(QTestState *s, QString *resp, const char *fmt, ...)

which allows a response string to be filled in.

---
 tests/Makefile   |    2 +-
 tests/libqtest.c |   17 ++++++++++-------
 tests/libqtest.h |   15 +++++++++++++--
 3 files changed, 24 insertions(+), 10 deletions(-)
tests/Makefile +1 -1
index b60f0fb..1756b47 100644
@@ -85,7 +85,7 @@ TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
 QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),))
 check-qtest-$(CONFIG_POSIX)=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
 
-qtest-obj-y = tests/libqtest.o $(oslib-obj-y) libqemustub.a
+qtest-obj-y = tests/libqtest.o $(oslib-obj-y) libqemustub.a $(test-qapi-obj-y) $(qom-obj-y)
 $(check-qtest-y): $(qtest-obj-y)
 
 .PHONY: check-help
tests/libqtest.c +10 -7
index 57665c9..994cd2f 100644
@@ -288,7 +288,7 @@ redo:
     return words;
 }
 
-void qtest_qmp(QTestState *s, const char *fmt, ...)
+void qtest_qmp_resp(QTestState *s, QString *resp, const char *fmt, ...)
 {
     va_list ap;
     bool has_reply = false;
@@ -313,16 +313,19 @@ void qtest_qmp(QTestState *s, const char *fmt, ...)
             fprintf(stderr, "Broken pipe\n");
             exit(1);
         }
-
-        switch (c) {
-        case '{':
+        if (c == '{') {
             nesting++;
             has_reply = true;
-            break;
-        case '}':
+        }
+        if (c == '}') {
             nesting--;
-            break;
         }
+        if (has_reply && resp) {
+            qstring_append_chr(resp, c);
+        }
+    }
+    if (has_reply && resp) {
+        qstring_append_chr(resp, '\0');
     }
 }
 
tests/libqtest.h +13 -2
index c8ade85..972ba5d 100644
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <sys/types.h>
+#include "qstring.h"
 
 typedef struct QTestState QTestState;
 
@@ -38,13 +39,14 @@ QTestState *qtest_init(const char *extra_args);
 void qtest_quit(QTestState *s);
 
 /**
- * qtest_qmp:
+ * qtest_qmp_resp:
  * @s: QTestState instance to operate on.
+ * @resp: Fills in response string if provided
  * @fmt...: QMP message to send to qemu
  *
  * Sends a QMP message to QEMU
  */
-void qtest_qmp(QTestState *s, const char *fmt, ...);
+void qtest_qmp_resp(QTestState *s, QString *resp, const char *fmt, ...);
 
 /**
  * qtest_get_irq:
@@ -349,4 +351,13 @@ void qtest_add_func(const char *str, void (*fn));
  */
 #define clock_set(val) qtest_clock_set(global_qtest, val)
 
+/**
+ * qtest_qmp:
+ * @s: QTestState instance to operate on.
+ * @fmt...: QMP message to send to qemu
+ *
+ * Sends a QMP message to QEMU
+ */
+#define qtest_qmp(s, fmt, ...) qtest_qmp_resp(s, NULL, fmt, ## __VA_ARGS__)
+
 #endif
Signature · 1 line
1.7.1
#7 lore raw

Re: [Qemu-devel] [PATCH v2 2/3] qtest: extend qtest_qmp() to fill in the reply

Quoted 6 lines · Jason Baron

From: Jason Baron <[email protected]>

Introduce:

Add void qtest_qmp_resp(QTestState *s, QString *resp, const char *fmt, ...)

Any reason to not just return a QString?

Also, why not do the parsing in this function and just return a QObject of the result?

Regards,

Anthony Liguori

Quoted 107 lines

which allows a response string to be filled in.

---
 tests/Makefile   |    2 +-
 tests/libqtest.c |   17 ++++++++++-------
 tests/libqtest.h |   15 +++++++++++++--
 3 files changed, 24 insertions(+), 10 deletions(-)
tests/Makefile +1 -1
index b60f0fb..1756b47 100644
@@ -85,7 +85,7 @@ TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
 QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),))
 check-qtest-$(CONFIG_POSIX)=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
 
-qtest-obj-y = tests/libqtest.o $(oslib-obj-y) libqemustub.a
+qtest-obj-y = tests/libqtest.o $(oslib-obj-y) libqemustub.a $(test-qapi-obj-y) $(qom-obj-y)
 $(check-qtest-y): $(qtest-obj-y)
 
 .PHONY: check-help
tests/libqtest.c +10 -7
index 57665c9..994cd2f 100644
@@ -288,7 +288,7 @@ redo:
     return words;
 }
 
-void qtest_qmp(QTestState *s, const char *fmt, ...)
+void qtest_qmp_resp(QTestState *s, QString *resp, const char *fmt, ...)
 {
     va_list ap;
     bool has_reply = false;
@@ -313,16 +313,19 @@ void qtest_qmp(QTestState *s, const char *fmt, ...)
             fprintf(stderr, "Broken pipe\n");
             exit(1);
         }
-
-        switch (c) {
-        case '{':
+        if (c == '{') {
             nesting++;
             has_reply = true;
-            break;
-        case '}':
+        }
+        if (c == '}') {
             nesting--;
-            break;
         }
+        if (has_reply && resp) {
+            qstring_append_chr(resp, c);
+        }
+    }
+    if (has_reply && resp) {
+        qstring_append_chr(resp, '\0');
     }
 }
 
tests/libqtest.h +13 -2
index c8ade85..972ba5d 100644
@@ -18,6 +18,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <sys/types.h>
+#include "qstring.h"
 
 typedef struct QTestState QTestState;
 
@@ -38,13 +39,14 @@ QTestState *qtest_init(const char *extra_args);
 void qtest_quit(QTestState *s);
 
 /**
- * qtest_qmp:
+ * qtest_qmp_resp:
  * @s: QTestState instance to operate on.
+ * @resp: Fills in response string if provided
  * @fmt...: QMP message to send to qemu
  *
  * Sends a QMP message to QEMU
  */
-void qtest_qmp(QTestState *s, const char *fmt, ...);
+void qtest_qmp_resp(QTestState *s, QString *resp, const char *fmt, ...);
 
 /**
  * qtest_get_irq:
@@ -349,4 +351,13 @@ void qtest_add_func(const char *str, void (*fn));
  */
 #define clock_set(val) qtest_clock_set(global_qtest, val)
 
+/**
+ * qtest_qmp:
+ * @s: QTestState instance to operate on.
+ * @fmt...: QMP message to send to qemu
+ *
+ * Sends a QMP message to QEMU
+ */
+#define qtest_qmp(s, fmt, ...) qtest_qmp_resp(s, NULL, fmt, ## __VA_ARGS__)
+
 #endif
Signature · 1 line
1.7.1
#8 lore raw

PATCH v2 3/3 qtest: add migrate-test

In reply to Jason Baron

From: Jason Baron <[email protected]>

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.

---
 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
tests/Makefile +2 -0
index 1756b47..3f0c5a2 100644
@@ -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
 
tests/libqtest.c +96 -2

137 lines not on this page · raw message

tests/libqtest.h +13 -2
index 972ba5d..47d189b 100644
@@ -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) \
         )
 
 /**
tests/migrate-test.c new file +68 -0

68 lines not on this page · raw message

Signature · 1 line
1.7.1
#9 lore raw

Re: [Qemu-devel] [PATCH v2 3/3] qtest: add migrate-test

Quoted 8 lines · Jason Baron

From: Jason Baron <[email protected]>

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.

Quoted 264 lines
---
 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
tests/Makefile +2 -0
index 1756b47..3f0c5a2 100644
@@ -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
 
tests/libqtest.c +96 -2
index 994cd2f..28cbea9 100644
@@ -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;
+}
tests/libqtest.h +13 -2
index 972ba5d..47d189b 100644
@@ -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) \
         )
 
 /**
tests/migrate-test.c new file +40 -0
new file mode 100644
index 0000000..80ef815
@@ -0,0 +1,68 @@
+/*
+ * Migration tests
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Jason Baron   <[email protected]>
+ *
+ * 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();

tests/migrate-test.c line 50

Quoted 10 lines
tests/migrate-test.c new file +10 -0
+        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.

Quoted 21 lines
tests/migrate-test.c new file +18 -1
+
+    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