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