The migration protocol has support for a 'cookie' parameter which
is an opaque array of bytes as far as libvirt is concerned. Drivers
may use this for passing around arbitrary extra data they might
need during migration. The QEMU driver needs todo a few things:
- Pass hostname/uuid to allow strict protection against localhost
migration attempts
- Pass SPICE/VNC server port from the target back to the source to
allow seemless relocation of client sessions
- Pass lock driver state from source to destination
* src/libvirt_private.syms: Export virXMLParseStrHelper
* src/qemu/qemu_migration.c, src/qemu/qemu_migration.h: Parsing
and formatting of migration cookies
* src/qemu/qemu_driver.c: Pass in cookie parameters where possible
* src/remote/remote_protocol.h, src/remote/remote_protocol.x: Change
cookie max length to 16384 bytes
---
src/libvirt_private.syms | 1 +
src/qemu/qemu_driver.c | 20 +-
src/qemu/qemu_migration.c | 600 +++++++++++++++++++++++++++++++++++++++++-
src/qemu/qemu_migration.h | 16 ++
src/remote/remote_protocol.h | 2 +-
src/remote/remote_protocol.x | 2 +-
6 files changed, 626 insertions(+), 15 deletions(-)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d1e2f4c..38912dc 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -946,6 +946,7 @@ virStrerror;
# xml.h
+virXMLParseStrHelper;
virXMLPropString;
virXPathBoolean;
virXPathInt;
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 82f735a..4f72c07 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -5378,8 +5378,9 @@ qemudDomainMigratePrepareTunnel(virConnectPtr dconn,
}
qemuDriverLock(driver);
- ret = qemuMigrationPrepareTunnel(driver, dconn, st,
- dname, dom_xml);
+ ret = qemuMigrationPrepareTunnel(driver, dconn,
+ NULL, 0, NULL, NULL, /* No cookies in v2 */
+ st, dname, dom_xml);
qemuDriverUnlock(driver);
cleanup:
@@ -5392,8 +5393,8 @@ cleanup:
*/
static int ATTRIBUTE_NONNULL (5)
qemudDomainMigratePrepare2 (virConnectPtr dconn,
- char **cookie ATTRIBUTE_UNUSED,
- int *cookielen ATTRIBUTE_UNUSED,
+ char **cookie,
+ int *cookielen,
const char *uri_in,
char **uri_out,
unsigned long flags,
@@ -5432,6 +5433,8 @@ qemudDomainMigratePrepare2 (virConnectPtr dconn,
}
ret = qemuMigrationPrepareDirect(driver, dconn,
+ NULL, 0, /* No input cookies in v2 */
+ cookie, cookielen,
uri_in, uri_out,
dname, dom_xml);
@@ -5475,8 +5478,9 @@ qemudDomainMigratePerform (virDomainPtr dom,
}
ret = qemuMigrationPerform(driver, dom->conn, vm,
- uri, flags,
- dname, resource);
+ uri, cookie, cookielen,
+ NULL, NULL, /* No output cookies in v2 */
+ flags, dname, resource);
cleanup:
qemuDriverUnlock(driver);
@@ -5519,7 +5523,9 @@ qemudDomainMigrateFinish2 (virConnectPtr dconn,
goto cleanup;
}
- dom = qemuMigrationFinish(driver, dconn, vm, flags, retcode);
+ dom = qemuMigrationFinish(driver, dconn, vm,
+ NULL, 0, NULL, NULL, /* No cookies in v2 */
+ flags, retcode);
cleanup:
if (orig_err) {
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 8d23cc5..097acaf 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -22,6 +22,8 @@
#include <config.h>
#include <sys/time.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
#include "qemu_migration.h"
#include "qemu_monitor.h"
@@ -37,11 +39,515 @@
#include "files.h"
#include "datatypes.h"
#include "fdstream.h"
+#include "uuid.h"
+
#define VIR_FROM_THIS VIR_FROM_QEMU
#define timeval_to_ms(tv) (((tv).tv_sec * 1000ull) + ((tv).tv_usec / 1000))
+enum qemuMigrationCookieFlags {
+ QEMU_MIGRATION_COOKIE_GRAPHICS = (1 << 0),
+};
+
+typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
+typedef qemuMigrationCookieGraphics *qemuMigrationCookieGraphicsPtr;
+struct _qemuMigrationCookieGraphics {
+ int type;
+ int port;
+ int tlsPort;
+ char *listen;
+ char *tlsSubject;
+};
+
+typedef struct _qemuMigrationCookie qemuMigrationCookie;
+typedef qemuMigrationCookie *qemuMigrationCookiePtr;
+struct _qemuMigrationCookie {
+ int flags;
+
+ /* Host properties */
+ unsigned char hostuuid[VIR_UUID_BUFLEN];
+ char *hostname;
+
+ /* Guest properties */
+ unsigned char uuid[VIR_UUID_BUFLEN];
+ char *name;
+
+ /* If (flags & QEMU_MIGRATION_COOKIE_GRAPHICS) */
+ qemuMigrationCookieGraphicsPtr graphics;
+};
+
+static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
+{
+ if (!grap)
+ return;
+ VIR_FREE(grap->listen);
+ VIR_FREE(grap->tlsSubject);
+ VIR_FREE(grap);
+}
+
+
+static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
+{
+ if (!mig)
+ return;
+
+ if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS)
+ qemuMigrationCookieGraphicsFree(mig->graphics);
+
+ VIR_FREE(mig->hostname);
+ VIR_FREE(mig->name);
+ VIR_FREE(mig);
+}
+
+
+static char *
+qemuDomainExtractTLSSubject(const char *certdir)
+{
+ char *certfile = NULL;
+ char *subject = NULL;
+ char *pemdata = NULL;
+ gnutls_datum_t pemdatum;
+ gnutls_x509_crt_t cert;
+ int ret;
+ size_t subjectlen;
+
+ if (virAsprintf(&certfile, "%s/server-cert.pem", certdir) < 0)
+ goto no_memory;
+
+ if (virFileReadAll(certfile, 8192, &pemdata) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unable to read server cert %s"), certfile);
+ goto error;
+ }
+
+ ret = gnutls_x509_crt_init(&cert);
+ if (ret < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot initialize cert object: %s"),
+ gnutls_strerror(ret));
+ goto error;
+ }
+
+ pemdatum.data = (unsigned char *)pemdata;
+ pemdatum.size = strlen(pemdata);
+
+ ret = gnutls_x509_crt_import(cert, &pemdatum, GNUTLS_X509_FMT_PEM);
+ if (ret < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("cannot load cert data from %s: %s"),
+ certfile, gnutls_strerror(ret));
+ goto error;
+ }
+
+ subjectlen = 1024;
+ if (VIR_ALLOC_N(subject, subjectlen+1) < 0)
+ goto no_memory;
+
+ gnutls_x509_crt_get_dn(cert, subject, &subjectlen);
+ subject[subjectlen] = '\0';
+
+ VIR_FREE(certfile);
+ VIR_FREE(pemdata);
+
+ return subject;
+
+no_memory:
+ virReportOOMError();
+error:
+ VIR_FREE(certfile);
+ VIR_FREE(pemdata);
+ return NULL;
+}
+
+
+static qemuMigrationCookieGraphicsPtr
+qemuMigrationCookieGraphicsAlloc(struct qemud_driver *driver,
+ virDomainGraphicsDefPtr def)
+{
+ qemuMigrationCookieGraphicsPtr mig = NULL;
+ const char *listenAddr;
+
+ if (VIR_ALLOC(mig) < 0)
+ goto no_memory;
+
+ mig->type = def->type;
+ if (mig->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
+ mig->port = def->data.vnc.port;
+ listenAddr = def->data.vnc.listenAddr;
+ if (!listenAddr)
+ listenAddr = driver->vncListen;
+
+ if (driver->vncTLS &&
+ !(mig->tlsSubject =
qemuDomainExtractTLSSubject(driver->vncTLSx509certdir)))
+ goto error;
+ } else {
+ mig->port = def->data.spice.port;
+ if (driver->spiceTLS)
+ mig->tlsPort = def->data.spice.tlsPort;
+ else
+ mig->tlsPort = -1;
+ listenAddr = def->data.spice.listenAddr;
+ if (!listenAddr)
+ listenAddr = driver->spiceListen;
+
+ if (driver->spiceTLS &&
+ !(mig->tlsSubject =
qemuDomainExtractTLSSubject(driver->spiceTLSx509certdir)))
+ goto error;
+ }
+ if (!(mig->listen = strdup(listenAddr)))
+ goto no_memory;
+
+ return mig;
+
+no_memory:
+ virReportOOMError();
+error:
+ qemuMigrationCookieGraphicsFree(mig);
+ return NULL;
+}
+
+
+static qemuMigrationCookiePtr
+qemuMigrationCookieNew(virDomainObjPtr dom)
+{
+ qemuMigrationCookiePtr mig = NULL;
+
+ if (VIR_ALLOC(mig) < 0)
+ goto no_memory;
+
+ if (!(mig->name = strdup(dom->def->name)))
+ goto no_memory;
+ memcpy(mig->uuid, dom->def->uuid, VIR_UUID_BUFLEN);
+
+ if (!(mig->hostname = virGetHostname(NULL)))
+ goto no_memory;
+ if (virGetHostUUID(mig->hostuuid) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Unable to obtain host UUID"));
+ goto error;
+ }
+
+ return mig;
+
+no_memory:
+ virReportOOMError();
+error:
+ qemuMigrationCookieFree(mig);
+ return NULL;
+}
+
+
+static int
+qemuMigrationCookieAddGraphics(qemuMigrationCookiePtr mig,
+ struct qemud_driver *driver,
+ virDomainObjPtr dom)
+{
+ if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Migration graphics data already present"));
+ return -1;
+ }
+
+ if (dom->def->ngraphics == 1 &&
+ (dom->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC ||
+ dom->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
&&
+ !(mig->graphics = qemuMigrationCookieGraphicsAlloc(driver,
dom->def->graphics[0])))
+ return -1;
+
+ mig->flags |= QEMU_MIGRATION_COOKIE_GRAPHICS;
+
+ return 0;
+}
+
+
+static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
+ qemuMigrationCookieGraphicsPtr grap)
+{
+ virBufferVSprintf(buf, " <graphics type='%s' port='%d'
listen='%s'",
+ virDomainGraphicsTypeToString(grap->type),
+ grap->port, grap->listen);
+ if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
+ virBufferVSprintf(buf, " tlsPort='%d'", grap->tlsPort);
+ if (grap->tlsSubject) {
+ virBufferVSprintf(buf, ">\n");
+ virBufferEscapeString(buf, " <cert info='subject'
value='%s'/>\n", grap->tlsSubject);
+ virBufferVSprintf(buf, " </graphics>\n");
+ } else {
+ virBufferVSprintf(buf, "/>\n");
+ }
+}
+
+
+static void qemuMigrationCookieXMLFormat(virBufferPtr buf,
+ qemuMigrationCookiePtr mig)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ char hostuuidstr[VIR_UUID_STRING_BUFLEN];
+
+ virUUIDFormat(mig->uuid, uuidstr);
+ virUUIDFormat(mig->hostuuid, hostuuidstr);
+
+ virBufferVSprintf(buf, "<qemu-migration>\n");
+ virBufferEscapeString(buf, " <name>%s</name>\n",
mig->name);
+ virBufferVSprintf(buf, " <uuid>%s</uuid>\n", uuidstr);
+ virBufferEscapeString(buf, " <hostname>%s</hostname>\n",
mig->hostname);
+ virBufferVSprintf(buf, " <hostuuid>%s</hostuuid>\n",
hostuuidstr);
+
+ if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS)
+ qemuMigrationCookieGraphicsXMLFormat(buf, mig->graphics);
+
+ virBufferAddLit(buf, "</qemu-migration>\n");
+}
+
+
+static char *qemuMigrationCookieXMLFormatStr(qemuMigrationCookiePtr mig)
+{
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+ qemuMigrationCookieXMLFormat(&buf, mig);
+
+ if (virBufferError(&buf)) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ return virBufferContentAndReset(&buf);
+}
+
+
+static qemuMigrationCookieGraphicsPtr
+qemuMigrationCookieGraphicsXMLParse(xmlXPathContextPtr ctxt)
+{
+ qemuMigrationCookieGraphicsPtr grap;
+ long port;
+ char *tmp;
+
+ if (VIR_ALLOC(grap) < 0)
+ goto no_memory;
+
+ if (!(tmp = virXPathString("string(./graphics/@type)", ctxt))) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing type attribute in migration
data"));
+ goto error;
+ }
+ if ((grap->type = virDomainGraphicsTypeFromString(tmp)) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unknown graphics type %s"), tmp);
+ VIR_FREE(tmp);
+ goto error;
+ }
+ if (virXPathLong("string(./graphics/@port)", ctxt, &port) < 0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing port attribute in migration
data"));
+ goto error;
+ }
+ grap->port = (int)port;
+ if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+ if (virXPathLong("string(./graphics/@tlsPort)", ctxt, &port) <
0) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing port attribute in migration
data"));
+ goto error;
+ }
+ grap->tlsPort = (int)port;
+ }
+ if (!(grap->listen = virXPathString("string(./graphics/@listen)",
ctxt))) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing listen attribute in migration
data"));
+ goto error;
+ }
+ /* Optional */
+ grap->tlsSubject = virXPathString("string(./graphics/cert[
info='subject']/@value)", ctxt);
+
+
+ return grap;
+
+no_memory:
+ virReportOOMError();
+error:
+ qemuMigrationCookieGraphicsFree(grap);
+ return NULL;
+}
+
+
+static int
+qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
+ xmlXPathContextPtr ctxt,
+ int flags)
+{
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ char *tmp;
+
+ /* We don't store the uuid, name, hostname, or hostuuid
+ * values. We just compare them to local data todo some
+ * sanity checking on migration operation
+ */
+
+ /* Extract domain name */
+ if (!(tmp = virXPathString("string(./name[1])", ctxt))) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing name element in migration
data"));
+ goto error;
+ }
+ if (STRNEQ(tmp, mig->name)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Incoming cookie data had unexpected name %s vs
%s"),
+ tmp, mig->name);
+ goto error;
+ }
+ VIR_FREE(tmp);
+
+ /* Extract domain uuid */
+ tmp = virXPathString("string(./uuid[1])", ctxt);
+ if (!tmp) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing uuid element in migration
data"));
+ goto error;
+ }
+ virUUIDFormat(mig->uuid, uuidstr);
+ if (STRNEQ(tmp, uuidstr)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Incoming cookie data had unexpected UUID %s vs
%s"),
+ tmp, uuidstr);
+ }
+ VIR_FREE(tmp);
+
+ /* Check & forbid "localhost" migration */
+ if (!(tmp = virXPathString("string(./hostname[1])", ctxt))) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing hostname element in migration
data"));
+ goto error;
+ }
+ if (STREQ(tmp, mig->hostname)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Attempt to migrate guest to the same host %s"),
+ tmp);
+ goto error;
+ }
+ VIR_FREE(tmp);
+
+ if (!(tmp = virXPathString("string(./hostuuid[1])", ctxt))) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("missing hostuuid element in migration
data"));
+ goto error;
+ }
+ virUUIDFormat(mig->hostuuid, uuidstr);
+ if (STREQ(tmp, uuidstr)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Attempt to migrate guest to the same host %s"),
+ tmp);
+ goto error;
+ }
+ VIR_FREE(tmp);
+
+ if ((flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
+ virXPathBoolean("count(./graphics) > 0", ctxt) &&
+ (!(mig->graphics = qemuMigrationCookieGraphicsXMLParse(ctxt))))
+ goto error;
+
+ return 0;
+
+error:
+ VIR_FREE(tmp);
+ return -1;
+}
+
+
+static int
+qemuMigrationCookieXMLParseStr(qemuMigrationCookiePtr mig,
+ const char *xml,
+ int flags)
+{
+ xmlDocPtr doc = NULL;
+ xmlXPathContextPtr ctxt = NULL;
+ int ret;
+
+ VIR_DEBUG("xml=%s", NULLSTR(xml));
+
+ if (!(doc = virXMLParseString(xml, "qemumigration.xml")))
+ goto cleanup;
+
+ if ((ctxt = xmlXPathNewContext(doc)) == NULL) {
+ virReportOOMError();
+ goto cleanup;
+ }
+
+ ctxt->node = xmlDocGetRootElement(doc);
+
+ ret = qemuMigrationCookieXMLParse(mig, ctxt, flags);
+
+cleanup:
+ xmlXPathFreeContext(ctxt);
+ xmlFreeDoc(doc);
+
+ return ret;
+}
+
+
+static int
+qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
+ struct qemud_driver *driver,
+ virDomainObjPtr dom,
+ char **cookieout,
+ int *cookieoutlen,
+ int flags)
+{
+ if (!cookieout || !cookieoutlen) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("missing migration cookie data"));
+ return -1;
+ }
+
+ *cookieoutlen = 0;
+
+ if (flags & QEMU_MIGRATION_COOKIE_GRAPHICS &&
+ qemuMigrationCookieAddGraphics(mig, driver, dom) < 0)
+ return -1;
+
+ if (!(*cookieout = qemuMigrationCookieXMLFormatStr(mig)))
+ return -1;
+
+ *cookieoutlen = strlen(*cookieout) + 1;
+
+ VIR_DEBUG("cookielen=%d cookie=%s", *cookieoutlen, *cookieout);
+
+ return 0;
+}
+
+
+static qemuMigrationCookiePtr
+qemuMigrationEatCookie(virDomainObjPtr dom,
+ const char *cookiein,
+ int cookieinlen,
+ int flags)
+{
+ qemuMigrationCookiePtr mig = NULL;
+
+ /* Parse & validate incoming cookie (if any) */
+ if (cookiein && cookieinlen &&
+ cookiein[cookieinlen-1] != '\0') {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("Migration cookie was not NULL terminated"));
+ goto error;
+ }
+
+ VIR_DEBUG("cookielen=%d cookie='%s'", cookieinlen,
NULLSTR(cookiein));
+
+ if (!(mig = qemuMigrationCookieNew(dom)))
+ return NULL;
+
+ if (cookiein && cookieinlen &&
+ qemuMigrationCookieXMLParseStr(mig,
+ cookiein,
+ flags) < 0)
+ goto error;
+
+ return mig;
+
+error:
+ qemuMigrationCookieFree(mig);
+ return NULL;
+}
bool
qemuMigrationIsAllowed(virDomainDefPtr def)
@@ -233,6 +739,10 @@ cleanup:
int
qemuMigrationPrepareTunnel(struct qemud_driver *driver,
virConnectPtr dconn,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
virStreamPtr st,
const char *dname,
const char *dom_xml)
@@ -247,6 +757,7 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
unsigned long long qemuCmdFlags;
qemuDomainObjPrivatePtr priv = NULL;
struct timeval now;
+ qemuMigrationCookiePtr mig = NULL;
if (gettimeofday(&now, NULL) < 0) {
virReportSystemError(errno, "%s",
@@ -285,6 +796,10 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
def = NULL;
priv = vm->privateData;
+ if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen,
+ QEMU_MIGRATION_COOKIE_GRAPHICS)))
+ goto cleanup;
+
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
priv->jobActive = QEMU_JOB_MIGRATION_OUT;
@@ -358,6 +873,16 @@ qemuMigrationPrepareTunnel(struct qemud_driver *driver,
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STARTED,
VIR_DOMAIN_EVENT_STARTED_MIGRATED);
+
+ if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
+ QEMU_MIGRATION_COOKIE_GRAPHICS) < 0) {
+ /* We could tear down the whole guest here, but
+ * cookie data is (so far) non-critical, so that
+ * seems little harsh. We'll just warn for now..
+ */
+ VIR_WARN0("Unable to encode migration cookie");
+ }
+
ret = 0;
endjob:
@@ -386,7 +911,7 @@ cleanup:
virDomainObjUnlock(vm);
if (event)
qemuDomainEventQueue(driver, event);
- qemuDriverUnlock(driver);
+ qemuMigrationCookieFree(mig);
return ret;
}
@@ -394,6 +919,10 @@ cleanup:
int
qemuMigrationPrepareDirect(struct qemud_driver *driver,
virConnectPtr dconn,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
const char *uri_in,
char **uri_out,
const char *dname,
@@ -411,6 +940,7 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
int internalret;
qemuDomainObjPrivatePtr priv = NULL;
struct timeval now;
+ qemuMigrationCookiePtr mig = NULL;
if (gettimeofday(&now, NULL) < 0) {
virReportSystemError(errno, "%s",
@@ -523,6 +1053,10 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
def = NULL;
priv = vm->privateData;
+ if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen,
+ QEMU_MIGRATION_COOKIE_GRAPHICS)))
+ goto cleanup;
+
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
priv->jobActive = QEMU_JOB_MIGRATION_OUT;
@@ -548,6 +1082,15 @@ qemuMigrationPrepareDirect(struct qemud_driver *driver,
goto endjob;
}
+ if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
+ QEMU_MIGRATION_COOKIE_GRAPHICS) < 0) {
+ /* We could tear down the whole guest here, but
+ * cookie data is (so far) non-critical, so that
+ * seems little harsh. We'll just warn for now..
+ */
+ VIR_WARN0("Unable to encode migration cookie");
+ }
+
qemuDomainStartAudit(vm, "migrated", true);
event = virDomainEventNewFromObj(vm,
VIR_DOMAIN_EVENT_STARTED,
@@ -580,6 +1123,7 @@ cleanup:
virDomainObjUnlock(vm);
if (event)
qemuDomainEventQueue(driver, event);
+ qemuMigrationCookieFree(mig);
return ret;
}
@@ -590,6 +1134,10 @@ cleanup:
static int doNativeMigrate(struct qemud_driver *driver,
virDomainObjPtr vm,
const char *uri,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
unsigned int flags,
const char *dname ATTRIBUTE_UNUSED,
unsigned long resource)
@@ -598,6 +1146,11 @@ static int doNativeMigrate(struct qemud_driver *driver,
xmlURIPtr uribits = NULL;
qemuDomainObjPrivatePtr priv = vm->privateData;
unsigned int background_flags = QEMU_MONITOR_MIGRATE_BACKGROUND;
+ qemuMigrationCookiePtr mig = NULL;
+
+ if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen,
+ QEMU_MIGRATION_COOKIE_GRAPHICS)))
+ goto cleanup;
/* Issue the migrate command. */
if (STRPREFIX(uri, "tcp:") && !STRPREFIX(uri, "tcp://"))
{
@@ -641,9 +1194,13 @@ static int doNativeMigrate(struct qemud_driver *driver,
if (qemuMigrationWaitForCompletion(driver, vm) < 0)
goto cleanup;
+ if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen, 0) < 0)
+ VIR_WARN0("Unable to encode migration cookie");
+
ret = 0;
cleanup:
+ qemuMigrationCookieFree(mig);
xmlFreeURI(uribits);
return ret;
}
@@ -929,14 +1486,16 @@ static int doNonTunnelMigrate(struct qemud_driver *driver,
virDomainPtr ddomain = NULL;
int retval = -1;
char *uri_out = NULL;
+ char *cookie = NULL;
+ int cookielen = 0;
int rc;
qemuDomainObjEnterRemoteWithDriver(driver, vm);
/* NB we don't pass 'uri' into this, since that's the libvirtd
* URI in this context - so we let dest pick it */
rc = dconn->driver->domainMigratePrepare2(dconn,
- NULL, /* cookie */
- 0, /* cookielen */
+ &cookie,
+ &cookielen,
NULL, /* uri */
&uri_out,
flags, dname,
@@ -961,7 +1520,10 @@ static int doNonTunnelMigrate(struct qemud_driver *driver,
goto cleanup;
}
- if (doNativeMigrate(driver, vm, uri_out, flags, dname, resource) < 0)
+ if (doNativeMigrate(driver, vm, uri_out,
+ cookie, cookielen,
+ NULL, NULL, /* No out cookie with v2 migration */
+ flags, dname, resource) < 0)
goto finish;
retval = 0;
@@ -970,13 +1532,14 @@ finish:
dname = dname ? dname : vm->def->name;
qemuDomainObjEnterRemoteWithDriver(driver, vm);
ddomain = dconn->driver->domainMigrateFinish2
- (dconn, dname, NULL, 0, uri_out, flags, retval);
+ (dconn, dname, cookie, cookielen, uri_out, flags, retval);
qemuDomainObjExitRemoteWithDriver(driver, vm);
if (ddomain)
virUnrefDomain(ddomain);
cleanup:
+ VIR_FREE(cookie);
return retval;
}
@@ -1052,6 +1615,10 @@ int qemuMigrationPerform(struct qemud_driver *driver,
virConnectPtr conn,
virDomainObjPtr vm,
const char *uri,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
unsigned long flags,
const char *dname,
unsigned long resource)
@@ -1081,11 +1648,19 @@ int qemuMigrationPerform(struct qemud_driver *driver,
}
if ((flags & (VIR_MIGRATE_TUNNELLED | VIR_MIGRATE_PEER2PEER))) {
+ if (cookieinlen) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("received unexpected cookie with P2P
migration"));
+ goto endjob;
+ }
+
if (doPeer2PeerMigrate(driver, vm, uri, flags, dname, resource) < 0)
/* doPeer2PeerMigrate already set the error, so just get out */
goto endjob;
} else {
- if (doNativeMigrate(driver, vm, uri, flags, dname, resource) < 0)
+ if (doNativeMigrate(driver, vm, uri, cookiein, cookieinlen,
+ cookieout, cookieoutlen,
+ flags, dname, resource) < 0)
goto endjob;
}
@@ -1103,6 +1678,7 @@ int qemuMigrationPerform(struct qemud_driver *driver,
virDomainRemoveInactive(&driver->domains, vm);
vm = NULL;
}
+
ret = 0;
endjob:
@@ -1179,6 +1755,10 @@ virDomainPtr
qemuMigrationFinish(struct qemud_driver *driver,
virConnectPtr dconn,
virDomainObjPtr vm,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
unsigned long flags,
int retcode)
{
@@ -1186,6 +1766,7 @@ qemuMigrationFinish(struct qemud_driver *driver,
virDomainEventPtr event = NULL;
int newVM = 1;
qemuDomainObjPrivatePtr priv = NULL;
+ qemuMigrationCookiePtr mig = NULL;
priv = vm->privateData;
if (priv->jobActive != QEMU_JOB_MIGRATION_IN) {
@@ -1196,6 +1777,9 @@ qemuMigrationFinish(struct qemud_driver *driver,
priv->jobActive = QEMU_JOB_NONE;
memset(&priv->jobInfo, 0, sizeof(priv->jobInfo));
+ if (!(mig = qemuMigrationEatCookie(vm, cookiein, cookieinlen, 0)))
+ goto cleanup;
+
if (qemuDomainObjBeginJobWithDriver(driver, vm) < 0)
goto cleanup;
@@ -1281,6 +1865,9 @@ qemuMigrationFinish(struct qemud_driver *driver,
}
}
+ if (qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen, 0) < 0)
+ VIR_WARN0("Unable to encode migration cookie");
+
endjob:
if (vm &&
qemuDomainObjEndJob(vm) == 0)
@@ -1291,5 +1878,6 @@ cleanup:
virDomainObjUnlock(vm);
if (event)
qemuDomainEventQueue(driver, event);
+ qemuMigrationCookieFree(mig);
return dom;
}
diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h
index 3cac617..e4e68dc 100644
--- a/src/qemu/qemu_migration.h
+++ b/src/qemu/qemu_migration.h
@@ -34,12 +34,20 @@ int qemuMigrationWaitForCompletion(struct qemud_driver *driver,
virDomainObjPtr
int qemuMigrationPrepareTunnel(struct qemud_driver *driver,
virConnectPtr dconn,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
virStreamPtr st,
const char *dname,
const char *dom_xml);
int qemuMigrationPrepareDirect(struct qemud_driver *driver,
virConnectPtr dconn,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
const char *uri_in,
char **uri_out,
const char *dname,
@@ -49,6 +57,10 @@ int qemuMigrationPerform(struct qemud_driver *driver,
virConnectPtr conn,
virDomainObjPtr vm,
const char *uri,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
unsigned long flags,
const char *dname,
unsigned long resource);
@@ -56,6 +68,10 @@ int qemuMigrationPerform(struct qemud_driver *driver,
virDomainPtr qemuMigrationFinish(struct qemud_driver *driver,
virConnectPtr dconn,
virDomainObjPtr vm,
+ const char *cookiein,
+ int cookieinlen,
+ char **cookieout,
+ int *cookieoutlen,
unsigned long flags,
int retcode);
diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h
index 016a2bb..37c6efe 100644
--- a/src/remote/remote_protocol.h
+++ b/src/remote/remote_protocol.h
@@ -43,7 +43,7 @@ typedef remote_nonnull_string *remote_string;
#define REMOTE_CPUMAP_MAX 256
#define REMOTE_VCPUINFO_MAX 2048
#define REMOTE_CPUMAPS_MAX 16384
-#define REMOTE_MIGRATE_COOKIE_MAX 256
+#define REMOTE_MIGRATE_COOKIE_MAX 16384
#define REMOTE_NETWORK_NAME_LIST_MAX 256
#define REMOTE_INTERFACE_NAME_LIST_MAX 256
#define REMOTE_DEFINED_INTERFACE_NAME_LIST_MAX 256
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 7eb27bd..20c671f 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -99,7 +99,7 @@ const REMOTE_VCPUINFO_MAX = 2048;
const REMOTE_CPUMAPS_MAX = 16384;
/* Upper limit on migrate cookie. */
-const REMOTE_MIGRATE_COOKIE_MAX = 256;
+const REMOTE_MIGRATE_COOKIE_MAX = 16384;
/* Upper limit on lists of network names. */
const REMOTE_NETWORK_NAME_LIST_MAX = 256;
--
1.7.4