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