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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list