[libvirt] [PATCHv4 2/2] qemu: allow migration over IPv6

Ján Tomko jtomko at redhat.com
Thu Mar 21 10:35:07 UTC 2013


Add VIR_MIGRATE_IPV6 flag which allows QEMU migration over IPv6 by
specifying a hostname.

If this flag is specified (or the migrate URI contains a numeric v6
address), we tell QEMU to listen on [::] instead of 0.0.0.0. The same
listen address is used for the NBD server.

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

Bug: https://bugzilla.redhat.com/show_bug.cgi?id=846013
---
 include/libvirt/libvirt.h.in |  1 +
 src/libvirt.c                |  8 +++++
 src/qemu/qemu_migration.c    | 82 ++++++++++++++++++++++++++++++++------------
 src/qemu/qemu_migration.h    |  3 +-
 tools/virsh-domain.c         |  7 ++++
 tools/virsh.pod              |  5 +--
 6 files changed, 81 insertions(+), 25 deletions(-)

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index f6a7aff..66e0588 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1188,6 +1188,7 @@ typedef enum {
     VIR_MIGRATE_UNSAFE            = (1 << 9), /* force migration even if it is considered unsafe */
     VIR_MIGRATE_OFFLINE           = (1 << 10), /* offline migrate */
     VIR_MIGRATE_COMPRESSED        = (1 << 11), /* compress data during migration */
+    VIR_MIGRATE_IPV6              = (1 << 12), /* use IPv6 for migration */
 } virDomainMigrateFlags;
 
 /* Domain migration. */
diff --git a/src/libvirt.c b/src/libvirt.c
index 02d5dd9..1372d44 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -5139,11 +5139,13 @@ virDomainMigrateDirect(virDomainPtr domain,
  *                                 automatically when supported).
  *   VIR_MIGRATE_UNSAFE    Force migration even if it is considered unsafe.
  *   VIR_MIGRATE_OFFLINE Migrate offline
+ *   VIR_MIGRATE_IPV6    Migrate over IPv6
  *
  * VIR_MIGRATE_TUNNELLED requires that VIR_MIGRATE_PEER2PEER be set.
  * Applications using the VIR_MIGRATE_PEER2PEER flag will probably
  * prefer to invoke virDomainMigrateToURI, avoiding the need to
  * open connection to the destination host themselves.
+ * VIR_MIGRATE_IPV6 has no effect on tunnelled migration.
  *
  * If a hypervisor supports renaming domains during migration,
  * then you may set the dname parameter to the new name (otherwise
@@ -5366,11 +5368,13 @@ error:
  *                                 automatically when supported).
  *   VIR_MIGRATE_UNSAFE    Force migration even if it is considered unsafe.
  *   VIR_MIGRATE_OFFLINE Migrate offline
+ *   VIR_MIGRATE_IPV6    Migrate over IPv6
  *
  * VIR_MIGRATE_TUNNELLED requires that VIR_MIGRATE_PEER2PEER be set.
  * Applications using the VIR_MIGRATE_PEER2PEER flag will probably
  * prefer to invoke virDomainMigrateToURI, avoiding the need to
  * open connection to the destination host themselves.
+ * VIR_MIGRATE_IPV6 has no effect on tunnelled migration.
  *
  * If a hypervisor supports renaming domains during migration,
  * then you may set the dname parameter to the new name (otherwise
@@ -5611,6 +5615,7 @@ error:
  *                                 automatically when supported).
  *   VIR_MIGRATE_UNSAFE    Force migration even if it is considered unsafe.
  *   VIR_MIGRATE_OFFLINE Migrate offline
+ *   VIR_MIGRATE_IPV6    Migrate over IPv6
  *
  * The operation of this API hinges on the VIR_MIGRATE_PEER2PEER flag.
  * If the VIR_MIGRATE_PEER2PEER flag is NOT set, the duri parameter
@@ -5626,6 +5631,7 @@ error:
  * libvirt driver can connect to the destination libvirt.
  *
  * VIR_MIGRATE_TUNNELLED requires that VIR_MIGRATE_PEER2PEER be set.
+ * VIR_MIGRATE_IPV6 has no effect on tunnelled migration.
  *
  * If you want to copy non-shared storage within migration you
  * can use either VIR_MIGRATE_NON_SHARED_DISK or
@@ -5763,6 +5769,7 @@ error:
  *                                 automatically when supported).
  *   VIR_MIGRATE_UNSAFE    Force migration even if it is considered unsafe.
  *   VIR_MIGRATE_OFFLINE Migrate offline
+ *   VIR_MIGRATE_IPV6    Migrate over IPv6
  *
  * The operation of this API hinges on the VIR_MIGRATE_PEER2PEER flag.
  *
@@ -5779,6 +5786,7 @@ error:
  * supported URI schemes.
  *
  * VIR_MIGRATE_TUNNELLED requires that VIR_MIGRATE_PEER2PEER be set.
+ * VIR_MIGRATE_IPV6 has no effect on tunnelled migration.
  *
  * If you want to copy non-shared storage within migration you
  * can use either VIR_MIGRATE_NON_SHARED_DISK or
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 537b834..56d870d 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -1104,12 +1104,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;
 
@@ -1982,6 +1982,7 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
                         const char *dom_xml,
                         const char *migrateFrom,
                         virStreamPtr st,
+                        const char *listenAddr,
                         unsigned long flags)
 {
     virDomainDefPtr def = NULL;
@@ -2172,7 +2173,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;
             }
@@ -2275,7 +2276,7 @@ qemuMigrationPrepareTunnel(virQEMUDriverPtr driver,
      */
     ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
                                   cookieout, cookieoutlen, dname, dom_xml,
-                                  "stdio", st, flags);
+                                  "stdio", st, NULL, flags);
     return ret;
 }
 
@@ -2296,9 +2297,13 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
     static int port = 0;
     int this_port;
     char *hostname = NULL;
-    char migrateFrom [64];
+    const char *listenAddr;
+    char *migrateFrom;
     const char *p;
+    char *uri_str = NULL;
     int ret = -1;
+    bool ipv6 = !!(flags & VIR_MIGRATE_IPV6);
+    virURIPtr uri;
 
     VIR_DEBUG("driver=%p, dconn=%p, cookiein=%s, cookieinlen=%d, "
               "cookieout=%p, cookieoutlen=%p, uri_in=%s, uri_out=%p, "
@@ -2347,16 +2352,45 @@ 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;
+        }
+
+        /* assume that a colon in the host part means it's an IPv6 address
+         * in this case we'll listen on IPv6 even if the IPV6 migration flag
+         * isn't set */
+        if (strchr(hostname, ':'))
+            ipv6 = true;
+
+        if (uri->port == 0) {
             /* Generate a port */
             this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
             if (port == QEMUD_MIGRATION_NUM_PORTS)
@@ -2369,25 +2403,26 @@ 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);
+    /* QEMU will be started with -incoming tcp:0.0.0.0:port
+     * or -incoming tcp:[::]:port for IPv6 */
+    listenAddr = ipv6 ? "[::]" : "0.0.0.0";
+
+    if (virAsprintf(&migrateFrom, "tcp:%s:%d", listenAddr, this_port) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
 
     ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
                                   cookieout, cookieoutlen, dname, dom_xml,
-                                  migrateFrom, NULL, flags);
+                                  migrateFrom, NULL, listenAddr, flags);
+    VIR_FREE(migrateFrom);
 cleanup:
     VIR_FREE(hostname);
     if (ret != 0)
