]> xenbits.xensource.com Git - people/liuw/libxenctrl-split/libvirt.git/commitdiff
qemu: wait for SPICE to migrate
authorMichal Privoznik <mprivozn@redhat.com>
Thu, 20 Sep 2012 09:15:31 +0000 (11:15 +0200)
committerMichal Privoznik <mprivozn@redhat.com>
Wed, 26 Sep 2012 09:42:59 +0000 (11:42 +0200)
Recently, there have been some improvements made to qemu so it
supports seamless migration or something very close to it.
However, it requires libvirt interaction. Once qemu is migrated,
the SPICE server needs to send its internal state to the destination.
Once it's done, it fires SPICE_MIGRATE_COMPLETED event and this
fact is advertised in 'query-spice' output as well.
We must not kill qemu until SPICE server finishes the transfer.

src/qemu/qemu_capabilities.c
src/qemu/qemu_capabilities.h
src/qemu/qemu_command.c
src/qemu/qemu_migration.c
src/qemu/qemu_monitor.c
src/qemu/qemu_monitor.h
src/qemu/qemu_monitor_json.c
src/qemu/qemu_monitor_json.h

index 3b08ef85967d3a20b54c1d245a70029687842a4c..be86014cfe5a9584920e5298307cb50352700225 100644 (file)
@@ -183,6 +183,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST,
 
               "reboot-timeout", /* 110 */
               "dump-guest-core",
+              "seamless-migration",
     );
 
 struct _qemuCaps {
@@ -1149,6 +1150,8 @@ qemuCapsComputeCmdFlags(const char *help,
     }
     if (strstr(help, "-spice"))
         qemuCapsSet(caps, QEMU_CAPS_SPICE);
+    if (strstr(help, "seamless-migration="))
+        qemuCapsSet(caps, QEMU_CAPS_SEAMLESS_MIGRATION);
     if (strstr(help, "boot=on"))
         qemuCapsSet(caps, QEMU_CAPS_DRIVE_BOOT);
     if (strstr(help, "serial=s"))
index 485c2974df93576e38e19c039e8b0bb1d39726c9..9e4543aede99be9fa49b60bf8e9f85d2676c3c91 100644 (file)
@@ -147,6 +147,7 @@ enum qemuCapsFlags {
     QEMU_CAPS_SECCOMP_SANDBOX    = 109, /* -sandbox */
     QEMU_CAPS_REBOOT_TIMEOUT     = 110, /* -boot reboot-timeout */
     QEMU_CAPS_DUMP_GUEST_CORE    = 111, /* dump-guest-core-parameter */
+    QEMU_CAPS_SEAMLESS_MIGRATION = 112, /* seamless-migration for SPICE */
 
     QEMU_CAPS_LAST,                   /* this must always be the last item */
 };
index e7bb88e6970a537f2b69692a04fdd97c578fda20..4e14ae3fb412d4c8210d0f0c59642b1f405186d0 100644 (file)
@@ -6150,6 +6150,14 @@ qemuBuildCommandLine(virConnectPtr conn,
         if (def->graphics[0]->data.spice.copypaste == VIR_DOMAIN_GRAPHICS_SPICE_CLIPBOARD_COPYPASTE_NO)
             virBufferAddLit(&opt, ",disable-copy-paste");
 
+        if (qemuCapsGet(caps, QEMU_CAPS_SEAMLESS_MIGRATION)) {
+            /* If qemu supports seamless migration turn it
+             * unconditionally on. If migration destination
+             * doesn't support it, it fallbacks to previous
+             * migration algorithm silently. */
+            virBufferAddLit(&opt, ",seamless-migration=on");
+        }
+
         virCommandAddArg(cmd, "-spice");
         virCommandAddArgBuffer(cmd, &opt);
         if (def->graphics[0]->data.spice.keymap)
index 8e8587573c54093e848aaa2b3d7cdb30dd615fe8..db69a0aa4ae4d2398dfeafdd656e29f58e8ed554 100644 (file)
@@ -896,10 +896,19 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver,
     qemuDomainObjPrivatePtr priv = vm->privateData;
     int ret;
     int status;
+    bool wait_for_spice = false;
+    bool spice_migrated = false;
     unsigned long long memProcessed;
     unsigned long long memRemaining;
     unsigned long long memTotal;
 
+    /* If guest uses SPICE and supports seamles_migration we have to hold up
+     * migration finish until SPICE server transfers its data */
+    if (vm->def->ngraphics == 1 &&
+        vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
+        qemuCapsGet(priv->caps, QEMU_CAPS_SEAMLESS_MIGRATION))
+        wait_for_spice = true;
+
     ret = qemuDomainObjEnterMonitorAsync(driver, vm, asyncJob);
     if (ret < 0) {
         /* Guest already exited; nothing further to update.  */
@@ -910,6 +919,13 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver,
                                         &memProcessed,
                                         &memRemaining,
                                         &memTotal);
+
+    /* If qemu says migrated, check spice */
+    if (wait_for_spice && (ret == 0) &&
+        (status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED))
+        ret = qemuMonitorGetSpiceMigrationStatus(priv->mon,
+                                                 &spice_migrated);
+
     qemuDomainObjExitMonitorWithDriver(driver, vm);
 
     if (ret < 0 || virTimeMillisNow(&priv->job.info.timeElapsed) < 0) {
@@ -939,7 +955,8 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver,
         break;
 
     case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED:
-        priv->job.info.type = VIR_DOMAIN_JOB_COMPLETED;
+        if ((wait_for_spice && spice_migrated) || (!wait_for_spice))
+            priv->job.info.type = VIR_DOMAIN_JOB_COMPLETED;
         ret = 0;
         break;
 
index 2796a613dc795b5f98c27724afe0274be2f945b5..58a9f945cf5350fb811f12fa1c693a053fbec6fa 100644 (file)
@@ -1828,6 +1828,30 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon,
 }
 
 
+int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon,
+                                       bool *spice_migrated)
+{
+    int ret;
+    VIR_DEBUG("mon=%p", mon);
+
+    if (!mon) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("monitor must not be NULL"));
+        return -1;
+    }
+
+    if (mon->json) {
+        ret = qemuMonitorJSONGetSpiceMigrationStatus(mon, spice_migrated);
+    } else {
+        virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
+                       _("JSON monitor is required"));
+        return -1;
+    }
+
+    return ret;
+}
+
+
 int qemuMonitorMigrateToFd(qemuMonitorPtr mon,
                            unsigned int flags,
                            int fd)
index 6b4eb6f1e3fd0d6837c88e24f33f58910f22b944..3455ab9c2551f2a7e3ab690024e47775dca06139 100644 (file)
@@ -343,6 +343,8 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon,
                                   unsigned long long *transferred,
                                   unsigned long long *remaining,
                                   unsigned long long *total);
+int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon,
+                                       bool *spice_migrated);
 
 typedef enum {
   QEMU_MONITOR_MIGRATE_BACKGROUND      = 1 << 0,
index f372199e3673392becb5a75817848a25c1cc9008..c55ee2bb090e1710e59ad4f67f852061a197d2ee 100644 (file)
@@ -2490,6 +2490,58 @@ int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
 }
 
 
+static int
+qemuMonitorJSONSpiceGetMigrationStatusReply(virJSONValuePtr reply,
+                                            bool *spice_migrated)
+{
+    virJSONValuePtr ret;
+    const char *migrated_str;
+
+    if (!(ret = virJSONValueObjectGet(reply, "return"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("query-spice reply was missing return data"));
+        return -1;
+    }
+
+    if (!(migrated_str = virJSONValueObjectGetString(ret, "migrated"))) {
+        /* Deliberately don't report error here as we are
+         * probably dealing with older qemu which doesn't
+         * report this yet. Pretend spice is migrated. */
+        *spice_migrated = true;
+    } else {
+        *spice_migrated = STREQ(migrated_str, "true");
+    }
+
+    return 0;
+}
+
+
+int qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorPtr mon,
+                                           bool *spice_migrated)
+{
+    int ret;
+    virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-spice",
+                                                     NULL);
+    virJSONValuePtr reply = NULL;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONCheckError(cmd, reply);
+
+    if (ret == 0)
+        ret = qemuMonitorJSONSpiceGetMigrationStatusReply(reply,
+                                                          spice_migrated);
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
 int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
                            unsigned int flags,
                            const char *uri)
index e531eb182e0cd489dc71d1c4809811f96093361a..751b81f948dbc582c33cf1b5af8122dc09e392e1 100644 (file)
@@ -134,6 +134,9 @@ int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
 int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
                            unsigned int flags,
                            const char *uri);
+int qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorPtr mon,
+                                           bool *spice_migrated);
+
 
 int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon);