diff mbox series

[V5,22/23] migration-test: cpr-transfer

Message ID 1735057028-308595-23-git-send-email-steven.sistare@oracle.com (mailing list archive)
State New
Headers show
Series Live update: cpr-transfer | expand

Commit Message

Steve Sistare Dec. 24, 2024, 4:17 p.m. UTC
Add a migration test for cpr-transfer mode.  Defer the connection to the
target monitor, else the test hangs because in cpr-transfer mode QEMU does
not listen for monitor connections until we send the migrate command to
source QEMU.

To test -incoming defer, send a migrate incoming command to the target,
after sending the migrate command to the source, as required by
cpr-transfer mode.

Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
---
 tests/qtest/migration/cpr-tests.c | 60 +++++++++++++++++++++++++++++++++++++++
 tests/qtest/migration/framework.c | 19 +++++++++++++
 tests/qtest/migration/framework.h |  3 ++
 3 files changed, 82 insertions(+)

Comments

Peter Xu Dec. 24, 2024, 8:01 p.m. UTC | #1
On Tue, Dec 24, 2024 at 08:17:07AM -0800, Steve Sistare wrote:
> Add a migration test for cpr-transfer mode.  Defer the connection to the
> target monitor, else the test hangs because in cpr-transfer mode QEMU does
> not listen for monitor connections until we send the migrate command to
> source QEMU.
> 
> To test -incoming defer, send a migrate incoming command to the target,
> after sending the migrate command to the source, as required by
> cpr-transfer mode.
> 
> Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> ---
>  tests/qtest/migration/cpr-tests.c | 60 +++++++++++++++++++++++++++++++++++++++
>  tests/qtest/migration/framework.c | 19 +++++++++++++
>  tests/qtest/migration/framework.h |  3 ++
>  3 files changed, 82 insertions(+)
> 
> diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
> index 44ce89a..b221980 100644
> --- a/tests/qtest/migration/cpr-tests.c
> +++ b/tests/qtest/migration/cpr-tests.c
> @@ -44,6 +44,62 @@ static void test_mode_reboot(void)
>      test_file_common(&args, true);
>  }
>  
> +static void *test_mode_transfer_start(QTestState *from, QTestState *to)
> +{
> +    migrate_set_parameter_str(from, "mode", "cpr-transfer");
> +    return NULL;
> +}
> +
> +/*
> + * cpr-transfer mode cannot use the target monitor prior to starting the
> + * migration, and cannot connect synchronously to the monitor, so defer
> + * the target connection.
> + */
> +static void test_mode_transfer_common(bool incoming_defer)
> +{
> +    g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs);
> +    g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs);
> +    g_autofree char *uri = g_strdup_printf("unix:%s", mig_path);
> +
> +    const char *opts = "-machine aux-ram-share=on -nodefaults";
> +    g_autofree const char *cpr_channel = g_strdup_printf(
> +        "cpr,addr.transport=socket,addr.type=unix,addr.path=%s",
> +        cpr_path);
> +    g_autofree char *opts_target = g_strdup_printf("-incoming %s %s",
> +                                                   cpr_channel, opts);
> +
> +    g_autofree char *connect_channels = g_strdup_printf(
> +        "[ { 'channel-type': 'main',"
> +        "    'addr': { 'transport': 'socket',"
> +        "              'type': 'unix',"
> +        "              'path': '%s' } } ]",
> +        mig_path);

So this test already uses json-format channels, IMHO we probably don't need
MigrateCommon.cpr_channel anymore?  We could put them all here. Then..

> +
> +    MigrateCommon args = {
> +        .start.opts_source = opts,
> +        .start.opts_target = opts_target,
> +        .start.defer_target_connect = true,
> +        .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s"
> +                                " -machine memory-backend=pc.ram",
> +        .listen_uri = incoming_defer ? "defer" : uri,
> +        .connect_channels = connect_channels,
> +        .cpr_channel = cpr_channel,
> +        .start_hook = test_mode_transfer_start,
> +    };
> +
> +    test_precopy_common(&args);
> +}
> +
> +static void test_mode_transfer(void)
> +{
> +    test_mode_transfer_common(NULL);
> +}
> +
> +static void test_mode_transfer_defer(void)
> +{
> +    test_mode_transfer_common(true);
> +}
> +
>  void migration_test_add_cpr(MigrationTestEnv *env)
>  {
>      tmpfs = env->tmpfs;
> @@ -55,4 +111,8 @@ void migration_test_add_cpr(MigrationTestEnv *env)
>      if (getenv("QEMU_TEST_FLAKY_TESTS")) {
>          migration_test_add("/migration/mode/reboot", test_mode_reboot);
>      }
> +
> +    migration_test_add("/migration/mode/transfer", test_mode_transfer);
> +    migration_test_add("/migration/mode/transfer/defer",
> +                       test_mode_transfer_defer);
>  }
> diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
> index c6ea3e1..f6175de 100644
> --- a/tests/qtest/migration/framework.c
> +++ b/tests/qtest/migration/framework.c
> @@ -411,6 +411,7 @@ void migrate_end(QTestState *from, QTestState *to, bool test_dest)
>      qtest_quit(to);
>  
>      cleanup("migsocket");
> +    cleanup("cpr.sock");
>      cleanup("src_serial");
>      cleanup("dest_serial");
>      cleanup(FILE_TEST_FILENAME);
> @@ -692,8 +693,11 @@ void test_precopy_common(MigrateCommon *args)
>  {
>      QTestState *from, *to;
>      void *data_hook = NULL;
> +    QObject *in_channels = NULL;
>      QObject *out_channels = NULL;
>  
> +    g_assert(!args->cpr_channel || args->connect_channels);
> +
>      if (migrate_start(&from, &to, args->listen_uri, &args->start)) {
>          return;
>      }
> @@ -725,8 +729,20 @@ void test_precopy_common(MigrateCommon *args)
>          }
>      }
>  
> +    /*
> +     * The cpr channel must be included in outgoing channels, but not in
> +     * migrate-incoming channels.
> +     */
>      if (args->connect_channels) {
> +        in_channels = qobject_from_json(args->connect_channels, &error_abort);
>          out_channels = qobject_from_json(args->connect_channels, &error_abort);
> +
> +        if (args->cpr_channel) {
> +            QList *channels_list = qobject_to(QList, out_channels);
> +            QObject *obj = migrate_str_to_channel(args->cpr_channel);
> +
> +            qlist_append(channels_list, obj);
> +        }

... here IIUC we don't need this trick to manipulate the qlist anymore?

Looks like if with that (convert the current cpr_connect string to JSON
format, attach it with connect_channels), then we also don't need the
pre-requisite patch to make connect_channels manipulate-able. Not sure.

>      }
>  
>      if (args->result == MIG_TEST_QMP_ERROR) {
> @@ -739,6 +755,9 @@ void test_precopy_common(MigrateCommon *args)
>      if (args->start.defer_target_connect) {
>          qtest_connect(to);
>          qtest_qmp_handshake(to);
> +        if (!strcmp(args->listen_uri, "defer")) {
> +            migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}");
> +        }
>      }
>  
>      if (args->result != MIG_TEST_SUCCEED) {
> diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
> index 1341368..4678e2a 100644
> --- a/tests/qtest/migration/framework.h
> +++ b/tests/qtest/migration/framework.h
> @@ -152,6 +152,9 @@ typedef struct {
>       */
>      const char *connect_channels;
>  
> +    /* Optional: the cpr migration channel, in JSON or dotted keys format */
> +    const char *cpr_channel;
> +
>      /* Optional: callback to run at start to set migration parameters */
>      TestMigrateStartHook start_hook;
>      /* Optional: callback to run at finish to cleanup */
> -- 
> 1.8.3.1
>
Peter Xu Dec. 24, 2024, 8:06 p.m. UTC | #2
On Tue, Dec 24, 2024 at 03:01:34PM -0500, Peter Xu wrote:
> On Tue, Dec 24, 2024 at 08:17:07AM -0800, Steve Sistare wrote:
> > Add a migration test for cpr-transfer mode.  Defer the connection to the
> > target monitor, else the test hangs because in cpr-transfer mode QEMU does
> > not listen for monitor connections until we send the migrate command to
> > source QEMU.
> > 
> > To test -incoming defer, send a migrate incoming command to the target,
> > after sending the migrate command to the source, as required by
> > cpr-transfer mode.
> > 
> > Signed-off-by: Steve Sistare <steven.sistare@oracle.com>
> > ---
> >  tests/qtest/migration/cpr-tests.c | 60 +++++++++++++++++++++++++++++++++++++++
> >  tests/qtest/migration/framework.c | 19 +++++++++++++
> >  tests/qtest/migration/framework.h |  3 ++
> >  3 files changed, 82 insertions(+)
> > 
> > diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
> > index 44ce89a..b221980 100644
> > --- a/tests/qtest/migration/cpr-tests.c
> > +++ b/tests/qtest/migration/cpr-tests.c
> > @@ -44,6 +44,62 @@ static void test_mode_reboot(void)
> >      test_file_common(&args, true);
> >  }
> >  
> > +static void *test_mode_transfer_start(QTestState *from, QTestState *to)
> > +{
> > +    migrate_set_parameter_str(from, "mode", "cpr-transfer");
> > +    return NULL;
> > +}
> > +
> > +/*
> > + * cpr-transfer mode cannot use the target monitor prior to starting the
> > + * migration, and cannot connect synchronously to the monitor, so defer
> > + * the target connection.
> > + */
> > +static void test_mode_transfer_common(bool incoming_defer)
> > +{
> > +    g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs);
> > +    g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs);
> > +    g_autofree char *uri = g_strdup_printf("unix:%s", mig_path);
> > +
> > +    const char *opts = "-machine aux-ram-share=on -nodefaults";
> > +    g_autofree const char *cpr_channel = g_strdup_printf(
> > +        "cpr,addr.transport=socket,addr.type=unix,addr.path=%s",
> > +        cpr_path);
> > +    g_autofree char *opts_target = g_strdup_printf("-incoming %s %s",
> > +                                                   cpr_channel, opts);
> > +
> > +    g_autofree char *connect_channels = g_strdup_printf(
> > +        "[ { 'channel-type': 'main',"
> > +        "    'addr': { 'transport': 'socket',"
> > +        "              'type': 'unix',"
> > +        "              'path': '%s' } } ]",
> > +        mig_path);
> 
> So this test already uses json-format channels, IMHO we probably don't need
> MigrateCommon.cpr_channel anymore?  We could put them all here. Then..
> 
> > +
> > +    MigrateCommon args = {
> > +        .start.opts_source = opts,
> > +        .start.opts_target = opts_target,
> > +        .start.defer_target_connect = true,

One more comment: maybe we can even drop defer_target_connect.  I don't
expect any non-cpr test to use it.. so introducing the parameter may only
complicate MigrateCommon struct with almost zero benefit to be reused.

If to drop it, we could detect "cpr" channel in test_precopy_common() and
only defer the connection if "cpr" channel is present.

> > +        .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s"
> > +                                " -machine memory-backend=pc.ram",
> > +        .listen_uri = incoming_defer ? "defer" : uri,
> > +        .connect_channels = connect_channels,
> > +        .cpr_channel = cpr_channel,
> > +        .start_hook = test_mode_transfer_start,
> > +    };
> > +
> > +    test_precopy_common(&args);
> > +}
> > +
> > +static void test_mode_transfer(void)
> > +{
> > +    test_mode_transfer_common(NULL);
> > +}
> > +
> > +static void test_mode_transfer_defer(void)
> > +{
> > +    test_mode_transfer_common(true);
> > +}
> > +
> >  void migration_test_add_cpr(MigrationTestEnv *env)
> >  {
> >      tmpfs = env->tmpfs;
> > @@ -55,4 +111,8 @@ void migration_test_add_cpr(MigrationTestEnv *env)
> >      if (getenv("QEMU_TEST_FLAKY_TESTS")) {
> >          migration_test_add("/migration/mode/reboot", test_mode_reboot);
> >      }
> > +
> > +    migration_test_add("/migration/mode/transfer", test_mode_transfer);
> > +    migration_test_add("/migration/mode/transfer/defer",
> > +                       test_mode_transfer_defer);
> >  }
> > diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
> > index c6ea3e1..f6175de 100644
> > --- a/tests/qtest/migration/framework.c
> > +++ b/tests/qtest/migration/framework.c
> > @@ -411,6 +411,7 @@ void migrate_end(QTestState *from, QTestState *to, bool test_dest)
> >      qtest_quit(to);
> >  
> >      cleanup("migsocket");
> > +    cleanup("cpr.sock");
> >      cleanup("src_serial");
> >      cleanup("dest_serial");
> >      cleanup(FILE_TEST_FILENAME);
> > @@ -692,8 +693,11 @@ void test_precopy_common(MigrateCommon *args)
> >  {
> >      QTestState *from, *to;
> >      void *data_hook = NULL;
> > +    QObject *in_channels = NULL;
> >      QObject *out_channels = NULL;
> >  
> > +    g_assert(!args->cpr_channel || args->connect_channels);
> > +
> >      if (migrate_start(&from, &to, args->listen_uri, &args->start)) {
> >          return;
> >      }
> > @@ -725,8 +729,20 @@ void test_precopy_common(MigrateCommon *args)
> >          }
> >      }
> >  
> > +    /*
> > +     * The cpr channel must be included in outgoing channels, but not in
> > +     * migrate-incoming channels.
> > +     */
> >      if (args->connect_channels) {
> > +        in_channels = qobject_from_json(args->connect_channels, &error_abort);
> >          out_channels = qobject_from_json(args->connect_channels, &error_abort);
> > +
> > +        if (args->cpr_channel) {
> > +            QList *channels_list = qobject_to(QList, out_channels);
> > +            QObject *obj = migrate_str_to_channel(args->cpr_channel);
> > +
> > +            qlist_append(channels_list, obj);
> > +        }
> 
> ... here IIUC we don't need this trick to manipulate the qlist anymore?
> 
> Looks like if with that (convert the current cpr_connect string to JSON
> format, attach it with connect_channels), then we also don't need the
> pre-requisite patch to make connect_channels manipulate-able. Not sure.
> 
> >      }
> >  
> >      if (args->result == MIG_TEST_QMP_ERROR) {
> > @@ -739,6 +755,9 @@ void test_precopy_common(MigrateCommon *args)
> >      if (args->start.defer_target_connect) {
> >          qtest_connect(to);
> >          qtest_qmp_handshake(to);
> > +        if (!strcmp(args->listen_uri, "defer")) {
> > +            migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}");
> > +        }
> >      }
> >  
> >      if (args->result != MIG_TEST_SUCCEED) {
> > diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
> > index 1341368..4678e2a 100644
> > --- a/tests/qtest/migration/framework.h
> > +++ b/tests/qtest/migration/framework.h
> > @@ -152,6 +152,9 @@ typedef struct {
> >       */
> >      const char *connect_channels;
> >  
> > +    /* Optional: the cpr migration channel, in JSON or dotted keys format */
> > +    const char *cpr_channel;
> > +
> >      /* Optional: callback to run at start to set migration parameters */
> >      TestMigrateStartHook start_hook;
> >      /* Optional: callback to run at finish to cleanup */
> > -- 
> > 1.8.3.1
> > 
> 
> -- 
> Peter Xu
diff mbox series

Patch

diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c
index 44ce89a..b221980 100644
--- a/tests/qtest/migration/cpr-tests.c
+++ b/tests/qtest/migration/cpr-tests.c
@@ -44,6 +44,62 @@  static void test_mode_reboot(void)
     test_file_common(&args, true);
 }
 
+static void *test_mode_transfer_start(QTestState *from, QTestState *to)
+{
+    migrate_set_parameter_str(from, "mode", "cpr-transfer");
+    return NULL;
+}
+
+/*
+ * cpr-transfer mode cannot use the target monitor prior to starting the
+ * migration, and cannot connect synchronously to the monitor, so defer
+ * the target connection.
+ */
+static void test_mode_transfer_common(bool incoming_defer)
+{
+    g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs);
+    g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs);
+    g_autofree char *uri = g_strdup_printf("unix:%s", mig_path);
+
+    const char *opts = "-machine aux-ram-share=on -nodefaults";
+    g_autofree const char *cpr_channel = g_strdup_printf(
+        "cpr,addr.transport=socket,addr.type=unix,addr.path=%s",
+        cpr_path);
+    g_autofree char *opts_target = g_strdup_printf("-incoming %s %s",
+                                                   cpr_channel, opts);
+
+    g_autofree char *connect_channels = g_strdup_printf(
+        "[ { 'channel-type': 'main',"
+        "    'addr': { 'transport': 'socket',"
+        "              'type': 'unix',"
+        "              'path': '%s' } } ]",
+        mig_path);
+
+    MigrateCommon args = {
+        .start.opts_source = opts,
+        .start.opts_target = opts_target,
+        .start.defer_target_connect = true,
+        .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s"
+                                " -machine memory-backend=pc.ram",
+        .listen_uri = incoming_defer ? "defer" : uri,
+        .connect_channels = connect_channels,
+        .cpr_channel = cpr_channel,
+        .start_hook = test_mode_transfer_start,
+    };
+
+    test_precopy_common(&args);
+}
+
+static void test_mode_transfer(void)
+{
+    test_mode_transfer_common(NULL);
+}
+
+static void test_mode_transfer_defer(void)
+{
+    test_mode_transfer_common(true);
+}
+
 void migration_test_add_cpr(MigrationTestEnv *env)
 {
     tmpfs = env->tmpfs;
@@ -55,4 +111,8 @@  void migration_test_add_cpr(MigrationTestEnv *env)
     if (getenv("QEMU_TEST_FLAKY_TESTS")) {
         migration_test_add("/migration/mode/reboot", test_mode_reboot);
     }
+
+    migration_test_add("/migration/mode/transfer", test_mode_transfer);
+    migration_test_add("/migration/mode/transfer/defer",
+                       test_mode_transfer_defer);
 }
diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c
index c6ea3e1..f6175de 100644
--- a/tests/qtest/migration/framework.c
+++ b/tests/qtest/migration/framework.c
@@ -411,6 +411,7 @@  void migrate_end(QTestState *from, QTestState *to, bool test_dest)
     qtest_quit(to);
 
     cleanup("migsocket");
+    cleanup("cpr.sock");
     cleanup("src_serial");
     cleanup("dest_serial");
     cleanup(FILE_TEST_FILENAME);
@@ -692,8 +693,11 @@  void test_precopy_common(MigrateCommon *args)
 {
     QTestState *from, *to;
     void *data_hook = NULL;
+    QObject *in_channels = NULL;
     QObject *out_channels = NULL;
 
+    g_assert(!args->cpr_channel || args->connect_channels);
+
     if (migrate_start(&from, &to, args->listen_uri, &args->start)) {
         return;
     }
@@ -725,8 +729,20 @@  void test_precopy_common(MigrateCommon *args)
         }
     }
 
+    /*
+     * The cpr channel must be included in outgoing channels, but not in
+     * migrate-incoming channels.
+     */
     if (args->connect_channels) {
+        in_channels = qobject_from_json(args->connect_channels, &error_abort);
         out_channels = qobject_from_json(args->connect_channels, &error_abort);
+
+        if (args->cpr_channel) {
+            QList *channels_list = qobject_to(QList, out_channels);
+            QObject *obj = migrate_str_to_channel(args->cpr_channel);
+
+            qlist_append(channels_list, obj);
+        }
     }
 
     if (args->result == MIG_TEST_QMP_ERROR) {
@@ -739,6 +755,9 @@  void test_precopy_common(MigrateCommon *args)
     if (args->start.defer_target_connect) {
         qtest_connect(to);
         qtest_qmp_handshake(to);
+        if (!strcmp(args->listen_uri, "defer")) {
+            migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}");
+        }
     }
 
     if (args->result != MIG_TEST_SUCCEED) {
diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h
index 1341368..4678e2a 100644
--- a/tests/qtest/migration/framework.h
+++ b/tests/qtest/migration/framework.h
@@ -152,6 +152,9 @@  typedef struct {
      */
     const char *connect_channels;
 
+    /* Optional: the cpr migration channel, in JSON or dotted keys format */
+    const char *cpr_channel;
+
     /* Optional: callback to run at start to set migration parameters */
     TestMigrateStartHook start_hook;
     /* Optional: callback to run at finish to cleanup */