@@ -2631,7 +2666,8 @@ cleanup:
 static int
 qemuMigrationConnect(virQEMUDriverPtr driver,
                      virDomainObjPtr vm,
-                     qemuMigrationSpecPtr spec)
+                     qemuMigrationSpecPtr spec,
+                     int addressFamily)
 {
     virNetSocketPtr sock;
     const char *host;
@@ -2649,7 +2685,7 @@ qemuMigrationConnect(virQEMUDriverPtr driver,
 
     if (virSecurityManagerSetSocketLabel(driver->securityManager, vm->def) < 0)
         goto cleanup;
-    if (virNetSocketNewConnectTCP(host, port, &sock) == 0) {
+    if (virNetSocketNewConnectTCPHints(host, port, &sock, addressFamily) == 0) {
         spec->dest.fd.qemu = virNetSocketDupFD(sock, true);
         virObjectUnref(sock);
     }
@@ -2763,7 +2799,9 @@ qemuMigrationRun(virQEMUDriverPtr driver,
 
     /* connect to the destination qemu if needed */
     if (spec->destType == MIGRATION_DEST_CONNECT_HOST &&
-        qemuMigrationConnect(driver, vm, spec) < 0) {
+        qemuMigrationConnect(driver, vm, spec,
+                             flags & VIR_MIGRATE_IPV6 ? AF_INET6
+                                                      : AF_INET) < 0) {
         qemuDomainObjExitMonitor(driver, vm);
         goto cleanup;
     }
diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
index 505e911..09d5c79 100644
--- a/src/qemu/qemu_migration.h
+++ b/src/qemu/qemu_migration.h
@@ -38,7 +38,8 @@
      VIR_MIGRATE_CHANGE_PROTECTION |            \
      VIR_MIGRATE_UNSAFE |                       \
      VIR_MIGRATE_OFFLINE |                      \
-     VIR_MIGRATE_COMPRESSED)
+     VIR_MIGRATE_COMPRESSED |                   \
+     VIR_MIGRATE_IPV6)
 
 enum qemuMigrationJobPhase {
     QEMU_MIGRATION_PHASE_NONE = 0,
diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c
index e9da11f..119c192 100644
--- a/tools/virsh-domain.c
+++ b/tools/virsh-domain.c
@@ -8326,6 +8326,10 @@ static const vshCmdOptDef opts_migrate[] = {
      .type = VSH_OT_STRING,
      .help = N_("filename containing updated XML for the target")
     },
+    {.name ="ipv6",
+     .type = VSH_OT_BOOL,
+     .help = N_("migrate over IPv6"),
+    },
     {.name = NULL}
 };
 
@@ -8393,6 +8397,9 @@ doMigrate(void *opaque)
         flags |= VIR_MIGRATE_OFFLINE;
     }
 
+    if (vshCommandOptBool(cmd, "ipv6"))
+        flags |= VIR_MIGRATE_IPV6;
+
     if (xmlfile &&
         virFileReadAll(xmlfile, 8192, &xml) < 0) {
         vshError(ctl, _("file '%s' doesn't exist"), xmlfile);
diff --git a/tools/virsh.pod b/tools/virsh.pod
index b5e632e..0125386 100644
--- a/tools/virsh.pod
+++ b/tools/virsh.pod
@@ -1043,7 +1043,7 @@ stats.
 =item B<migrate> [I<--live>] [I<--offline>] [I<--direct>] [I<--p2p> [I<--tunnelled>]]
 [I<--persistent>] [I<--undefinesource>] [I<--suspend>] [I<--copy-storage-all>]
 [I<--copy-storage-inc>] [I<--change-protection>] [I<--unsafe>] [I<--verbose>]
-[I<--compressed>] I<domain> I<desturi> [I<migrateuri>] [I<dname>]
+[I<--compressed>] [I<--ipv6>] I<domain> I<desturi> [I<migrateuri>] [I<dname>]
 [I<--timeout> B<seconds>] [I<--xml> B<file>]
 
 Migrate domain to another host.  Add I<--live> for live migration; <--p2p>
@@ -1066,7 +1066,8 @@ is implicitly enabled when supported by the hypervisor, but can be explicitly
 used to reject the migration if the hypervisor lacks change protection
 support.  I<--verbose> displays the progress of migration.  I<--compressed>
 activates compression of memory pages that have to be transferred repeatedly
-during live migration.
+during live migration.  I<--ipv6> uses IPv6 for migration (not needed for
+numeric IPv6 addresses). Has no effect on tunnelled migration.
 
 B<Note>: Individual hypervisors usually do not support all possible types of
 migration. For example, QEMU does not support direct migration.
-- 
1.8.1.5




More information about the libvir-list mailing list