mirror of https://gitee.com/openkylin/libvirt.git
qemu: Allow migration over IPv6
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
This commit is contained in:
parent
8893df388e
commit
f03dcc5df1
|
@ -213,6 +213,8 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST,
|
||||||
"virtio-ccw",
|
"virtio-ccw",
|
||||||
"dtb",
|
"dtb",
|
||||||
"megasas",
|
"megasas",
|
||||||
|
|
||||||
|
"ipv6-migration", /* 135 */
|
||||||
);
|
);
|
||||||
|
|
||||||
struct _virQEMUCaps {
|
struct _virQEMUCaps {
|
||||||
|
@ -1181,6 +1183,9 @@ virQEMUCapsComputeCmdFlags(const char *help,
|
||||||
if (version >= 11000)
|
if (version >= 11000)
|
||||||
virQEMUCapsSet(qemuCaps, QEMU_CAPS_CPU_HOST);
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_CPU_HOST);
|
||||||
|
|
||||||
|
if (version >= 1001000)
|
||||||
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION);
|
||||||
|
|
||||||
if (version >= 1002000)
|
if (version >= 1002000)
|
||||||
virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2310,6 +2315,7 @@ virQEMUCapsInitQMPBasic(virQEMUCapsPtr qemuCaps)
|
||||||
virQEMUCapsSet(qemuCaps, QEMU_CAPS_SECCOMP_SANDBOX);
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_SECCOMP_SANDBOX);
|
||||||
virQEMUCapsSet(qemuCaps, QEMU_CAPS_NO_KVM_PIT);
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_NO_KVM_PIT);
|
||||||
virQEMUCapsSet(qemuCaps, QEMU_CAPS_DTB);
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_DTB);
|
||||||
|
virQEMUCapsSet(qemuCaps, QEMU_CAPS_IPV6_MIGRATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -174,6 +174,7 @@ enum virQEMUCapsFlags {
|
||||||
QEMU_CAPS_VIRTIO_CCW = 132, /* -device virtio-*-ccw */
|
QEMU_CAPS_VIRTIO_CCW = 132, /* -device virtio-*-ccw */
|
||||||
QEMU_CAPS_DTB = 133, /* -dtb file */
|
QEMU_CAPS_DTB = 133, /* -dtb file */
|
||||||
QEMU_CAPS_SCSI_MEGASAS = 134, /* -device megasas */
|
QEMU_CAPS_SCSI_MEGASAS = 134, /* -device megasas */
|
||||||
|
QEMU_CAPS_IPV6_MIGRATION = 135, /* -incoming [::] */
|
||||||
|
|
||||||
QEMU_CAPS_LAST, /* this must always be the last item */
|
QEMU_CAPS_LAST, /* this must always be the last item */
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#ifdef WITH_GNUTLS
|
#ifdef WITH_GNUTLS
|
||||||
# include <gnutls/gnutls.h>
|
# include <gnutls/gnutls.h>
|
||||||
|
@ -1104,12 +1106,12 @@ error:
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
qemuMigrationStartNBDServer(virQEMUDriverPtr driver,
|
qemuMigrationStartNBDServer(virQEMUDriverPtr driver,
|
||||||
virDomainObjPtr vm)
|
virDomainObjPtr vm,
|
||||||
|
const char *listenAddr)
|
||||||
{
|
{
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
qemuDomainObjPrivatePtr priv = vm->privateData;
|
qemuDomainObjPrivatePtr priv = vm->privateData;
|
||||||
unsigned short port = 0;
|
unsigned short port = 0;
|
||||||
const char *listenAddr = "0.0.0.0";
|
|
||||||
char *diskAlias = NULL;
|
char *diskAlias = NULL;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
|
@ -1980,8 +1982,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
|
||||||
int *cookieoutlen,
|
int *cookieoutlen,
|
||||||
const char *dname,
|
const char *dname,
|
||||||
const char *dom_xml,
|
const char *dom_xml,
|
||||||
const char *migrateFrom,
|
|
||||||
virStreamPtr st,
|
virStreamPtr st,
|
||||||
|
unsigned int port,
|
||||||
unsigned long flags)
|
unsigned long flags)
|
||||||
{
|
{
|
||||||
virDomainDefPtr def = NULL;
|
virDomainDefPtr def = NULL;
|
||||||
|
@ -1997,6 +1999,8 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver,
|
||||||
char *xmlout = NULL;
|
char *xmlout = NULL;
|
||||||
unsigned int cookieFlags;
|
unsigned int cookieFlags;
|
||||||
virCapsPtr caps = NULL;
|
virCapsPtr caps = NULL;
|
||||||
|
const char *listenAddr = NULL;
|
||||||
|
char *migrateFrom = NULL;
|
||||||
|
|
||||||
if (virTimeMillisNow(&now) < 0)
|
if (virTimeMillisNow(&now) < 0)
|
||||||
return -1;
|
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,
|
if (!(vm = virDomainObjListAdd(driver->domains,
|
||||||
driver->xmlconf,
|
driver->xmlconf,
|
||||||
def,
|
def,
|
||||||
|
@ -2172,7 +2215,7 @@ done:
|
||||||
if (flags & VIR_MIGRATE_TUNNELLED)
|
if (flags & VIR_MIGRATE_TUNNELLED)
|
||||||
VIR_DEBUG("NBD in tunnelled migration is currently not supported");
|
VIR_DEBUG("NBD in tunnelled migration is currently not supported");
|
||||||
else {
|
else {
|
||||||
if (qemuMigrationStartNBDServer(driver, vm) < 0) {
|
if (qemuMigrationStartNBDServer(driver, vm, listenAddr) < 0) {
|
||||||
/* error already reported */
|
/* error already reported */
|
||||||
goto endjob;
|
goto endjob;
|
||||||
}
|
}
|
||||||
|
@ -2213,6 +2256,7 @@ done:
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
VIR_FREE(migrateFrom);
|
||||||
VIR_FREE(origname);
|
VIR_FREE(origname);
|
||||||
VIR_FREE(xmlout);
|
VIR_FREE(xmlout);
|
||||||
virDomainDefFree(def);
|
virDomainDefFree(def);
|
||||||
|
@ -2270,12 +2314,9 @@ qemuMigrationPrepareTunnel(virQEMUDriverPtr driver,
|
||||||
driver, dconn, NULLSTR(cookiein), cookieinlen,
|
driver, dconn, NULLSTR(cookiein), cookieinlen,
|
||||||
cookieout, cookieoutlen, st, NULLSTR(dname), dom_xml, flags);
|
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,
|
ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
|
||||||
cookieout, cookieoutlen, dname, dom_xml,
|
cookieout, cookieoutlen, dname, dom_xml,
|
||||||
"stdio", st, flags);
|
st, 0, flags);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2296,9 +2337,10 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
|
||||||
static int port = 0;
|
static int port = 0;
|
||||||
int this_port;
|
int this_port;
|
||||||
char *hostname = NULL;
|
char *hostname = NULL;
|
||||||
char migrateFrom [64];
|
|
||||||
const char *p;
|
const char *p;
|
||||||
|
char *uri_str = NULL;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
virURIPtr uri;
|
||||||
|
|
||||||
VIR_DEBUG("driver=%p, dconn=%p, cookiein=%s, cookieinlen=%d, "
|
VIR_DEBUG("driver=%p, dconn=%p, cookiein=%s, cookieinlen=%d, "
|
||||||
"cookieout=%p, cookieoutlen=%p, uri_in=%s, uri_out=%p, "
|
"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
|
* URI when passing it to the qemu monitor, so bad
|
||||||
* characters in hostname part don't matter.
|
* characters in hostname part don't matter.
|
||||||
*/
|
*/
|
||||||
if (!STRPREFIX(uri_in, "tcp:")) {
|
if (!(p = STRSKIP(uri_in, "tcp:"))) {
|
||||||
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
||||||
_("only tcp URIs are supported for KVM/QEMU"
|
_("only tcp URIs are supported for KVM/QEMU"
|
||||||
" migrations"));
|
" migrations"));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the port number. */
|
/* Convert uri_in to well-formed URI with // after tcp: */
|
||||||
p = strrchr(uri_in, ':');
|
if (!(STRPREFIX(uri_in, "tcp://"))) {
|
||||||
if (p == strchr(uri_in, ':')) {
|
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 */
|
/* Generate a port */
|
||||||
this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
|
this_port = QEMUD_MIGRATION_FIRST_PORT + port++;
|
||||||
if (port == QEMUD_MIGRATION_NUM_PORTS)
|
if (port == QEMUD_MIGRATION_NUM_PORTS)
|
||||||
|
@ -2369,25 +2434,16 @@ qemuMigrationPrepareDirect(virQEMUDriverPtr driver,
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
p++; /* definitely has a ':' in it, see above */
|
this_port = uri->port;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*uri_out)
|
if (*uri_out)
|
||||||
VIR_DEBUG("Generated uri_out=%s", *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,
|
ret = qemuMigrationPrepareAny(driver, dconn, cookiein, cookieinlen,
|
||||||
cookieout, cookieoutlen, dname, dom_xml,
|
cookieout, cookieoutlen, dname, dom_xml,
|
||||||
migrateFrom, NULL, flags);
|
NULL, this_port, flags);
|
||||||
cleanup:
|
cleanup:
|
||||||
VIR_FREE(hostname);
|
VIR_FREE(hostname);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
|
|
|
@ -812,7 +812,8 @@ mymain(void)
|
||||||
QEMU_CAPS_DEVICE_VMWARE_SVGA,
|
QEMU_CAPS_DEVICE_VMWARE_SVGA,
|
||||||
QEMU_CAPS_DEVICE_USB_SERIAL,
|
QEMU_CAPS_DEVICE_USB_SERIAL,
|
||||||
QEMU_CAPS_DEVICE_USB_NET,
|
QEMU_CAPS_DEVICE_USB_NET,
|
||||||
QEMU_CAPS_DTB);
|
QEMU_CAPS_DTB,
|
||||||
|
QEMU_CAPS_IPV6_MIGRATION);
|
||||||
DO_TEST("qemu-1.2.0", 1002000, 0, 0,
|
DO_TEST("qemu-1.2.0", 1002000, 0, 0,
|
||||||
QEMU_CAPS_VNC_COLON,
|
QEMU_CAPS_VNC_COLON,
|
||||||
QEMU_CAPS_NO_REBOOT,
|
QEMU_CAPS_NO_REBOOT,
|
||||||
|
@ -913,7 +914,8 @@ mymain(void)
|
||||||
QEMU_CAPS_DEVICE_USB_SERIAL,
|
QEMU_CAPS_DEVICE_USB_SERIAL,
|
||||||
QEMU_CAPS_DEVICE_USB_NET,
|
QEMU_CAPS_DEVICE_USB_NET,
|
||||||
QEMU_CAPS_DTB,
|
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,
|
DO_TEST("qemu-kvm-1.2.0", 1002000, 1, 0,
|
||||||
QEMU_CAPS_VNC_COLON,
|
QEMU_CAPS_VNC_COLON,
|
||||||
QEMU_CAPS_NO_REBOOT,
|
QEMU_CAPS_NO_REBOOT,
|
||||||
|
@ -1019,7 +1021,8 @@ mymain(void)
|
||||||
QEMU_CAPS_DEVICE_USB_SERIAL,
|
QEMU_CAPS_DEVICE_USB_SERIAL,
|
||||||
QEMU_CAPS_DEVICE_USB_NET,
|
QEMU_CAPS_DEVICE_USB_NET,
|
||||||
QEMU_CAPS_DTB,
|
QEMU_CAPS_DTB,
|
||||||
QEMU_CAPS_SCSI_MEGASAS);
|
QEMU_CAPS_SCSI_MEGASAS,
|
||||||
|
QEMU_CAPS_IPV6_MIGRATION);
|
||||||
|
|
||||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue