[libvirt] [PATCH v5] qemu: Allow migration over IPv6

Ján Tomko jtomko at redhat.com
Fri Mar 22 19:06:00 UTC 2013


Allow migration over IPv6 by listening on [::] instead of 0.0.0.0
when QEMU supports it (QEMU_CAPS_IPV6_MIGRATION) and there is
at least one v6 address configured on the system.

Use virURIParse in qemuMigrationPrepareDirect to allow parsing
IPv6 addresses, which would cause an 'incorrect :port' error
message before.

Move setting of migrateFrom from qemuMigrationPrepare{Direct,Tunnel}
after domain XML parsing, since we need the QEMU binary path from it
to get its capabilities.

Bug: https://bugzilla.redhat.com/show_bug.cgi?id=846013
---

diff to v4:
Always listen on IPv6 if it's available.
Don't add a migration flag.

v4:
https://www.redhat.com/archives/libvir-list/2013-March/msg01213.html

discussion:
https://www.redhat.com/archives/libvir-list/2013-March/msg00515.html

 src/qemu/qemu_capabilities.c |   6 +++
 src/qemu/qemu_capabilities.h |   1 +
 src/qemu/qemu_migration.c    | 104 +++++++++++++++++++++++++++++++++----------
 tests/qemuhelptest.c         |   9 ++--
 4 files changed, 93 insertions(+), 27 deletions(-)

diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c
index 3840b41..1e1da4d 100644
--- a/src/qemu/qemu_capabilities.c
+++ b/src/qemu/qemu_capabilities.c
@@ -213,6 +213,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
               "virtio-ccw",
               "dtb",
               "megasas",
+
+              "ipv6-migration", /* 135 */
     );
 
 struct _virQEMUCaps {
@@ -1181,6 +1183,9 @@ virQEMUCapsComputeCmdFlags(const char *help,
     if (version >= 11000)
         virQEMUCapsSet(qemuCaps, QEMU_CAPS_CPU_HOST);
 
+    if (version >= 1001000)
+        virQEMUCapsSet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION);
+
     if (version >= 1002000)
         virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
     return 0;
@@ -2317,6 +2322,7 @@ virQEMUCapsInitQMPBasic(virQEMUCapsPtr qemuCaps)
     virQEMUCapsSet(qemuCaps, QEMU_CAPS_SECCOMP_SANDBOX);
     virQEMUCapsSet(qemuCaps, QEMU_CAPS_NO_KVM_PIT);
     virQEMUCapsSet(qemuCaps, QEMU_CAPS_DTB);
+    virQEMUCapsSet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION);
 }
 
 
diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h
index 7101f67..2ccc7c2 100644
--- a/src/qemu/qemu_capabilities.h
+++ b/src/qemu/qemu_capabilities.h
@@ -174,6 +174,7 @@ enum virQEMUCapsFlags {
     QEMU_CAPS_VIRTIO_CCW         = 132, /* -device virtio-*-ccw */
     QEMU_CAPS_DTB                = 133, /* -dtb file */
     QEMU_CAPS_SCSI_MEGASAS       = 134, /* -device megasas */
+    QEMU_CAPS_IPV6_MIGRATION     = 135, /* -incoming [::] */
 
     QEMU_CAPS_LAST,                   /* this must always be the last item */
 };
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 537b834..867c7f1 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -22,6 +22,8 @@
 
 #include <config.h>
 
+#include <netdb.h>
+#include <sys/socket.h>
 #include <sys/time.h>
 #ifdef WITH_GNUTLS
 # include <gnutls/gnutls.h>
@@ -1104,12 +1106,12 @@ error:
  */
 static int
 qemuMigrationStartNBDServer(virQEMUDriverPtr driver,
-                            virDomainObjPtr vm)
+                            virDomainObjPtr vm,
+                            const char *listenAddr)
 {
     int ret = -1;
     qemuDomainObjPrivatePtr priv = vm->privateData;
     unsigned short port = 0;
-    const char *listenAddr = "0.0.0.0";
     char *diskAlias = NULL;
     size_t i;
 
@@ -1980,8 +1982,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
                         int *cookieoutlen,
                         const char *dname,
                         const char *dom_xml,
-                        const char *migrateFrom,
                         virStreamPtr st,
+                        unsigned int port,
                         unsigned long flags)
 {
     virDomainDefPtr def = NULL;
@@ -1997,6 +1999,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
     char *xmlout = NULL;
     unsigned int cookieFlags;
     virCapsPtr caps = NULL;
+    const char *listenAddr = NULL;
+    char *migrateFrom = NULL;
 
     if (virTimeMillisNow(&now) < 0)
         return -1;
@@ -2084,6 +2088,45 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
         }
     }
 
+    if (tunnel) {
+        /* QEMU will be started with -incoming stdio
+         * (which qemu_command might convert to exec:cat or fd:n)
+         */
+        if (!(migrateFrom = strdup("stdio"))) {
+            virReportOOMError();
+            goto cleanup;
+        }
+    } else {
+        virQEMUCapsPtr qemuCaps = NULL;
+        struct addrinfo *info = NULL;
+        struct addrinfo hints = { .ai_flags = AI_ADDRCONFIG,
+                                  .ai_socktype = SOCK_STREAM };
+
+        if (!(qemuCaps = virQEMUCapsCacheLookupCopy(driver->qemuCapsCache,
+                                                    def->emulator)))
+            goto cleanup;
+
+        /* Listen on :: instead of 0.0.0.0 if QEMU understands it
+         * and there is at least one IPv6 address configured
+         */
+        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION) &&
+            getaddrinfo("::", NULL, &hints, &info) == 0) {
+            freeaddrinfo(info);
+            listenAddr = "[::]";
+        } else {
+            listenAddr = "0.0.0.0";
+        }
+        virObjectUnref(qemuCaps);
+
+        /* QEMU will be started with -incoming [::]:port
+         * or -incoming 0.0.0.0:port
+         */
+        if (virAsprintf(&migrateFrom, "tcp:%s:%d", listenAddr, port) < 0) {
+            virReportOOMError();
+            goto cleanup;
+        }
+    }
+
     if (!(vm = virDomainObjListAdd(driver->domains,
                                    driver->xmlconf,
                                    def,
@@ -2172,7 +2215,7 @@ done:
         if (flags & VIR_MIGRATE_TUNNELLED)
             VIR_DEBUG("NBD in tunnelled migration is currently not supported");
         else {
-            if (qemuMigrationStartNBDServer(driver, vm) < 0) {
+            if (qemuMigrationStartNBDServer(driver, vm, listenAddr) < 0) {
                 /* error already reported */
                 goto endjob;
             }
@@ -2213,6 +2256,7 @@ done:
     ret = 0;
 
 cleanup:
+    VIR_FREE(migrateFrom);
     VIR_FREE(origname);
     VIR_FREE(xmlout);
     virDomainDefFree(def);
@@ -2270,12 +2314,9 @@ qemuMigrationPrepareTunnel(virQEMUDriverPtr driver,
               driver, dconn, NULLSTR(cookiein), cookieinlen,
               cookieout, cookieoutlen, st, NULLSTR(dname), dom_xml, flags);
 
-    /* QEMU will be started with -incoming stdio (which qemu_command might
-     * convert to exec:cat or fd:n)
-     */
     ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
                                   cookieout, cookieoutlen, dname, dom_xml,
-                                  "stdio", st, flags);
+                                  st, 0, flags);
     return ret;
 }
 
@@ -2296,9 +2337,10 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
     static int port = 0;
     int this_port;
     char *hostname = NULL;
-    char migrateFrom [64];
     const char *p;
+    char *uri_str = NULL;
     int ret = -1;
+    virURIPtr uri;
 
     VIR_DEBUG("driver=%p, dconn=%p, cookiein=%s, cookieinlen=%d, "
               "cookieout=%p, cookieoutlen=%p, uri_in=%s, uri_out=%p, "
@@ -2347,16 +2389,39 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
          * URI when passing it to the qemu monitor, so bad
          * characters in hostname part don't matter.
          */
-        if (!STRPREFIX(uri_in, "tcp:")) {
+        if (!(p = STRSKIP(uri_in, "tcp:"))) {
             virReportError(VIR_ERR_INVALID_ARG, "%s",
                            _("only tcp URIs are supported for KVM/QEMU"
                              " migrations"));
             goto cleanup;
         }
 
-        /* Get the port number. */
-        p = strrchr(uri_in, ':');
-        if (p == strchr(uri_in, ':')) {
+        /* Convert uri_in to well-formed URI with // after tcp: */
+        if (!(STRPREFIX(uri_in, "tcp://"))) {
+            if (virAsprintf(&uri_str, "tcp://%s", p) < 0) {
+                virReportOOMError();
+                goto cleanup;
+            }
+        }
+
+        uri = virURIParse(uri_str ? uri_str : uri_in);
+        VIR_FREE(uri_str);
+
+        if (uri == NULL) {
+            virReportError(VIR_ERR_INVALID_ARG, _("unable to parse URI: %s"),
+                           uri_in);
+            goto cleanup;
+        }
+
+        if (uri->server == NULL) {
+            virReportError(VIR_ERR_INVALID_ARG, _("missing host in migration"
+                                                  " URI: %s"), uri_in);
+            goto cleanup;
+        } else {
+            hostname = uri->server;
+        }
+
+        if (uri->port == 0) {
             /* Generate a port */
             this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
             if (port == QEMUD_MIGRATION_NUM_PORTS)
@@ -2369,25 +2434,16 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
             }
 
         } else {
-            p++; /* definitely has a ':' in it, see above */
-            this_port = virParseNumber(&p);
-            if (this_port == -1 || p-uri_in != strlen(uri_in)) {
-                virReportError(VIR_ERR_INVALID_ARG,
-                               "%s", _("URI ended with incorrect ':port'"));
-                goto cleanup;
-            }
+            this_port = uri->port;
         }
     }
 
     if (*uri_out)
         VIR_DEBUG("Generated uri_out=%s", *uri_out);
 
-    /* QEMU will be started with -incoming tcp:0.0.0.0:port */
-    snprintf(migrateFrom, sizeof(migrateFrom), "tcp:0.0.0.0:%d", this_port);
-
     ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
                                   cookieout, cookieoutlen, dname, dom_xml,
-                                  migrateFrom, NULL, flags);
+                                  NULL, this_port, flags);
 cleanup:
     VIR_FREE(hostname);
     if (ret != 0)
diff --git a/tests/qemuhelptest.c b/tests/qemuhelptest.c
index a28109a..05bb8a6 100644
--- a/tests/qemuhelptest.c
+++ b/tests/qemuhelptest.c
@@ -812,7 +812,8 @@ mymain(void)
             QEMU_CAPS_DEVICE_VMWARE_SVGA,
             QEMU_CAPS_DEVICE_USB_SERIAL,
             QEMU_CAPS_DEVICE_USB_NET,
-            QEMU_CAPS_DTB);
+            QEMU_CAPS_DTB,
+            QEMU_CAPS_IPV6_MIGRATION);
     DO_TEST("qemu-1.2.0", 1002000, 0, 0,
             QEMU_CAPS_VNC_COLON,
             QEMU_CAPS_NO_REBOOT,
@@ -913,7 +914,8 @@ mymain(void)
             QEMU_CAPS_DEVICE_USB_SERIAL,
             QEMU_CAPS_DEVICE_USB_NET,
             QEMU_CAPS_DTB,
-            QEMU_CAPS_SCSI_MEGASAS);
+            QEMU_CAPS_SCSI_MEGASAS,
+            QEMU_CAPS_IPV6_MIGRATION);
     DO_TEST("qemu-kvm-1.2.0", 1002000, 1, 0,
             QEMU_CAPS_VNC_COLON,
             QEMU_CAPS_NO_REBOOT,
@@ -1019,7 +1021,8 @@ mymain(void)
             QEMU_CAPS_DEVICE_USB_SERIAL,
             QEMU_CAPS_DEVICE_USB_NET,
             QEMU_CAPS_DTB,
-            QEMU_CAPS_SCSI_MEGASAS);
+            QEMU_CAPS_SCSI_MEGASAS,
+            QEMU_CAPS_IPV6_MIGRATION);
 
     return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
-- 
1.8.1.5




More information about the libvir-list mailing list