When -incoming "defer" is not used, the incoming migration is invoked
directly by the command line parsing code in vl.c. Allow the migration
config to be passed via the -incoming command line option so that
invocation of qmp_migrate_incoming() can receive it.
E.g.
-incoming '{"tls-creds": "tlscredsx509server0", "tls-hostname": "qemu.org"}'
Signed-off-by: Fabiano Rosas <[email protected]>
---
This is useful for the tests. If we want to declare that
config-passing only works with -incoming defer, that's fine with me.
---
system/vl.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 100 insertions(+), 12 deletions(-)
diff --git a/system/vl.c b/system/vl.c
index d09dc9a61c..ac44933a11 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -169,6 +169,9 @@ static const char *mem_path;
static const char *incoming;
static const char *incoming_str[MIGRATION_CHANNEL_TYPE__MAX];
static MigrationChannel *incoming_channels[MIGRATION_CHANNEL_TYPE__MAX];
+static MigrationParameters *migration_config;
+static Error *migration_channel_err;
+static Error *migration_config_err;
static const char *loadvm;
static const char *accelerators;
static bool have_custom_ram_size;
@@ -1825,28 +1828,102 @@ static void object_option_add_visitor(Visitor *v)
QTAILQ_INSERT_TAIL(&object_opts, opt, next);
}
-static void incoming_option_parse(const char *str)
+/*
+ * Either "defer" or a proper uri, whether plain string or a json
+ * representation of MigrationChannel.
+ */
+static bool incoming_option_parse_channels(const char *str, Error **errp)
{
MigrationChannelType type = MIGRATION_CHANNEL_TYPE_MAIN;
- MigrationChannel *channel;
+ MigrationChannel *channel = NULL;
Visitor *v;
- if (!strcmp(str, "defer")) {
- channel = NULL;
- } else if (migrate_is_uri(str)) {
+ if (g_str_equal(str, "defer")) {
+ incoming_str[type] = str;
+ return true;
+ }
+
+ if (migrate_is_uri(str)) {
migrate_uri_parse(str, &channel, &error_fatal);
} else {
v = qobject_input_visitor_new_str(str, "channel-type", &error_fatal);
- visit_type_MigrationChannel(v, NULL, &channel, &error_fatal);
+ if (v && !visit_type_MigrationChannel(v, NULL, &channel, errp)) {
+ visit_free(v);
+ return false;
+ }
visit_free(v);
+ }
+
+ if (channel) {
type = channel->channel_type;
+ /* New incoming spec replaces the previous */
+ qapi_free_MigrationChannel(incoming_channels[type]);
+ incoming_channels[type] = channel;
+ incoming_str[type] = str;
}
- /* New incoming spec replaces the previous */
- qapi_free_MigrationChannel(incoming_channels[type]);
- incoming_channels[type] = channel;
- incoming_str[type] = str;
incoming = incoming_str[MIGRATION_CHANNEL_TYPE_MAIN];
+ return true;
+}
+
+/*
+ * The migration configuration object in JSON form.
+ */
+static bool incoming_option_parse_config(const char *str, Error **errp)
+{
+ MigrationParameters *config = NULL;
+ Visitor *v;
+
+ v = qobject_input_visitor_new_str(str, "config", &error_fatal);
+ if (v && !visit_type_MigrationParameters(v, NULL, &config, errp)) {
+ visit_free(v);
+ return false;
+ }
+
+ if (config) {
+ /* later incoming configs replace the previous ones */
+ migration_config = config;
+ }
+
+ visit_free(v);
+ return true;
+}
+
+static void incoming_option_parse(const char *str)
+{
+ /*
+ * Independent Error objects because we don't know whether the
+ * input is meant to be the channels or the config. The parsing
+ * may fail for one and succeed for the other.
+ */
+ g_autoptr(Error) channel_err = NULL;
+ g_autoptr(Error) config_err = NULL;
+
+ /*
+ * Skip if there's already an error for a previous -incoming
+ * instance.
+ */
+ if (migration_channel_err || migration_config_err) {
+ return;
+ }
+
+ if (!migration_channel_err &&
+ incoming_option_parse_channels(str, &channel_err)) {
+ return;
+ }
+
+ if (!migration_config_err &&
+ incoming_option_parse_config(str, &config_err)) {
+ return;
+ }
+
+ if (channel_err) {
+ migration_channel_err = error_copy(channel_err);
+ error_prepend(&migration_channel_err, "-incoming %s: ", str);
+ } else if (config_err) {
+ migration_config_err = error_copy(config_err);
+ error_prepend(&migration_config_err, "-incoming %s: ", str);
+ }
}
static void object_option_parse(const char *str)
@@ -2537,6 +2614,16 @@ static void qemu_validate_options(const QDict
*machine_opts)
exit(EXIT_FAILURE);
}
+ if (migration_channel_err && !incoming) {
+ error_report_err(migration_config_err);
+ exit(EXIT_FAILURE);
+ }
+
+ if (migration_config_err && !migration_config) {
+ error_report_err(migration_config_err);
+ exit(EXIT_FAILURE);
+ }
+
#ifdef CONFIG_CURSES
if (is_daemonized() && dpy.type == DISPLAY_TYPE_CURSES) {
error_report("curses display cannot be used with -daemonize");
@@ -2824,13 +2911,14 @@ void qmp_x_exit_preconfig(Error **errp)
if (incoming) {
Error *local_err = NULL;
+
if (strcmp(incoming, "defer") != 0) {
g_autofree MigrationChannelList *channels =
g_new0(MigrationChannelList, 1);
channels->value = incoming_channels[MIGRATION_CHANNEL_TYPE_MAIN];
- qmp_migrate_incoming(NULL, true, channels, NULL, true, true,
- &local_err);
+ qmp_migrate_incoming(NULL, true, channels, migration_config, true,
+ true, &local_err);
if (local_err) {
error_reportf_err(local_err, "-incoming %s: ", incoming);
exit(1);
--
2.51.0