[libvirt] [PATCH 1/4] qemu: Move migration cookies to a separate file

Jiri Denemark jdenemar at redhat.com
Fri Mar 24 15:40:05 UTC 2017


Signed-off-by: Jiri Denemark <jdenemar at redhat.com>
---
 po/POTFILES.in                   |    1 +
 src/Makefile.am                  |    1 +
 src/qemu/qemu_migration.c        | 1407 +-------------------------------------
 src/qemu/qemu_migration_cookie.c | 1340 ++++++++++++++++++++++++++++++++++++
 src/qemu/qemu_migration_cookie.h |  153 +++++
 5 files changed, 1496 insertions(+), 1406 deletions(-)
 create mode 100644 src/qemu/qemu_migration_cookie.c
 create mode 100644 src/qemu/qemu_migration_cookie.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 64cb88cfa..d9e9d0591 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -141,6 +141,7 @@ src/qemu/qemu_hostdev.c
 src/qemu/qemu_hotplug.c
 src/qemu/qemu_interface.c
 src/qemu/qemu_migration.c
+src/qemu/qemu_migration_cookie.c
 src/qemu/qemu_monitor.c
 src/qemu/qemu_monitor_json.c
 src/qemu/qemu_monitor_text.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 3b1bb1da3..3295bc2c7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -839,6 +839,7 @@ QEMU_DRIVER_SOURCES =							\
 		qemu/qemu_process.c qemu/qemu_process.h			\
 		qemu/qemu_processpriv.h					\
 		qemu/qemu_migration.c qemu/qemu_migration.h		\
+		qemu/qemu_migration_cookie.c qemu/qemu_migration_cookie.h \
 		qemu/qemu_monitor.c qemu/qemu_monitor.h			\
 		qemu/qemu_monitor_text.c				\
 		qemu/qemu_monitor_text.h				\
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index f5711bcf7..b1d141755 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -24,14 +24,11 @@
 #include <netdb.h>
 #include <sys/socket.h>
 #include <sys/time.h>
-#ifdef WITH_GNUTLS
-# include <gnutls/gnutls.h>
-# include <gnutls/x509.h>
-#endif
 #include <fcntl.h>
 #include <poll.h>
 
 #include "qemu_migration.h"
+#include "qemu_migration_cookie.h"
 #include "qemu_monitor.h"
 #include "qemu_domain.h"
 #include "qemu_process.h"
@@ -85,1408 +82,6 @@ VIR_ENUM_IMPL(qemuMigrationCompressMethod, QEMU_MIGRATION_COMPRESS_LAST,
               "mt",
 );
 
-enum qemuMigrationCookieFlags {
-    QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS,
-    QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE,
-    QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT,
-    QEMU_MIGRATION_COOKIE_FLAG_NETWORK,
-    QEMU_MIGRATION_COOKIE_FLAG_NBD,
-    QEMU_MIGRATION_COOKIE_FLAG_STATS,
-    QEMU_MIGRATION_COOKIE_FLAG_MEMORY_HOTPLUG,
-    QEMU_MIGRATION_COOKIE_FLAG_CPU_HOTPLUG,
-
-    QEMU_MIGRATION_COOKIE_FLAG_LAST
-};
-
-VIR_ENUM_DECL(qemuMigrationCookieFlag);
-VIR_ENUM_IMPL(qemuMigrationCookieFlag,
-              QEMU_MIGRATION_COOKIE_FLAG_LAST,
-              "graphics",
-              "lockstate",
-              "persistent",
-              "network",
-              "nbd",
-              "statistics",
-              "memory-hotplug",
-              "cpu-hotplug");
-
-enum qemuMigrationCookieFeatures {
-    QEMU_MIGRATION_COOKIE_GRAPHICS  = (1 << QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS),
-    QEMU_MIGRATION_COOKIE_LOCKSTATE = (1 << QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE),
-    QEMU_MIGRATION_COOKIE_PERSISTENT = (1 << QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT),
-    QEMU_MIGRATION_COOKIE_NETWORK = (1 << QEMU_MIGRATION_COOKIE_FLAG_NETWORK),
-    QEMU_MIGRATION_COOKIE_NBD = (1 << QEMU_MIGRATION_COOKIE_FLAG_NBD),
-    QEMU_MIGRATION_COOKIE_STATS = (1 << QEMU_MIGRATION_COOKIE_FLAG_STATS),
-    QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG = (1 << QEMU_MIGRATION_COOKIE_FLAG_MEMORY_HOTPLUG),
-    QEMU_MIGRATION_COOKIE_CPU_HOTPLUG = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU_HOTPLUG),
-};
-
-typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
-typedef qemuMigrationCookieGraphics *qemuMigrationCookieGraphicsPtr;
-struct _qemuMigrationCookieGraphics {
-    int type;
-    int port;
-    int tlsPort;
-    char *listen;
-    char *tlsSubject;
-};
-
-typedef struct _qemuMigrationCookieNetData qemuMigrationCookieNetData;
-typedef qemuMigrationCookieNetData *qemuMigrationCookieNetDataPtr;
-struct _qemuMigrationCookieNetData {
-    int vporttype; /* enum virNetDevVPortProfile */
-
-    /*
-     * Array of pointers to saved data. Each VIF will have its own
-     * data to transfer.
-     */
-    char *portdata;
-};
-
-typedef struct _qemuMigrationCookieNetwork qemuMigrationCookieNetwork;
-typedef qemuMigrationCookieNetwork *qemuMigrationCookieNetworkPtr;
-struct _qemuMigrationCookieNetwork {
-    /* How many virtual NICs are we saving data for? */
-    int nnets;
-
-    qemuMigrationCookieNetDataPtr net;
-};
-
-typedef struct _qemuMigrationCookieNBD qemuMigrationCookieNBD;
-typedef qemuMigrationCookieNBD *qemuMigrationCookieNBDPtr;
-struct _qemuMigrationCookieNBD {
-    int port; /* on which port does NBD server listen for incoming data */
-
-    size_t ndisks;  /* Number of items in @disk array */
-    struct {
-        char *target;                   /* Disk target */
-        unsigned long long capacity;    /* And its capacity */
-    } *disks;
-};
-
-typedef struct _qemuMigrationCookie qemuMigrationCookie;
-typedef qemuMigrationCookie *qemuMigrationCookiePtr;
-struct _qemuMigrationCookie {
-    unsigned int flags;
-    unsigned int flagsMandatory;
-
-    /* Host properties */
-    unsigned char localHostuuid[VIR_UUID_BUFLEN];
-    unsigned char remoteHostuuid[VIR_UUID_BUFLEN];
-    char *localHostname;
-    char *remoteHostname;
-
-    /* Guest properties */
-    unsigned char uuid[VIR_UUID_BUFLEN];
-    char *name;
-
-    /* If (flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) */
-    char *lockState;
-    char *lockDriver;
-
-    /* If (flags & QEMU_MIGRATION_COOKIE_GRAPHICS) */
-    qemuMigrationCookieGraphicsPtr graphics;
-
-    /* If (flags & QEMU_MIGRATION_COOKIE_PERSISTENT) */
-    virDomainDefPtr persistent;
-
-    /* If (flags & QEMU_MIGRATION_COOKIE_NETWORK) */
-    qemuMigrationCookieNetworkPtr network;
-
-    /* If (flags & QEMU_MIGRATION_COOKIE_NBD) */
-    qemuMigrationCookieNBDPtr nbd;
-
-    /* If (flags & QEMU_MIGRATION_COOKIE_STATS) */
-    qemuDomainJobInfoPtr jobInfo;
-};
-
-static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
-{
-    if (!grap)
-        return;
-    VIR_FREE(grap->listen);
-    VIR_FREE(grap->tlsSubject);
-    VIR_FREE(grap);
-}
-
-
-static void
-qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr network)
-{
-    size_t i;
-
-    if (!network)
-        return;
-
-    if (network->net) {
-        for (i = 0; i < network->nnets; i++)
-            VIR_FREE(network->net[i].portdata);
-    }
-    VIR_FREE(network->net);
-    VIR_FREE(network);
-}
-
-
-static void qemuMigrationCookieNBDFree(qemuMigrationCookieNBDPtr nbd)
-{
-    if (!nbd)
-        return;
-
-    while (nbd->ndisks)
-        VIR_FREE(nbd->disks[--nbd->ndisks].target);
-    VIR_FREE(nbd->disks);
-    VIR_FREE(nbd);
-}
-
-
-static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
-{
-    if (!mig)
-        return;
-
-    qemuMigrationCookieGraphicsFree(mig->graphics);
-    qemuMigrationCookieNetworkFree(mig->network);
-    qemuMigrationCookieNBDFree(mig->nbd);
-
-    VIR_FREE(mig->localHostname);
-    VIR_FREE(mig->remoteHostname);
-    VIR_FREE(mig->name);
-    VIR_FREE(mig->lockState);
-    VIR_FREE(mig->lockDriver);
-    VIR_FREE(mig->jobInfo);
-    VIR_FREE(mig);
-}
-
-
-#ifdef WITH_GNUTLS
-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 error;
-
-    if (virFileReadAll(certfile, 8192, &pemdata) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unable to read server cert %s"), certfile);
-        goto error;
-    }
-
-    ret = gnutls_x509_crt_init(&cert);
-    if (ret < 0) {
-        virReportError(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) {
-        virReportError(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 error;
-
-    gnutls_x509_crt_get_dn(cert, subject, &subjectlen);
-    subject[subjectlen] = '\0';
-
-    VIR_FREE(certfile);
-    VIR_FREE(pemdata);
-
-    return subject;
-
- error:
-    VIR_FREE(certfile);
-    VIR_FREE(pemdata);
-    return NULL;
-}
-#endif
-
-static qemuMigrationCookieGraphicsPtr
-qemuMigrationCookieGraphicsSpiceAlloc(virQEMUDriverPtr driver,
-                                      virDomainGraphicsDefPtr def,
-                                      virDomainGraphicsListenDefPtr glisten)
-{
-    qemuMigrationCookieGraphicsPtr mig = NULL;
-    const char *listenAddr;
-    virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
-
-    if (VIR_ALLOC(mig) < 0)
-        goto error;
-
-    mig->type = VIR_DOMAIN_GRAPHICS_TYPE_SPICE;
-    mig->port = def->data.spice.port;
-    if (cfg->spiceTLS)
-        mig->tlsPort = def->data.spice.tlsPort;
-    else
-        mig->tlsPort = -1;
-
-    if (!glisten || !(listenAddr = glisten->address))
-        listenAddr = cfg->spiceListen;
-
-#ifdef WITH_GNUTLS
-    if (cfg->spiceTLS &&
-        !(mig->tlsSubject = qemuDomainExtractTLSSubject(cfg->spiceTLSx509certdir)))
-        goto error;
-#endif
-    if (VIR_STRDUP(mig->listen, listenAddr) < 0)
-        goto error;
-
-    virObjectUnref(cfg);
-    return mig;
-
- error:
-    qemuMigrationCookieGraphicsFree(mig);
-    virObjectUnref(cfg);
-    return NULL;
-}
-
-
-static qemuMigrationCookieNetworkPtr
-qemuMigrationCookieNetworkAlloc(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
-                                virDomainDefPtr def)
-{
-    qemuMigrationCookieNetworkPtr mig;
-    size_t i;
-
-    if (VIR_ALLOC(mig) < 0)
-        goto error;
-
-    mig->nnets = def->nnets;
-
-    if (VIR_ALLOC_N(mig->net, def->nnets) <0)
-        goto error;
-
-    for (i = 0; i < def->nnets; i++) {
-        virDomainNetDefPtr netptr;
-        virNetDevVPortProfilePtr vport;
-
-        netptr = def->nets[i];
-        vport = virDomainNetGetActualVirtPortProfile(netptr);
-
-        if (vport) {
-            mig->net[i].vporttype = vport->virtPortType;
-
-            switch (vport->virtPortType) {
-            case VIR_NETDEV_VPORT_PROFILE_NONE:
-            case VIR_NETDEV_VPORT_PROFILE_8021QBG:
-            case VIR_NETDEV_VPORT_PROFILE_8021QBH:
-               break;
-            case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
-                if (virNetDevOpenvswitchGetMigrateData(&mig->net[i].portdata,
-                                                       netptr->ifname) != 0) {
-                        virReportError(VIR_ERR_INTERNAL_ERROR,
-                                       _("Unable to run command to get OVS port data for "
-                                         "interface %s"), netptr->ifname);
-                        goto error;
-                }
-                break;
-            default:
-                break;
-            }
-        }
-    }
-    return mig;
-
- error:
-    qemuMigrationCookieNetworkFree(mig);
-    return NULL;
-}
-
-static qemuMigrationCookiePtr
-qemuMigrationCookieNew(virDomainObjPtr dom)
-{
-    qemuDomainObjPrivatePtr priv = dom->privateData;
-    qemuMigrationCookiePtr mig = NULL;
-    const char *name;
-
-    if (VIR_ALLOC(mig) < 0)
-        goto error;
-
-    if (priv->origname)
-        name = priv->origname;
-    else
-        name = dom->def->name;
-    if (VIR_STRDUP(mig->name, name) < 0)
-        goto error;
-    memcpy(mig->uuid, dom->def->uuid, VIR_UUID_BUFLEN);
-
-    if (!(mig->localHostname = virGetHostname()))
-        goto error;
-    if (virGetHostUUID(mig->localHostuuid) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Unable to obtain host UUID"));
-        goto error;
-    }
-
-    return mig;
-
- error:
-    qemuMigrationCookieFree(mig);
-    return NULL;
-}
-
-
-static int
-qemuMigrationCookieAddGraphics(qemuMigrationCookiePtr mig,
-                               virQEMUDriverPtr driver,
-                               virDomainObjPtr dom)
-{
-    size_t i = 0;
-
-    if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Migration graphics data already present"));
-        return -1;
-    }
-
-    for (i = 0; i < dom->def->ngraphics; i++) {
-        if (dom->def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
-            virDomainGraphicsListenDefPtr glisten =
-                virDomainGraphicsGetListen(dom->def->graphics[i], 0);
-
-            if (!glisten) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("missing listen element"));
-                return -1;
-            }
-
-            switch (glisten->type) {
-            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
-            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
-                /* Seamless migration is supported only for listen types
-                 * 'address and 'network'. */
-                if (!(mig->graphics =
-                      qemuMigrationCookieGraphicsSpiceAlloc(driver,
-                                                            dom->def->graphics[i],
-                                                            glisten)))
-                    return -1;
-                mig->flags |= QEMU_MIGRATION_COOKIE_GRAPHICS;
-                break;
-
-            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET:
-            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
-            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
-                break;
-            }
-
-            /* Seamless migration is supported only for one graphics. */
-            if (mig->graphics)
-                break;
-        }
-    }
-
-    return 0;
-}
-
-
-static int
-qemuMigrationCookieAddLockstate(qemuMigrationCookiePtr mig,
-                                virQEMUDriverPtr driver,
-                                virDomainObjPtr dom)
-{
-    qemuDomainObjPrivatePtr priv = dom->privateData;
-
-    if (mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Migration lockstate data already present"));
-        return -1;
-    }
-
-    if (virDomainObjGetState(dom, NULL) == VIR_DOMAIN_PAUSED) {
-        if (VIR_STRDUP(mig->lockState, priv->lockState) < 0)
-            return -1;
-    } else {
-        if (virDomainLockProcessInquire(driver->lockManager, dom, &mig->lockState) < 0)
-            return -1;
-    }
-
-    if (VIR_STRDUP(mig->lockDriver, virLockManagerPluginGetName(driver->lockManager)) < 0) {
-        VIR_FREE(mig->lockState);
-        return -1;
-    }
-
-    mig->flags |= QEMU_MIGRATION_COOKIE_LOCKSTATE;
-    mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_LOCKSTATE;
-
-    return 0;
-}
-
-
-static int
-qemuMigrationCookieAddPersistent(qemuMigrationCookiePtr mig,
-                                 virDomainDefPtr def)
-{
-    if (mig->flags & QEMU_MIGRATION_COOKIE_PERSISTENT) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Migration persistent data already present"));
-        return -1;
-    }
-
-    if (!def)
-        return 0;
-
-    mig->persistent = def;
-    mig->flags |= QEMU_MIGRATION_COOKIE_PERSISTENT;
-    mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_PERSISTENT;
-    return 0;
-}
-
-
-static virDomainDefPtr
-qemuMigrationCookieGetPersistent(qemuMigrationCookiePtr mig)
-{
-    virDomainDefPtr def = mig->persistent;
-
-    mig->persistent = NULL;
-    mig->flags &= ~QEMU_MIGRATION_COOKIE_PERSISTENT;
-    mig->flagsMandatory &= ~QEMU_MIGRATION_COOKIE_PERSISTENT;
-
-    return def;
-}
-
-
-static int
-qemuMigrationCookieAddNetwork(qemuMigrationCookiePtr mig,
-                              virQEMUDriverPtr driver,
-                              virDomainObjPtr dom)
-{
-    if (mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("Network migration data already present"));
-        return -1;
-    }
-
-    if (dom->def->nnets > 0) {
-        mig->network = qemuMigrationCookieNetworkAlloc(driver, dom->def);
-        if (!mig->network)
-            return -1;
-        mig->flags |= QEMU_MIGRATION_COOKIE_NETWORK;
-    }
-
-    return 0;
-}
-
-
-static int
-qemuMigrationCookieAddNBD(qemuMigrationCookiePtr mig,
-                          virQEMUDriverPtr driver,
-                          virDomainObjPtr vm)
-{
-    qemuDomainObjPrivatePtr priv = vm->privateData;
-    virHashTablePtr stats = NULL;
-    size_t i;
-    int ret = -1, rc;
-
-    /* It is not a bug if there already is a NBD data */
-    qemuMigrationCookieNBDFree(mig->nbd);
-
-    if (VIR_ALLOC(mig->nbd) < 0)
-        return -1;
-
-    if (vm->def->ndisks &&
-        VIR_ALLOC_N(mig->nbd->disks, vm->def->ndisks) < 0)
-        return -1;
-    mig->nbd->ndisks = 0;
-
-    for (i = 0; i < vm->def->ndisks; i++) {
-        virDomainDiskDefPtr disk = vm->def->disks[i];
-        qemuBlockStats *entry;
-
-        if (!stats) {
-            if (!(stats = virHashCreate(10, virHashValueFree)))
-                goto cleanup;
-
-            if (qemuDomainObjEnterMonitorAsync(driver, vm,
-                                               priv->job.asyncJob) < 0)
-                goto cleanup;
-            rc = qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats, false);
-            if (qemuDomainObjExitMonitor(driver, vm) < 0)
-                goto cleanup;
-            if (rc < 0)
-                goto cleanup;
-        }
-
-        if (!disk->info.alias ||
-            !(entry = virHashLookup(stats, disk->info.alias)))
-            continue;
-
-        if (VIR_STRDUP(mig->nbd->disks[mig->nbd->ndisks].target,
-                       disk->dst) < 0)
-            goto cleanup;
-        mig->nbd->disks[mig->nbd->ndisks].capacity = entry->capacity;
-        mig->nbd->ndisks++;
-    }
-
-    mig->nbd->port = priv->nbdPort;
-    mig->flags |= QEMU_MIGRATION_COOKIE_NBD;
-
-    ret = 0;
- cleanup:
-    virHashFree(stats);
-    return ret;
-}
-
-
-static int
-qemuMigrationCookieAddStatistics(qemuMigrationCookiePtr mig,
-                                 virDomainObjPtr vm)
-{
-    qemuDomainObjPrivatePtr priv = vm->privateData;
-
-    if (!priv->job.completed)
-        return 0;
-
-    if (!mig->jobInfo && VIR_ALLOC(mig->jobInfo) < 0)
-        return -1;
-
-    *mig->jobInfo = *priv->job.completed;
-    mig->flags |= QEMU_MIGRATION_COOKIE_STATS;
-
-    return 0;
-}
-
-
-static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
-                                                 qemuMigrationCookieGraphicsPtr grap)
-{
-    virBufferAsprintf(buf, "<graphics type='%s' port='%d' listen='%s'",
-                      virDomainGraphicsTypeToString(grap->type),
-                      grap->port, grap->listen);
-    if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
-        virBufferAsprintf(buf, " tlsPort='%d'", grap->tlsPort);
-    if (grap->tlsSubject) {
-        virBufferAddLit(buf, ">\n");
-        virBufferAdjustIndent(buf, 2);
-        virBufferEscapeString(buf, "<cert info='subject' value='%s'/>\n", grap->tlsSubject);
-        virBufferAdjustIndent(buf, -2);
-        virBufferAddLit(buf, "</graphics>\n");
-    } else {
-        virBufferAddLit(buf, "/>\n");
-    }
-}
-
-
-static void
-qemuMigrationCookieNetworkXMLFormat(virBufferPtr buf,
-                                    qemuMigrationCookieNetworkPtr optr)
-{
-    size_t i;
-    bool empty = true;
-
-    for (i = 0; i < optr->nnets; i++) {
-        /* If optr->net[i].vporttype is not set, there is nothing to transfer */
-        if (optr->net[i].vporttype != VIR_NETDEV_VPORT_PROFILE_NONE) {
-            if (empty) {
-                virBufferAddLit(buf, "<network>\n");
-                virBufferAdjustIndent(buf, 2);
-                empty = false;
-            }
-            virBufferAsprintf(buf, "<interface index='%zu' vporttype='%s'",
-                              i, virNetDevVPortTypeToString(optr->net[i].vporttype));
-            if (optr->net[i].portdata) {
-                virBufferAddLit(buf, ">\n");
-                virBufferAdjustIndent(buf, 2);
-                virBufferEscapeString(buf, "<portdata>%s</portdata>\n",
-                                      optr->net[i].portdata);
-                virBufferAdjustIndent(buf, -2);
-                virBufferAddLit(buf, "</interface>\n");
-            } else {
-                virBufferAddLit(buf, "/>\n");
-            }
-        }
-    }
-    if (!empty) {
-        virBufferAdjustIndent(buf, -2);
-        virBufferAddLit(buf, "</network>\n");
-    }
-}
-
-
-static void
-qemuMigrationCookieStatisticsXMLFormat(virBufferPtr buf,
-                                       qemuDomainJobInfoPtr jobInfo)
-{
-    qemuMonitorMigrationStats *stats = &jobInfo->stats;
-
-    virBufferAddLit(buf, "<statistics>\n");
-    virBufferAdjustIndent(buf, 2);
-
-    virBufferAsprintf(buf, "<started>%llu</started>\n", jobInfo->started);
-    virBufferAsprintf(buf, "<stopped>%llu</stopped>\n", jobInfo->stopped);
-    virBufferAsprintf(buf, "<sent>%llu</sent>\n", jobInfo->sent);
-    if (jobInfo->timeDeltaSet)
-        virBufferAsprintf(buf, "<delta>%lld</delta>\n", jobInfo->timeDelta);
-
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_TIME_ELAPSED,
-                      jobInfo->timeElapsed);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_TIME_REMAINING,
-                      jobInfo->timeRemaining);
-    if (stats->downtime_set)
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_DOWNTIME,
-                          stats->downtime);
-    if (stats->setup_time_set)
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_SETUP_TIME,
-                          stats->setup_time);
-
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_MEMORY_TOTAL,
-                      stats->ram_total);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_MEMORY_PROCESSED,
-                      stats->ram_transferred);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_MEMORY_REMAINING,
-                      stats->ram_remaining);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_MEMORY_BPS,
-                      stats->ram_bps);
-
-    if (stats->ram_duplicate_set) {
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_MEMORY_CONSTANT,
-                          stats->ram_duplicate);
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_MEMORY_NORMAL,
-                          stats->ram_normal);
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
-                          stats->ram_normal_bytes);
-    }
-
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
-                      stats->ram_dirty_rate);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_MEMORY_ITERATION,
-                      stats->ram_iteration);
-
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_DISK_TOTAL,
-                      stats->disk_total);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_DISK_PROCESSED,
-                      stats->disk_transferred);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_DISK_REMAINING,
-                      stats->disk_remaining);
-    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                      VIR_DOMAIN_JOB_DISK_BPS,
-                      stats->disk_bps);
-
-    if (stats->xbzrle_set) {
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_COMPRESSION_CACHE,
-                          stats->xbzrle_cache_size);
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_COMPRESSION_BYTES,
-                          stats->xbzrle_bytes);
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_COMPRESSION_PAGES,
-                          stats->xbzrle_pages);
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
-                          stats->xbzrle_cache_miss);
-        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
-                          VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
-                          stats->xbzrle_overflow);
-    }
-
-    virBufferAsprintf(buf, "<%1$s>%2$d</%1$s>\n",
-                      VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
-                      stats->cpu_throttle_percentage);
-
-    virBufferAdjustIndent(buf, -2);
-    virBufferAddLit(buf, "</statistics>\n");
-}
-
-
-static int
-qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver,
-                             virBufferPtr buf,
-                             qemuMigrationCookiePtr mig)
-{
-    char uuidstr[VIR_UUID_STRING_BUFLEN];
-    char hostuuidstr[VIR_UUID_STRING_BUFLEN];
-    size_t i;
-
-    virUUIDFormat(mig->uuid, uuidstr);
-    virUUIDFormat(mig->localHostuuid, hostuuidstr);
-
-    virBufferAddLit(buf, "<qemu-migration>\n");
-    virBufferAdjustIndent(buf, 2);
-    virBufferEscapeString(buf, "<name>%s</name>\n", mig->name);
-    virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
-    virBufferEscapeString(buf, "<hostname>%s</hostname>\n", mig->localHostname);
-    virBufferAsprintf(buf, "<hostuuid>%s</hostuuid>\n", hostuuidstr);
-
-    for (i = 0; i < QEMU_MIGRATION_COOKIE_FLAG_LAST; i++) {
-        if (mig->flagsMandatory & (1 << i))
-            virBufferAsprintf(buf, "<feature name='%s'/>\n",
-                              qemuMigrationCookieFlagTypeToString(i));
-    }
-
-    if ((mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
-        mig->graphics)
-        qemuMigrationCookieGraphicsXMLFormat(buf, mig->graphics);
-
-    if ((mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) &&
-        mig->lockState) {
-        virBufferAsprintf(buf, "<lockstate driver='%s'>\n",
-                          mig->lockDriver);
-        virBufferAdjustIndent(buf, 2);
-        virBufferAsprintf(buf, "<leases>%s</leases>\n",
-                          mig->lockState);
-        virBufferAdjustIndent(buf, -2);
-        virBufferAddLit(buf, "</lockstate>\n");
-    }
-
-    if ((mig->flags & QEMU_MIGRATION_COOKIE_PERSISTENT) &&
-        mig->persistent) {
-        if (qemuDomainDefFormatBuf(driver,
-                                   mig->persistent,
-                                   VIR_DOMAIN_XML_INACTIVE |
-                                   VIR_DOMAIN_XML_SECURE |
-                                   VIR_DOMAIN_XML_MIGRATABLE,
-                                   buf) < 0)
-            return -1;
-    }
-
-    if ((mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) && mig->network)
-        qemuMigrationCookieNetworkXMLFormat(buf, mig->network);
-
-    if ((mig->flags & QEMU_MIGRATION_COOKIE_NBD) && mig->nbd) {
-        virBufferAddLit(buf, "<nbd");
-        if (mig->nbd->port)
-            virBufferAsprintf(buf, " port='%d'", mig->nbd->port);
-        if (mig->nbd->ndisks) {
-            virBufferAddLit(buf, ">\n");
-            virBufferAdjustIndent(buf, 2);
-            for (i = 0; i < mig->nbd->ndisks; i++) {
-                virBufferEscapeString(buf, "<disk target='%s'",
-                                      mig->nbd->disks[i].target);
-                virBufferAsprintf(buf, " capacity='%llu'/>\n",
-                                  mig->nbd->disks[i].capacity);
-            }
-            virBufferAdjustIndent(buf, -2);
-            virBufferAddLit(buf, "</nbd>\n");
-        } else {
-            virBufferAddLit(buf, "/>\n");
-        }
-    }
-
-    if (mig->flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo)
-        qemuMigrationCookieStatisticsXMLFormat(buf, mig->jobInfo);
-
-    virBufferAdjustIndent(buf, -2);
-    virBufferAddLit(buf, "</qemu-migration>\n");
-    return 0;
-}
-
-
-static char *qemuMigrationCookieXMLFormatStr(virQEMUDriverPtr driver,
-                                             qemuMigrationCookiePtr mig)
-{
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-
-    if (qemuMigrationCookieXMLFormat(driver, &buf, mig) < 0) {
-        virBufferFreeAndReset(&buf);
-        return NULL;
-    }
-
-    if (virBufferCheckError(&buf) < 0)
-        return NULL;
-
-    return virBufferContentAndReset(&buf);
-}
-
-
-static qemuMigrationCookieGraphicsPtr
-qemuMigrationCookieGraphicsXMLParse(xmlXPathContextPtr ctxt)
-{
-    qemuMigrationCookieGraphicsPtr grap;
-    char *tmp;
-
-    if (VIR_ALLOC(grap) < 0)
-        goto error;
-
-    if (!(tmp = virXPathString("string(./graphics/@type)", ctxt))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing type attribute in migration data"));
-        goto error;
-    }
-    if ((grap->type = virDomainGraphicsTypeFromString(tmp)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unknown graphics type %s"), tmp);
-        VIR_FREE(tmp);
-        goto error;
-    }
-    VIR_FREE(tmp);
-    if (virXPathInt("string(./graphics/@port)", ctxt, &grap->port) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing port attribute in migration data"));
-        goto error;
-    }
-    if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
-        if (virXPathInt("string(./graphics/@tlsPort)", ctxt, &grap->tlsPort) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("missing tlsPort attribute in migration data"));
-            goto error;
-        }
-    }
-    if (!(grap->listen = virXPathString("string(./graphics/@listen)", ctxt))) {
-        virReportError(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;
-
- error:
-    qemuMigrationCookieGraphicsFree(grap);
-    return NULL;
-}
-
-
-static qemuMigrationCookieNetworkPtr
-qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)
-{
-    qemuMigrationCookieNetworkPtr optr;
-    size_t i;
-    int n;
-    xmlNodePtr *interfaces = NULL;
-    char *vporttype;
-    xmlNodePtr save_ctxt = ctxt->node;
-
-    if (VIR_ALLOC(optr) < 0)
-        goto error;
-
-    if ((n = virXPathNodeSet("./network/interface", ctxt, &interfaces)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing interface information"));
-        goto error;
-    }
-
-    optr->nnets = n;
-    if (VIR_ALLOC_N(optr->net, optr->nnets) < 0)
-        goto error;
-
-    for (i = 0; i < n; i++) {
-        /* portdata is optional, and may not exist */
-        ctxt->node = interfaces[i];
-        optr->net[i].portdata = virXPathString("string(./portdata[1])", ctxt);
-
-        if (!(vporttype = virXMLPropString(interfaces[i], "vporttype"))) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("missing vporttype attribute in migration data"));
-            goto error;
-        }
-        optr->net[i].vporttype = virNetDevVPortTypeFromString(vporttype);
-    }
-
-    VIR_FREE(interfaces);
-
- cleanup:
-    ctxt->node = save_ctxt;
-    return optr;
-
- error:
-    VIR_FREE(interfaces);
-    qemuMigrationCookieNetworkFree(optr);
-    optr = NULL;
-    goto cleanup;
-}
-
-
-static qemuMigrationCookieNBDPtr
-qemuMigrationCookieNBDXMLParse(xmlXPathContextPtr ctxt)
-{
-    qemuMigrationCookieNBDPtr ret = NULL;
-    char *port = NULL, *capacity = NULL;
-    size_t i;
-    int n;
-    xmlNodePtr *disks = NULL;
-    xmlNodePtr save_ctxt = ctxt->node;
-
-    if (VIR_ALLOC(ret) < 0)
-        goto error;
-
-    port = virXPathString("string(./nbd/@port)", ctxt);
-    if (port && virStrToLong_i(port, NULL, 10, &ret->port) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Malformed nbd port '%s'"),
-                       port);
-        goto error;
-    }
-
-    /* Now check if source sent a list of disks to prealloc. We might be
-     * talking to an older server, so it's not an error if the list is
-     * missing. */
-    if ((n = virXPathNodeSet("./nbd/disk", ctxt, &disks)) > 0) {
-        if (VIR_ALLOC_N(ret->disks, n) < 0)
-            goto error;
-        ret->ndisks = n;
-
-        for (i = 0; i < n; i++) {
-            ctxt->node = disks[i];
-            VIR_FREE(capacity);
-
-            if (!(ret->disks[i].target = virXPathString("string(./@target)", ctxt))) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("Malformed disk target"));
-                goto error;
-            }
-
-            capacity = virXPathString("string(./@capacity)", ctxt);
-            if (!capacity ||
-                virStrToLong_ull(capacity, NULL, 10,
-                                 &ret->disks[i].capacity) < 0) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("Malformed disk capacity: '%s'"),
-                               NULLSTR(capacity));
-                goto error;
-            }
-        }
-    }
-
- cleanup:
-    VIR_FREE(port);
-    VIR_FREE(capacity);
-    VIR_FREE(disks);
-    ctxt->node = save_ctxt;
-    return ret;
- error:
-    qemuMigrationCookieNBDFree(ret);
-    ret = NULL;
-    goto cleanup;
-}
-
-
-static qemuDomainJobInfoPtr
-qemuMigrationCookieStatisticsXMLParse(xmlXPathContextPtr ctxt)
-{
-    qemuDomainJobInfoPtr jobInfo = NULL;
-    qemuMonitorMigrationStats *stats;
-    xmlNodePtr save_ctxt = ctxt->node;
-
-    if (!(ctxt->node = virXPathNode("./statistics", ctxt)))
-        goto cleanup;
-
-    if (VIR_ALLOC(jobInfo) < 0)
-        goto cleanup;
-
-    stats = &jobInfo->stats;
-    jobInfo->type = VIR_DOMAIN_JOB_COMPLETED;
-
-    virXPathULongLong("string(./started[1])", ctxt, &jobInfo->started);
-    virXPathULongLong("string(./stopped[1])", ctxt, &jobInfo->stopped);
-    virXPathULongLong("string(./sent[1])", ctxt, &jobInfo->sent);
-    if (virXPathLongLong("string(./delta[1])", ctxt, &jobInfo->timeDelta) == 0)
-        jobInfo->timeDeltaSet = true;
-
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_TIME_ELAPSED "[1])",
-                      ctxt, &jobInfo->timeElapsed);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_TIME_REMAINING "[1])",
-                      ctxt, &jobInfo->timeRemaining);
-
-    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_DOWNTIME "[1])",
-                          ctxt, &stats->downtime) == 0)
-        stats->downtime_set = true;
-    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_SETUP_TIME "[1])",
-                          ctxt, &stats->setup_time) == 0)
-        stats->setup_time_set = true;
-
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_TOTAL "[1])",
-                      ctxt, &stats->ram_total);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_PROCESSED "[1])",
-                      ctxt, &stats->ram_transferred);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_REMAINING "[1])",
-                      ctxt, &stats->ram_remaining);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_BPS "[1])",
-                      ctxt, &stats->ram_bps);
-
-    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_CONSTANT "[1])",
-                          ctxt, &stats->ram_duplicate) == 0)
-        stats->ram_duplicate_set = true;
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_NORMAL "[1])",
-                      ctxt, &stats->ram_normal);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES "[1])",
-                      ctxt, &stats->ram_normal_bytes);
-
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE "[1])",
-                      ctxt, &stats->ram_dirty_rate);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_ITERATION "[1])",
-                      ctxt, &stats->ram_iteration);
-
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_TOTAL "[1])",
-                      ctxt, &stats->disk_total);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_PROCESSED "[1])",
-                      ctxt, &stats->disk_transferred);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_REMAINING "[1])",
-                      ctxt, &stats->disk_remaining);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_BPS "[1])",
-                      ctxt, &stats->disk_bps);
-
-    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_CACHE "[1])",
-                          ctxt, &stats->xbzrle_cache_size) == 0)
-        stats->xbzrle_set = true;
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_BYTES "[1])",
-                      ctxt, &stats->xbzrle_bytes);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_PAGES "[1])",
-                      ctxt, &stats->xbzrle_pages);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES "[1])",
-                      ctxt, &stats->xbzrle_cache_miss);
-    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW "[1])",
-                      ctxt, &stats->xbzrle_overflow);
-
-    virXPathInt("string(./" VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE "[1])",
-                ctxt, &stats->cpu_throttle_percentage);
- cleanup:
-    ctxt->node = save_ctxt;
-    return jobInfo;
-}
-
-
-static int
-qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
-                            virQEMUDriverPtr driver,
-                            xmlDocPtr doc,
-                            xmlXPathContextPtr ctxt,
-                            unsigned int flags)
-{
-    char uuidstr[VIR_UUID_STRING_BUFLEN];
-    char *tmp = NULL;
-    xmlNodePtr *nodes = NULL;
-    size_t i;
-    int n;
-    virCapsPtr caps = NULL;
-
-    if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
-        goto error;
-
-    /* We don't store the uuid, name, hostname, or hostuuid
-     * values. We just compare them to local data to do some
-     * sanity checking on migration operation
-     */
-
-    /* Extract domain name */
-    if (!(tmp = virXPathString("string(./name[1])", ctxt))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing name element in migration data"));
-        goto error;
-    }
-    if (STRNEQ(tmp, mig->name)) {
-        virReportError(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) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing uuid element in migration data"));
-        goto error;
-    }
-    virUUIDFormat(mig->uuid, uuidstr);
-    if (STRNEQ(tmp, uuidstr)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Incoming cookie data had unexpected UUID %s vs %s"),
-                       tmp, uuidstr);
-        goto error;
-    }
-    VIR_FREE(tmp);
-
-    /* Check & forbid "localhost" migration */
-    if (!(mig->remoteHostname = virXPathString("string(./hostname[1])", ctxt))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing hostname element in migration data"));
-        goto error;
-    }
-    if (STREQ(mig->remoteHostname, mig->localHostname)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Attempt to migrate guest to the same host %s"),
-                       mig->remoteHostname);
-        goto error;
-    }
-
-    if (!(tmp = virXPathString("string(./hostuuid[1])", ctxt))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("missing hostuuid element in migration data"));
-        goto error;
-    }
-    if (virUUIDParse(tmp, mig->remoteHostuuid) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       "%s", _("malformed hostuuid element in migration data"));
-        goto error;
-    }
-    if (memcmp(mig->remoteHostuuid, mig->localHostuuid, VIR_UUID_BUFLEN) == 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Attempt to migrate guest to the same host %s"),
-                       tmp);
-        goto error;
-    }
-    VIR_FREE(tmp);
-
-    /* Check to ensure all mandatory features from XML are also
-     * present in 'flags' */
-    if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0)
-        goto error;
-
-    for (i = 0; i < n; i++) {
-        int val;
-        char *str = virXMLPropString(nodes[i], "name");
-        if (!str) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           "%s", _("missing feature name"));
-            goto error;
-        }
-
-        if ((val = qemuMigrationCookieFlagTypeFromString(str)) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Unknown migration cookie feature %s"),
-                           str);
-            VIR_FREE(str);
-            goto error;
-        }
-
-        if ((flags & (1 << val)) == 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Unsupported migration cookie feature %s"),
-                           str);
-            VIR_FREE(str);
-            goto error;
-        }
-        VIR_FREE(str);
-    }
-    VIR_FREE(nodes);
-
-    if ((flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
-        virXPathBoolean("count(./graphics) > 0", ctxt) &&
-        (!(mig->graphics = qemuMigrationCookieGraphicsXMLParse(ctxt))))
-        goto error;
-
-    if ((flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) &&
-        virXPathBoolean("count(./lockstate) > 0", ctxt)) {
-        mig->lockDriver = virXPathString("string(./lockstate[1]/@driver)", ctxt);
-        if (!mig->lockDriver) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("Missing lock driver name in migration cookie"));
-            goto error;
-        }
-        mig->lockState = virXPathString("string(./lockstate[1]/leases[1])", ctxt);
-        if (mig->lockState && STREQ(mig->lockState, ""))
-            VIR_FREE(mig->lockState);
-    }
-
-    if ((flags & QEMU_MIGRATION_COOKIE_PERSISTENT) &&
-        virXPathBoolean("count(./domain) > 0", ctxt)) {
-        if ((n = virXPathNodeSet("./domain", ctxt, &nodes)) > 1) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Too many domain elements in "
-                             "migration cookie: %d"),
-                           n);
-            goto error;
-        }
-        mig->persistent = virDomainDefParseNode(doc, nodes[0],
-                                                caps, driver->xmlopt, NULL,
-                                                VIR_DOMAIN_DEF_PARSE_INACTIVE |
-                                                VIR_DOMAIN_DEF_PARSE_ABI_UPDATE |
-                                                VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
-        if (!mig->persistent) {
-            /* virDomainDefParseNode already reported
-             * an error for us */
-            goto error;
-        }
-        VIR_FREE(nodes);
-    }
-
-    if ((flags & QEMU_MIGRATION_COOKIE_NETWORK) &&
-        virXPathBoolean("count(./network) > 0", ctxt) &&
-        (!(mig->network = qemuMigrationCookieNetworkXMLParse(ctxt))))
-        goto error;
-
-    if (flags & QEMU_MIGRATION_COOKIE_NBD &&
-        virXPathBoolean("boolean(./nbd)", ctxt) &&
-        (!(mig->nbd = qemuMigrationCookieNBDXMLParse(ctxt))))
-        goto error;
-
-    if (flags & QEMU_MIGRATION_COOKIE_STATS &&
-        virXPathBoolean("boolean(./statistics)", ctxt) &&
-        (!(mig->jobInfo = qemuMigrationCookieStatisticsXMLParse(ctxt))))
-        goto error;
-
-    virObjectUnref(caps);
-    return 0;
-
- error:
-    VIR_FREE(tmp);
-    VIR_FREE(nodes);
-    virObjectUnref(caps);
-    return -1;
-}
-
-
-static int
-qemuMigrationCookieXMLParseStr(qemuMigrationCookiePtr mig,
-                               virQEMUDriverPtr driver,
-                               const char *xml,
-                               unsigned int flags)
-{
-    xmlDocPtr doc = NULL;
-    xmlXPathContextPtr ctxt = NULL;
-    int ret = -1;
-
-    VIR_DEBUG("xml=%s", NULLSTR(xml));
-
-    if (!(doc = virXMLParseStringCtxt(xml, _("(qemu_migration_cookie)"), &ctxt)))
-        goto cleanup;
-
-    ret = qemuMigrationCookieXMLParse(mig, driver, doc, ctxt, flags);
-
- cleanup:
-    xmlXPathFreeContext(ctxt);
-    xmlFreeDoc(doc);
-
-    return ret;
-}
-
-
-static int
-qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
-                        virQEMUDriverPtr driver,
-                        virDomainObjPtr dom,
-                        char **cookieout,
-                        int *cookieoutlen,
-                        unsigned int flags)
-{
-    if (!cookieout || !cookieoutlen)
-        return 0;
-
-    *cookieoutlen = 0;
-
-    if (flags & QEMU_MIGRATION_COOKIE_GRAPHICS &&
-        qemuMigrationCookieAddGraphics(mig, driver, dom) < 0)
-        return -1;
-
-    if (flags & QEMU_MIGRATION_COOKIE_LOCKSTATE &&
-        qemuMigrationCookieAddLockstate(mig, driver, dom) < 0)
-        return -1;
-
-    if (flags & QEMU_MIGRATION_COOKIE_NETWORK &&
-        qemuMigrationCookieAddNetwork(mig, driver, dom) < 0) {
-        return -1;
-    }
-
-    if ((flags & QEMU_MIGRATION_COOKIE_NBD) &&
-        qemuMigrationCookieAddNBD(mig, driver, dom) < 0)
-        return -1;
-
-    if (flags & QEMU_MIGRATION_COOKIE_STATS &&
-        qemuMigrationCookieAddStatistics(mig, dom) < 0)
-        return -1;
-
-    if (flags & QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG)
-        mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG;
-
-    if (flags & QEMU_MIGRATION_COOKIE_CPU_HOTPLUG)
-        mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_CPU_HOTPLUG;
-
-    if (!(*cookieout = qemuMigrationCookieXMLFormatStr(driver, mig)))
-        return -1;
-
-    *cookieoutlen = strlen(*cookieout) + 1;
-
-    VIR_DEBUG("cookielen=%d cookie=%s", *cookieoutlen, *cookieout);
-
-    return 0;
-}
-
-
-static qemuMigrationCookiePtr
-qemuMigrationEatCookie(virQEMUDriverPtr driver,
-                       virDomainObjPtr dom,
-                       const char *cookiein,
-                       int cookieinlen,
-                       unsigned int flags)
-{
-    qemuMigrationCookiePtr mig = NULL;
-
-    /* Parse & validate incoming cookie (if any) */
-    if (cookiein && cookieinlen &&
-        cookiein[cookieinlen-1] != '\0') {
-        virReportError(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,
-                                       driver,
-                                       cookiein,
-                                       flags) < 0)
-        goto error;
-
-    if (flags & QEMU_MIGRATION_COOKIE_PERSISTENT &&
-        mig->persistent &&
-        STRNEQ(dom->def->name, mig->persistent->name)) {
-        VIR_FREE(mig->persistent->name);
-        if (VIR_STRDUP(mig->persistent->name, dom->def->name) < 0)
-            goto error;
-    }
-
-    if (mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) {
-        if (!mig->lockDriver) {
-            if (virLockManagerPluginUsesState(driver->lockManager)) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("Missing %s lock state for migration cookie"),
-                               virLockManagerPluginGetName(driver->lockManager));
-                goto error;
-            }
-        } else if (STRNEQ(mig->lockDriver,
-                          virLockManagerPluginGetName(driver->lockManager))) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Source host lock driver %s different from target %s"),
-                           mig->lockDriver,
-                           virLockManagerPluginGetName(driver->lockManager));
-            goto error;
-        }
-    }
-
-    return mig;
-
- error:
-    qemuMigrationCookieFree(mig);
-    return NULL;
-}
 
 static void
 qemuMigrationStoreDomainState(virDomainObjPtr vm)
diff --git a/src/qemu/qemu_migration_cookie.c b/src/qemu/qemu_migration_cookie.c
new file mode 100644
index 000000000..7b237c546
--- /dev/null
+++ b/src/qemu/qemu_migration_cookie.c
@@ -0,0 +1,1340 @@
+/*
+ * qemu_migration_cookie.c: QEMU migration cookie handling
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#ifdef WITH_GNUTLS
+# include <gnutls/gnutls.h>
+# include <gnutls/x509.h>
+#endif
+
+#include "locking/domain_lock.h"
+#include "viralloc.h"
+#include "virerror.h"
+#include "virlog.h"
+#include "virnetdevopenvswitch.h"
+#include "virstring.h"
+#include "virutil.h"
+
+#include "qemu_domain.h"
+#include "qemu_migration_cookie.h"
+
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_LOG_INIT("qemu.qemu_migration_cookie");
+
+VIR_ENUM_IMPL(qemuMigrationCookieFlag,
+              QEMU_MIGRATION_COOKIE_FLAG_LAST,
+              "graphics",
+              "lockstate",
+              "persistent",
+              "network",
+              "nbd",
+              "statistics",
+              "memory-hotplug",
+              "cpu-hotplug");
+
+
+static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
+{
+    if (!grap)
+        return;
+    VIR_FREE(grap->listen);
+    VIR_FREE(grap->tlsSubject);
+    VIR_FREE(grap);
+}
+
+
+static void
+qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr network)
+{
+    size_t i;
+
+    if (!network)
+        return;
+
+    if (network->net) {
+        for (i = 0; i < network->nnets; i++)
+            VIR_FREE(network->net[i].portdata);
+    }
+    VIR_FREE(network->net);
+    VIR_FREE(network);
+}
+
+
+static void qemuMigrationCookieNBDFree(qemuMigrationCookieNBDPtr nbd)
+{
+    if (!nbd)
+        return;
+
+    while (nbd->ndisks)
+        VIR_FREE(nbd->disks[--nbd->ndisks].target);
+    VIR_FREE(nbd->disks);
+    VIR_FREE(nbd);
+}
+
+
+void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
+{
+    if (!mig)
+        return;
+
+    qemuMigrationCookieGraphicsFree(mig->graphics);
+    qemuMigrationCookieNetworkFree(mig->network);
+    qemuMigrationCookieNBDFree(mig->nbd);
+
+    VIR_FREE(mig->localHostname);
+    VIR_FREE(mig->remoteHostname);
+    VIR_FREE(mig->name);
+    VIR_FREE(mig->lockState);
+    VIR_FREE(mig->lockDriver);
+    VIR_FREE(mig->jobInfo);
+    VIR_FREE(mig);
+}
+
+
+#ifdef WITH_GNUTLS
+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 error;
+
+    if (virFileReadAll(certfile, 8192, &pemdata) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unable to read server cert %s"), certfile);
+        goto error;
+    }
+
+    ret = gnutls_x509_crt_init(&cert);
+    if (ret < 0) {
+        virReportError(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) {
+        virReportError(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 error;
+
+    gnutls_x509_crt_get_dn(cert, subject, &subjectlen);
+    subject[subjectlen] = '\0';
+
+    VIR_FREE(certfile);
+    VIR_FREE(pemdata);
+
+    return subject;
+
+ error:
+    VIR_FREE(certfile);
+    VIR_FREE(pemdata);
+    return NULL;
+}
+#endif
+
+static qemuMigrationCookieGraphicsPtr
+qemuMigrationCookieGraphicsSpiceAlloc(virQEMUDriverPtr driver,
+                                      virDomainGraphicsDefPtr def,
+                                      virDomainGraphicsListenDefPtr glisten)
+{
+    qemuMigrationCookieGraphicsPtr mig = NULL;
+    const char *listenAddr;
+    virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
+
+    if (VIR_ALLOC(mig) < 0)
+        goto error;
+
+    mig->type = VIR_DOMAIN_GRAPHICS_TYPE_SPICE;
+    mig->port = def->data.spice.port;
+    if (cfg->spiceTLS)
+        mig->tlsPort = def->data.spice.tlsPort;
+    else
+        mig->tlsPort = -1;
+
+    if (!glisten || !(listenAddr = glisten->address))
+        listenAddr = cfg->spiceListen;
+
+#ifdef WITH_GNUTLS
+    if (cfg->spiceTLS &&
+        !(mig->tlsSubject = qemuDomainExtractTLSSubject(cfg->spiceTLSx509certdir)))
+        goto error;
+#endif
+    if (VIR_STRDUP(mig->listen, listenAddr) < 0)
+        goto error;
+
+    virObjectUnref(cfg);
+    return mig;
+
+ error:
+    qemuMigrationCookieGraphicsFree(mig);
+    virObjectUnref(cfg);
+    return NULL;
+}
+
+
+static qemuMigrationCookieNetworkPtr
+qemuMigrationCookieNetworkAlloc(virQEMUDriverPtr driver ATTRIBUTE_UNUSED,
+                                virDomainDefPtr def)
+{
+    qemuMigrationCookieNetworkPtr mig;
+    size_t i;
+
+    if (VIR_ALLOC(mig) < 0)
+        goto error;
+
+    mig->nnets = def->nnets;
+
+    if (VIR_ALLOC_N(mig->net, def->nnets) <0)
+        goto error;
+
+    for (i = 0; i < def->nnets; i++) {
+        virDomainNetDefPtr netptr;
+        virNetDevVPortProfilePtr vport;
+
+        netptr = def->nets[i];
+        vport = virDomainNetGetActualVirtPortProfile(netptr);
+
+        if (vport) {
+            mig->net[i].vporttype = vport->virtPortType;
+
+            switch (vport->virtPortType) {
+            case VIR_NETDEV_VPORT_PROFILE_NONE:
+            case VIR_NETDEV_VPORT_PROFILE_8021QBG:
+            case VIR_NETDEV_VPORT_PROFILE_8021QBH:
+               break;
+            case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
+                if (virNetDevOpenvswitchGetMigrateData(&mig->net[i].portdata,
+                                                       netptr->ifname) != 0) {
+                        virReportError(VIR_ERR_INTERNAL_ERROR,
+                                       _("Unable to run command to get OVS port data for "
+                                         "interface %s"), netptr->ifname);
+                        goto error;
+                }
+                break;
+            default:
+                break;
+            }
+        }
+    }
+    return mig;
+
+ error:
+    qemuMigrationCookieNetworkFree(mig);
+    return NULL;
+}
+
+static qemuMigrationCookiePtr
+qemuMigrationCookieNew(virDomainObjPtr dom)
+{
+    qemuDomainObjPrivatePtr priv = dom->privateData;
+    qemuMigrationCookiePtr mig = NULL;
+    const char *name;
+
+    if (VIR_ALLOC(mig) < 0)
+        goto error;
+
+    if (priv->origname)
+        name = priv->origname;
+    else
+        name = dom->def->name;
+    if (VIR_STRDUP(mig->name, name) < 0)
+        goto error;
+    memcpy(mig->uuid, dom->def->uuid, VIR_UUID_BUFLEN);
+
+    if (!(mig->localHostname = virGetHostname()))
+        goto error;
+    if (virGetHostUUID(mig->localHostuuid) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Unable to obtain host UUID"));
+        goto error;
+    }
+
+    return mig;
+
+ error:
+    qemuMigrationCookieFree(mig);
+    return NULL;
+}
+
+
+static int
+qemuMigrationCookieAddGraphics(qemuMigrationCookiePtr mig,
+                               virQEMUDriverPtr driver,
+                               virDomainObjPtr dom)
+{
+    size_t i = 0;
+
+    if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Migration graphics data already present"));
+        return -1;
+    }
+
+    for (i = 0; i < dom->def->ngraphics; i++) {
+        if (dom->def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+            virDomainGraphicsListenDefPtr glisten =
+                virDomainGraphicsGetListen(dom->def->graphics[i], 0);
+
+            if (!glisten) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("missing listen element"));
+                return -1;
+            }
+
+            switch (glisten->type) {
+            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
+            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
+                /* Seamless migration is supported only for listen types
+                 * 'address and 'network'. */
+                if (!(mig->graphics =
+                      qemuMigrationCookieGraphicsSpiceAlloc(driver,
+                                                            dom->def->graphics[i],
+                                                            glisten)))
+                    return -1;
+                mig->flags |= QEMU_MIGRATION_COOKIE_GRAPHICS;
+                break;
+
+            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET:
+            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
+            case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
+                break;
+            }
+
+            /* Seamless migration is supported only for one graphics. */
+            if (mig->graphics)
+                break;
+        }
+    }
+
+    return 0;
+}
+
+
+static int
+qemuMigrationCookieAddLockstate(qemuMigrationCookiePtr mig,
+                                virQEMUDriverPtr driver,
+                                virDomainObjPtr dom)
+{
+    qemuDomainObjPrivatePtr priv = dom->privateData;
+
+    if (mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Migration lockstate data already present"));
+        return -1;
+    }
+
+    if (virDomainObjGetState(dom, NULL) == VIR_DOMAIN_PAUSED) {
+        if (VIR_STRDUP(mig->lockState, priv->lockState) < 0)
+            return -1;
+    } else {
+        if (virDomainLockProcessInquire(driver->lockManager, dom, &mig->lockState) < 0)
+            return -1;
+    }
+
+    if (VIR_STRDUP(mig->lockDriver, virLockManagerPluginGetName(driver->lockManager)) < 0) {
+        VIR_FREE(mig->lockState);
+        return -1;
+    }
+
+    mig->flags |= QEMU_MIGRATION_COOKIE_LOCKSTATE;
+    mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_LOCKSTATE;
+
+    return 0;
+}
+
+
+int
+qemuMigrationCookieAddPersistent(qemuMigrationCookiePtr mig,
+                                 virDomainDefPtr def)
+{
+    if (mig->flags & QEMU_MIGRATION_COOKIE_PERSISTENT) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Migration persistent data already present"));
+        return -1;
+    }
+
+    if (!def)
+        return 0;
+
+    mig->persistent = def;
+    mig->flags |= QEMU_MIGRATION_COOKIE_PERSISTENT;
+    mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_PERSISTENT;
+    return 0;
+}
+
+
+virDomainDefPtr
+qemuMigrationCookieGetPersistent(qemuMigrationCookiePtr mig)
+{
+    virDomainDefPtr def = mig->persistent;
+
+    mig->persistent = NULL;
+    mig->flags &= ~QEMU_MIGRATION_COOKIE_PERSISTENT;
+    mig->flagsMandatory &= ~QEMU_MIGRATION_COOKIE_PERSISTENT;
+
+    return def;
+}
+
+
+static int
+qemuMigrationCookieAddNetwork(qemuMigrationCookiePtr mig,
+                              virQEMUDriverPtr driver,
+                              virDomainObjPtr dom)
+{
+    if (mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("Network migration data already present"));
+        return -1;
+    }
+
+    if (dom->def->nnets > 0) {
+        mig->network = qemuMigrationCookieNetworkAlloc(driver, dom->def);
+        if (!mig->network)
+            return -1;
+        mig->flags |= QEMU_MIGRATION_COOKIE_NETWORK;
+    }
+
+    return 0;
+}
+
+
+static int
+qemuMigrationCookieAddNBD(qemuMigrationCookiePtr mig,
+                          virQEMUDriverPtr driver,
+                          virDomainObjPtr vm)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+    virHashTablePtr stats = NULL;
+    size_t i;
+    int ret = -1, rc;
+
+    /* It is not a bug if there already is a NBD data */
+    qemuMigrationCookieNBDFree(mig->nbd);
+
+    if (VIR_ALLOC(mig->nbd) < 0)
+        return -1;
+
+    if (vm->def->ndisks &&
+        VIR_ALLOC_N(mig->nbd->disks, vm->def->ndisks) < 0)
+        return -1;
+    mig->nbd->ndisks = 0;
+
+    for (i = 0; i < vm->def->ndisks; i++) {
+        virDomainDiskDefPtr disk = vm->def->disks[i];
+        qemuBlockStats *entry;
+
+        if (!stats) {
+            if (!(stats = virHashCreate(10, virHashValueFree)))
+                goto cleanup;
+
+            if (qemuDomainObjEnterMonitorAsync(driver, vm,
+                                               priv->job.asyncJob) < 0)
+                goto cleanup;
+            rc = qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats, false);
+            if (qemuDomainObjExitMonitor(driver, vm) < 0)
+                goto cleanup;
+            if (rc < 0)
+                goto cleanup;
+        }
+
+        if (!disk->info.alias ||
+            !(entry = virHashLookup(stats, disk->info.alias)))
+            continue;
+
+        if (VIR_STRDUP(mig->nbd->disks[mig->nbd->ndisks].target,
+                       disk->dst) < 0)
+            goto cleanup;
+        mig->nbd->disks[mig->nbd->ndisks].capacity = entry->capacity;
+        mig->nbd->ndisks++;
+    }
+
+    mig->nbd->port = priv->nbdPort;
+    mig->flags |= QEMU_MIGRATION_COOKIE_NBD;
+
+    ret = 0;
+ cleanup:
+    virHashFree(stats);
+    return ret;
+}
+
+
+static int
+qemuMigrationCookieAddStatistics(qemuMigrationCookiePtr mig,
+                                 virDomainObjPtr vm)
+{
+    qemuDomainObjPrivatePtr priv = vm->privateData;
+
+    if (!priv->job.completed)
+        return 0;
+
+    if (!mig->jobInfo && VIR_ALLOC(mig->jobInfo) < 0)
+        return -1;
+
+    *mig->jobInfo = *priv->job.completed;
+    mig->flags |= QEMU_MIGRATION_COOKIE_STATS;
+
+    return 0;
+}
+
+
+static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
+                                                 qemuMigrationCookieGraphicsPtr grap)
+{
+    virBufferAsprintf(buf, "<graphics type='%s' port='%d' listen='%s'",
+                      virDomainGraphicsTypeToString(grap->type),
+                      grap->port, grap->listen);
+    if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
+        virBufferAsprintf(buf, " tlsPort='%d'", grap->tlsPort);
+    if (grap->tlsSubject) {
+        virBufferAddLit(buf, ">\n");
+        virBufferAdjustIndent(buf, 2);
+        virBufferEscapeString(buf, "<cert info='subject' value='%s'/>\n", grap->tlsSubject);
+        virBufferAdjustIndent(buf, -2);
+        virBufferAddLit(buf, "</graphics>\n");
+    } else {
+        virBufferAddLit(buf, "/>\n");
+    }
+}
+
+
+static void
+qemuMigrationCookieNetworkXMLFormat(virBufferPtr buf,
+                                    qemuMigrationCookieNetworkPtr optr)
+{
+    size_t i;
+    bool empty = true;
+
+    for (i = 0; i < optr->nnets; i++) {
+        /* If optr->net[i].vporttype is not set, there is nothing to transfer */
+        if (optr->net[i].vporttype != VIR_NETDEV_VPORT_PROFILE_NONE) {
+            if (empty) {
+                virBufferAddLit(buf, "<network>\n");
+                virBufferAdjustIndent(buf, 2);
+                empty = false;
+            }
+            virBufferAsprintf(buf, "<interface index='%zu' vporttype='%s'",
+                              i, virNetDevVPortTypeToString(optr->net[i].vporttype));
+            if (optr->net[i].portdata) {
+                virBufferAddLit(buf, ">\n");
+                virBufferAdjustIndent(buf, 2);
+                virBufferEscapeString(buf, "<portdata>%s</portdata>\n",
+                                      optr->net[i].portdata);
+                virBufferAdjustIndent(buf, -2);
+                virBufferAddLit(buf, "</interface>\n");
+            } else {
+                virBufferAddLit(buf, "/>\n");
+            }
+        }
+    }
+    if (!empty) {
+        virBufferAdjustIndent(buf, -2);
+        virBufferAddLit(buf, "</network>\n");
+    }
+}
+
+
+static void
+qemuMigrationCookieStatisticsXMLFormat(virBufferPtr buf,
+                                       qemuDomainJobInfoPtr jobInfo)
+{
+    qemuMonitorMigrationStats *stats = &jobInfo->stats;
+
+    virBufferAddLit(buf, "<statistics>\n");
+    virBufferAdjustIndent(buf, 2);
+
+    virBufferAsprintf(buf, "<started>%llu</started>\n", jobInfo->started);
+    virBufferAsprintf(buf, "<stopped>%llu</stopped>\n", jobInfo->stopped);
+    virBufferAsprintf(buf, "<sent>%llu</sent>\n", jobInfo->sent);
+    if (jobInfo->timeDeltaSet)
+        virBufferAsprintf(buf, "<delta>%lld</delta>\n", jobInfo->timeDelta);
+
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_TIME_ELAPSED,
+                      jobInfo->timeElapsed);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_TIME_REMAINING,
+                      jobInfo->timeRemaining);
+    if (stats->downtime_set)
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_DOWNTIME,
+                          stats->downtime);
+    if (stats->setup_time_set)
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_SETUP_TIME,
+                          stats->setup_time);
+
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_MEMORY_TOTAL,
+                      stats->ram_total);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_MEMORY_PROCESSED,
+                      stats->ram_transferred);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_MEMORY_REMAINING,
+                      stats->ram_remaining);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_MEMORY_BPS,
+                      stats->ram_bps);
+
+    if (stats->ram_duplicate_set) {
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_MEMORY_CONSTANT,
+                          stats->ram_duplicate);
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_MEMORY_NORMAL,
+                          stats->ram_normal);
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
+                          stats->ram_normal_bytes);
+    }
+
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
+                      stats->ram_dirty_rate);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_MEMORY_ITERATION,
+                      stats->ram_iteration);
+
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_DISK_TOTAL,
+                      stats->disk_total);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_DISK_PROCESSED,
+                      stats->disk_transferred);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_DISK_REMAINING,
+                      stats->disk_remaining);
+    virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                      VIR_DOMAIN_JOB_DISK_BPS,
+                      stats->disk_bps);
+
+    if (stats->xbzrle_set) {
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_COMPRESSION_CACHE,
+                          stats->xbzrle_cache_size);
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_COMPRESSION_BYTES,
+                          stats->xbzrle_bytes);
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_COMPRESSION_PAGES,
+                          stats->xbzrle_pages);
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
+                          stats->xbzrle_cache_miss);
+        virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
+                          VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
+                          stats->xbzrle_overflow);
+    }
+
+    virBufferAsprintf(buf, "<%1$s>%2$d</%1$s>\n",
+                      VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
+                      stats->cpu_throttle_percentage);
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</statistics>\n");
+}
+
+
+static int
+qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver,
+                             virBufferPtr buf,
+                             qemuMigrationCookiePtr mig)
+{
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    char hostuuidstr[VIR_UUID_STRING_BUFLEN];
+    size_t i;
+
+    virUUIDFormat(mig->uuid, uuidstr);
+    virUUIDFormat(mig->localHostuuid, hostuuidstr);
+
+    virBufferAddLit(buf, "<qemu-migration>\n");
+    virBufferAdjustIndent(buf, 2);
+    virBufferEscapeString(buf, "<name>%s</name>\n", mig->name);
+    virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
+    virBufferEscapeString(buf, "<hostname>%s</hostname>\n", mig->localHostname);
+    virBufferAsprintf(buf, "<hostuuid>%s</hostuuid>\n", hostuuidstr);
+
+    for (i = 0; i < QEMU_MIGRATION_COOKIE_FLAG_LAST; i++) {
+        if (mig->flagsMandatory & (1 << i))
+            virBufferAsprintf(buf, "<feature name='%s'/>\n",
+                              qemuMigrationCookieFlagTypeToString(i));
+    }
+
+    if ((mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
+        mig->graphics)
+        qemuMigrationCookieGraphicsXMLFormat(buf, mig->graphics);
+
+    if ((mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) &&
+        mig->lockState) {
+        virBufferAsprintf(buf, "<lockstate driver='%s'>\n",
+                          mig->lockDriver);
+        virBufferAdjustIndent(buf, 2);
+        virBufferAsprintf(buf, "<leases>%s</leases>\n",
+                          mig->lockState);
+        virBufferAdjustIndent(buf, -2);
+        virBufferAddLit(buf, "</lockstate>\n");
+    }
+
+    if ((mig->flags & QEMU_MIGRATION_COOKIE_PERSISTENT) &&
+        mig->persistent) {
+        if (qemuDomainDefFormatBuf(driver,
+                                   mig->persistent,
+                                   VIR_DOMAIN_XML_INACTIVE |
+                                   VIR_DOMAIN_XML_SECURE |
+                                   VIR_DOMAIN_XML_MIGRATABLE,
+                                   buf) < 0)
+            return -1;
+    }
+
+    if ((mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) && mig->network)
+        qemuMigrationCookieNetworkXMLFormat(buf, mig->network);
+
+    if ((mig->flags & QEMU_MIGRATION_COOKIE_NBD) && mig->nbd) {
+        virBufferAddLit(buf, "<nbd");
+        if (mig->nbd->port)
+            virBufferAsprintf(buf, " port='%d'", mig->nbd->port);
+        if (mig->nbd->ndisks) {
+            virBufferAddLit(buf, ">\n");
+            virBufferAdjustIndent(buf, 2);
+            for (i = 0; i < mig->nbd->ndisks; i++) {
+                virBufferEscapeString(buf, "<disk target='%s'",
+                                      mig->nbd->disks[i].target);
+                virBufferAsprintf(buf, " capacity='%llu'/>\n",
+                                  mig->nbd->disks[i].capacity);
+            }
+            virBufferAdjustIndent(buf, -2);
+            virBufferAddLit(buf, "</nbd>\n");
+        } else {
+            virBufferAddLit(buf, "/>\n");
+        }
+    }
+
+    if (mig->flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo)
+        qemuMigrationCookieStatisticsXMLFormat(buf, mig->jobInfo);
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</qemu-migration>\n");
+    return 0;
+}
+
+
+static char *qemuMigrationCookieXMLFormatStr(virQEMUDriverPtr driver,
+                                             qemuMigrationCookiePtr mig)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    if (qemuMigrationCookieXMLFormat(driver, &buf, mig) < 0) {
+        virBufferFreeAndReset(&buf);
+        return NULL;
+    }
+
+    if (virBufferCheckError(&buf) < 0)
+        return NULL;
+
+    return virBufferContentAndReset(&buf);
+}
+
+
+static qemuMigrationCookieGraphicsPtr
+qemuMigrationCookieGraphicsXMLParse(xmlXPathContextPtr ctxt)
+{
+    qemuMigrationCookieGraphicsPtr grap;
+    char *tmp;
+
+    if (VIR_ALLOC(grap) < 0)
+        goto error;
+
+    if (!(tmp = virXPathString("string(./graphics/@type)", ctxt))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing type attribute in migration data"));
+        goto error;
+    }
+    if ((grap->type = virDomainGraphicsTypeFromString(tmp)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unknown graphics type %s"), tmp);
+        VIR_FREE(tmp);
+        goto error;
+    }
+    VIR_FREE(tmp);
+    if (virXPathInt("string(./graphics/@port)", ctxt, &grap->port) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing port attribute in migration data"));
+        goto error;
+    }
+    if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
+        if (virXPathInt("string(./graphics/@tlsPort)", ctxt, &grap->tlsPort) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("missing tlsPort attribute in migration data"));
+            goto error;
+        }
+    }
+    if (!(grap->listen = virXPathString("string(./graphics/@listen)", ctxt))) {
+        virReportError(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;
+
+ error:
+    qemuMigrationCookieGraphicsFree(grap);
+    return NULL;
+}
+
+
+static qemuMigrationCookieNetworkPtr
+qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)
+{
+    qemuMigrationCookieNetworkPtr optr;
+    size_t i;
+    int n;
+    xmlNodePtr *interfaces = NULL;
+    char *vporttype;
+    xmlNodePtr save_ctxt = ctxt->node;
+
+    if (VIR_ALLOC(optr) < 0)
+        goto error;
+
+    if ((n = virXPathNodeSet("./network/interface", ctxt, &interfaces)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing interface information"));
+        goto error;
+    }
+
+    optr->nnets = n;
+    if (VIR_ALLOC_N(optr->net, optr->nnets) < 0)
+        goto error;
+
+    for (i = 0; i < n; i++) {
+        /* portdata is optional, and may not exist */
+        ctxt->node = interfaces[i];
+        optr->net[i].portdata = virXPathString("string(./portdata[1])", ctxt);
+
+        if (!(vporttype = virXMLPropString(interfaces[i], "vporttype"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("missing vporttype attribute in migration data"));
+            goto error;
+        }
+        optr->net[i].vporttype = virNetDevVPortTypeFromString(vporttype);
+    }
+
+    VIR_FREE(interfaces);
+
+ cleanup:
+    ctxt->node = save_ctxt;
+    return optr;
+
+ error:
+    VIR_FREE(interfaces);
+    qemuMigrationCookieNetworkFree(optr);
+    optr = NULL;
+    goto cleanup;
+}
+
+
+static qemuMigrationCookieNBDPtr
+qemuMigrationCookieNBDXMLParse(xmlXPathContextPtr ctxt)
+{
+    qemuMigrationCookieNBDPtr ret = NULL;
+    char *port = NULL, *capacity = NULL;
+    size_t i;
+    int n;
+    xmlNodePtr *disks = NULL;
+    xmlNodePtr save_ctxt = ctxt->node;
+
+    if (VIR_ALLOC(ret) < 0)
+        goto error;
+
+    port = virXPathString("string(./nbd/@port)", ctxt);
+    if (port && virStrToLong_i(port, NULL, 10, &ret->port) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Malformed nbd port '%s'"),
+                       port);
+        goto error;
+    }
+
+    /* Now check if source sent a list of disks to prealloc. We might be
+     * talking to an older server, so it's not an error if the list is
+     * missing. */
+    if ((n = virXPathNodeSet("./nbd/disk", ctxt, &disks)) > 0) {
+        if (VIR_ALLOC_N(ret->disks, n) < 0)
+            goto error;
+        ret->ndisks = n;
+
+        for (i = 0; i < n; i++) {
+            ctxt->node = disks[i];
+            VIR_FREE(capacity);
+
+            if (!(ret->disks[i].target = virXPathString("string(./@target)", ctxt))) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Malformed disk target"));
+                goto error;
+            }
+
+            capacity = virXPathString("string(./@capacity)", ctxt);
+            if (!capacity ||
+                virStrToLong_ull(capacity, NULL, 10,
+                                 &ret->disks[i].capacity) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Malformed disk capacity: '%s'"),
+                               NULLSTR(capacity));
+                goto error;
+            }
+        }
+    }
+
+ cleanup:
+    VIR_FREE(port);
+    VIR_FREE(capacity);
+    VIR_FREE(disks);
+    ctxt->node = save_ctxt;
+    return ret;
+ error:
+    qemuMigrationCookieNBDFree(ret);
+    ret = NULL;
+    goto cleanup;
+}
+
+
+static qemuDomainJobInfoPtr
+qemuMigrationCookieStatisticsXMLParse(xmlXPathContextPtr ctxt)
+{
+    qemuDomainJobInfoPtr jobInfo = NULL;
+    qemuMonitorMigrationStats *stats;
+    xmlNodePtr save_ctxt = ctxt->node;
+
+    if (!(ctxt->node = virXPathNode("./statistics", ctxt)))
+        goto cleanup;
+
+    if (VIR_ALLOC(jobInfo) < 0)
+        goto cleanup;
+
+    stats = &jobInfo->stats;
+    jobInfo->type = VIR_DOMAIN_JOB_COMPLETED;
+
+    virXPathULongLong("string(./started[1])", ctxt, &jobInfo->started);
+    virXPathULongLong("string(./stopped[1])", ctxt, &jobInfo->stopped);
+    virXPathULongLong("string(./sent[1])", ctxt, &jobInfo->sent);
+    if (virXPathLongLong("string(./delta[1])", ctxt, &jobInfo->timeDelta) == 0)
+        jobInfo->timeDeltaSet = true;
+
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_TIME_ELAPSED "[1])",
+                      ctxt, &jobInfo->timeElapsed);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_TIME_REMAINING "[1])",
+                      ctxt, &jobInfo->timeRemaining);
+
+    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_DOWNTIME "[1])",
+                          ctxt, &stats->downtime) == 0)
+        stats->downtime_set = true;
+    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_SETUP_TIME "[1])",
+                          ctxt, &stats->setup_time) == 0)
+        stats->setup_time_set = true;
+
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_TOTAL "[1])",
+                      ctxt, &stats->ram_total);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_PROCESSED "[1])",
+                      ctxt, &stats->ram_transferred);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_REMAINING "[1])",
+                      ctxt, &stats->ram_remaining);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_BPS "[1])",
+                      ctxt, &stats->ram_bps);
+
+    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_CONSTANT "[1])",
+                          ctxt, &stats->ram_duplicate) == 0)
+        stats->ram_duplicate_set = true;
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_NORMAL "[1])",
+                      ctxt, &stats->ram_normal);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES "[1])",
+                      ctxt, &stats->ram_normal_bytes);
+
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE "[1])",
+                      ctxt, &stats->ram_dirty_rate);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_ITERATION "[1])",
+                      ctxt, &stats->ram_iteration);
+
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_TOTAL "[1])",
+                      ctxt, &stats->disk_total);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_PROCESSED "[1])",
+                      ctxt, &stats->disk_transferred);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_REMAINING "[1])",
+                      ctxt, &stats->disk_remaining);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_BPS "[1])",
+                      ctxt, &stats->disk_bps);
+
+    if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_CACHE "[1])",
+                          ctxt, &stats->xbzrle_cache_size) == 0)
+        stats->xbzrle_set = true;
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_BYTES "[1])",
+                      ctxt, &stats->xbzrle_bytes);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_PAGES "[1])",
+                      ctxt, &stats->xbzrle_pages);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES "[1])",
+                      ctxt, &stats->xbzrle_cache_miss);
+    virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW "[1])",
+                      ctxt, &stats->xbzrle_overflow);
+
+    virXPathInt("string(./" VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE "[1])",
+                ctxt, &stats->cpu_throttle_percentage);
+ cleanup:
+    ctxt->node = save_ctxt;
+    return jobInfo;
+}
+
+
+static int
+qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
+                            virQEMUDriverPtr driver,
+                            xmlDocPtr doc,
+                            xmlXPathContextPtr ctxt,
+                            unsigned int flags)
+{
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    char *tmp = NULL;
+    xmlNodePtr *nodes = NULL;
+    size_t i;
+    int n;
+    virCapsPtr caps = NULL;
+
+    if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
+        goto error;
+
+    /* We don't store the uuid, name, hostname, or hostuuid
+     * values. We just compare them to local data to do some
+     * sanity checking on migration operation
+     */
+
+    /* Extract domain name */
+    if (!(tmp = virXPathString("string(./name[1])", ctxt))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing name element in migration data"));
+        goto error;
+    }
+    if (STRNEQ(tmp, mig->name)) {
+        virReportError(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) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing uuid element in migration data"));
+        goto error;
+    }
+    virUUIDFormat(mig->uuid, uuidstr);
+    if (STRNEQ(tmp, uuidstr)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Incoming cookie data had unexpected UUID %s vs %s"),
+                       tmp, uuidstr);
+        goto error;
+    }
+    VIR_FREE(tmp);
+
+    /* Check & forbid "localhost" migration */
+    if (!(mig->remoteHostname = virXPathString("string(./hostname[1])", ctxt))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing hostname element in migration data"));
+        goto error;
+    }
+    if (STREQ(mig->remoteHostname, mig->localHostname)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Attempt to migrate guest to the same host %s"),
+                       mig->remoteHostname);
+        goto error;
+    }
+
+    if (!(tmp = virXPathString("string(./hostuuid[1])", ctxt))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("missing hostuuid element in migration data"));
+        goto error;
+    }
+    if (virUUIDParse(tmp, mig->remoteHostuuid) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("malformed hostuuid element in migration data"));
+        goto error;
+    }
+    if (memcmp(mig->remoteHostuuid, mig->localHostuuid, VIR_UUID_BUFLEN) == 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Attempt to migrate guest to the same host %s"),
+                       tmp);
+        goto error;
+    }
+    VIR_FREE(tmp);
+
+    /* Check to ensure all mandatory features from XML are also
+     * present in 'flags' */
+    if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0)
+        goto error;
+
+    for (i = 0; i < n; i++) {
+        int val;
+        char *str = virXMLPropString(nodes[i], "name");
+        if (!str) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           "%s", _("missing feature name"));
+            goto error;
+        }
+
+        if ((val = qemuMigrationCookieFlagTypeFromString(str)) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unknown migration cookie feature %s"),
+                           str);
+            VIR_FREE(str);
+            goto error;
+        }
+
+        if ((flags & (1 << val)) == 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unsupported migration cookie feature %s"),
+                           str);
+            VIR_FREE(str);
+            goto error;
+        }
+        VIR_FREE(str);
+    }
+    VIR_FREE(nodes);
+
+    if ((flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
+        virXPathBoolean("count(./graphics) > 0", ctxt) &&
+        (!(mig->graphics = qemuMigrationCookieGraphicsXMLParse(ctxt))))
+        goto error;
+
+    if ((flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) &&
+        virXPathBoolean("count(./lockstate) > 0", ctxt)) {
+        mig->lockDriver = virXPathString("string(./lockstate[1]/@driver)", ctxt);
+        if (!mig->lockDriver) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Missing lock driver name in migration cookie"));
+            goto error;
+        }
+        mig->lockState = virXPathString("string(./lockstate[1]/leases[1])", ctxt);
+        if (mig->lockState && STREQ(mig->lockState, ""))
+            VIR_FREE(mig->lockState);
+    }
+
+    if ((flags & QEMU_MIGRATION_COOKIE_PERSISTENT) &&
+        virXPathBoolean("count(./domain) > 0", ctxt)) {
+        if ((n = virXPathNodeSet("./domain", ctxt, &nodes)) > 1) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Too many domain elements in "
+                             "migration cookie: %d"),
+                           n);
+            goto error;
+        }
+        mig->persistent = virDomainDefParseNode(doc, nodes[0],
+                                                caps, driver->xmlopt, NULL,
+                                                VIR_DOMAIN_DEF_PARSE_INACTIVE |
+                                                VIR_DOMAIN_DEF_PARSE_ABI_UPDATE |
+                                                VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
+        if (!mig->persistent) {
+            /* virDomainDefParseNode already reported
+             * an error for us */
+            goto error;
+        }
+        VIR_FREE(nodes);
+    }
+
+    if ((flags & QEMU_MIGRATION_COOKIE_NETWORK) &&
+        virXPathBoolean("count(./network) > 0", ctxt) &&
+        (!(mig->network = qemuMigrationCookieNetworkXMLParse(ctxt))))
+        goto error;
+
+    if (flags & QEMU_MIGRATION_COOKIE_NBD &&
+        virXPathBoolean("boolean(./nbd)", ctxt) &&
+        (!(mig->nbd = qemuMigrationCookieNBDXMLParse(ctxt))))
+        goto error;
+
+    if (flags & QEMU_MIGRATION_COOKIE_STATS &&
+        virXPathBoolean("boolean(./statistics)", ctxt) &&
+        (!(mig->jobInfo = qemuMigrationCookieStatisticsXMLParse(ctxt))))
+        goto error;
+
+    virObjectUnref(caps);
+    return 0;
+
+ error:
+    VIR_FREE(tmp);
+    VIR_FREE(nodes);
+    virObjectUnref(caps);
+    return -1;
+}
+
+
+static int
+qemuMigrationCookieXMLParseStr(qemuMigrationCookiePtr mig,
+                               virQEMUDriverPtr driver,
+                               const char *xml,
+                               unsigned int flags)
+{
+    xmlDocPtr doc = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    int ret = -1;
+
+    VIR_DEBUG("xml=%s", NULLSTR(xml));
+
+    if (!(doc = virXMLParseStringCtxt(xml, _("(qemu_migration_cookie)"), &ctxt)))
+        goto cleanup;
+
+    ret = qemuMigrationCookieXMLParse(mig, driver, doc, ctxt, flags);
+
+ cleanup:
+    xmlXPathFreeContext(ctxt);
+    xmlFreeDoc(doc);
+
+    return ret;
+}
+
+
+int
+qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
+                        virQEMUDriverPtr driver,
+                        virDomainObjPtr dom,
+                        char **cookieout,
+                        int *cookieoutlen,
+                        unsigned int flags)
+{
+    if (!cookieout || !cookieoutlen)
+        return 0;
+
+    *cookieoutlen = 0;
+
+    if (flags & QEMU_MIGRATION_COOKIE_GRAPHICS &&
+        qemuMigrationCookieAddGraphics(mig, driver, dom) < 0)
+        return -1;
+
+    if (flags & QEMU_MIGRATION_COOKIE_LOCKSTATE &&
+        qemuMigrationCookieAddLockstate(mig, driver, dom) < 0)
+        return -1;
+
+    if (flags & QEMU_MIGRATION_COOKIE_NETWORK &&
+        qemuMigrationCookieAddNetwork(mig, driver, dom) < 0) {
+        return -1;
+    }
+
+    if ((flags & QEMU_MIGRATION_COOKIE_NBD) &&
+        qemuMigrationCookieAddNBD(mig, driver, dom) < 0)
+        return -1;
+
+    if (flags & QEMU_MIGRATION_COOKIE_STATS &&
+        qemuMigrationCookieAddStatistics(mig, dom) < 0)
+        return -1;
+
+    if (flags & QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG)
+        mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG;
+
+    if (flags & QEMU_MIGRATION_COOKIE_CPU_HOTPLUG)
+        mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_CPU_HOTPLUG;
+
+    if (!(*cookieout = qemuMigrationCookieXMLFormatStr(driver, mig)))
+        return -1;
+
+    *cookieoutlen = strlen(*cookieout) + 1;
+
+    VIR_DEBUG("cookielen=%d cookie=%s", *cookieoutlen, *cookieout);
+
+    return 0;
+}
+
+
+qemuMigrationCookiePtr
+qemuMigrationEatCookie(virQEMUDriverPtr driver,
+                       virDomainObjPtr dom,
+                       const char *cookiein,
+                       int cookieinlen,
+                       unsigned int flags)
+{
+    qemuMigrationCookiePtr mig = NULL;
+
+    /* Parse & validate incoming cookie (if any) */
+    if (cookiein && cookieinlen &&
+        cookiein[cookieinlen-1] != '\0') {
+        virReportError(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,
+                                       driver,
+                                       cookiein,
+                                       flags) < 0)
+        goto error;
+
+    if (flags & QEMU_MIGRATION_COOKIE_PERSISTENT &&
+        mig->persistent &&
+        STRNEQ(dom->def->name, mig->persistent->name)) {
+        VIR_FREE(mig->persistent->name);
+        if (VIR_STRDUP(mig->persistent->name, dom->def->name) < 0)
+            goto error;
+    }
+
+    if (mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) {
+        if (!mig->lockDriver) {
+            if (virLockManagerPluginUsesState(driver->lockManager)) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("Missing %s lock state for migration cookie"),
+                               virLockManagerPluginGetName(driver->lockManager));
+                goto error;
+            }
+        } else if (STRNEQ(mig->lockDriver,
+                          virLockManagerPluginGetName(driver->lockManager))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Source host lock driver %s different from target %s"),
+                           mig->lockDriver,
+                           virLockManagerPluginGetName(driver->lockManager));
+            goto error;
+        }
+    }
+
+    return mig;
+
+ error:
+    qemuMigrationCookieFree(mig);
+    return NULL;
+}
diff --git a/src/qemu/qemu_migration_cookie.h b/src/qemu/qemu_migration_cookie.h
new file mode 100644
index 000000000..ca3d639a3
--- /dev/null
+++ b/src/qemu/qemu_migration_cookie.h
@@ -0,0 +1,153 @@
+/*
+ * qemu_migration_cookie.h: QEMU migration cookie handling
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __QEMU_MIGRATION_COOKIE_H__
+# define __QEMU_MIGRATION_COOKIE_H__
+
+enum qemuMigrationCookieFlags {
+    QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS,
+    QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE,
+    QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT,
+    QEMU_MIGRATION_COOKIE_FLAG_NETWORK,
+    QEMU_MIGRATION_COOKIE_FLAG_NBD,
+    QEMU_MIGRATION_COOKIE_FLAG_STATS,
+    QEMU_MIGRATION_COOKIE_FLAG_MEMORY_HOTPLUG,
+    QEMU_MIGRATION_COOKIE_FLAG_CPU_HOTPLUG,
+
+    QEMU_MIGRATION_COOKIE_FLAG_LAST
+};
+
+VIR_ENUM_DECL(qemuMigrationCookieFlag);
+
+enum qemuMigrationCookieFeatures {
+    QEMU_MIGRATION_COOKIE_GRAPHICS  = (1 << QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS),
+    QEMU_MIGRATION_COOKIE_LOCKSTATE = (1 << QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE),
+    QEMU_MIGRATION_COOKIE_PERSISTENT = (1 << QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT),
+    QEMU_MIGRATION_COOKIE_NETWORK = (1 << QEMU_MIGRATION_COOKIE_FLAG_NETWORK),
+    QEMU_MIGRATION_COOKIE_NBD = (1 << QEMU_MIGRATION_COOKIE_FLAG_NBD),
+    QEMU_MIGRATION_COOKIE_STATS = (1 << QEMU_MIGRATION_COOKIE_FLAG_STATS),
+    QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG = (1 << QEMU_MIGRATION_COOKIE_FLAG_MEMORY_HOTPLUG),
+    QEMU_MIGRATION_COOKIE_CPU_HOTPLUG = (1 << QEMU_MIGRATION_COOKIE_FLAG_CPU_HOTPLUG),
+};
+
+typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
+typedef qemuMigrationCookieGraphics *qemuMigrationCookieGraphicsPtr;
+struct _qemuMigrationCookieGraphics {
+    int type;
+    int port;
+    int tlsPort;
+    char *listen;
+    char *tlsSubject;
+};
+
+typedef struct _qemuMigrationCookieNetData qemuMigrationCookieNetData;
+typedef qemuMigrationCookieNetData *qemuMigrationCookieNetDataPtr;
+struct _qemuMigrationCookieNetData {
+    int vporttype; /* enum virNetDevVPortProfile */
+
+    /*
+     * Array of pointers to saved data. Each VIF will have its own
+     * data to transfer.
+     */
+    char *portdata;
+};
+
+typedef struct _qemuMigrationCookieNetwork qemuMigrationCookieNetwork;
+typedef qemuMigrationCookieNetwork *qemuMigrationCookieNetworkPtr;
+struct _qemuMigrationCookieNetwork {
+    /* How many virtual NICs are we saving data for? */
+    int nnets;
+
+    qemuMigrationCookieNetDataPtr net;
+};
+
+typedef struct _qemuMigrationCookieNBD qemuMigrationCookieNBD;
+typedef qemuMigrationCookieNBD *qemuMigrationCookieNBDPtr;
+struct _qemuMigrationCookieNBD {
+    int port; /* on which port does NBD server listen for incoming data */
+
+    size_t ndisks;  /* Number of items in @disk array */
+    struct {
+        char *target;                   /* Disk target */
+        unsigned long long capacity;    /* And its capacity */
+    } *disks;
+};
+
+typedef struct _qemuMigrationCookie qemuMigrationCookie;
+typedef qemuMigrationCookie *qemuMigrationCookiePtr;
+struct _qemuMigrationCookie {
+    unsigned int flags;
+    unsigned int flagsMandatory;
+
+    /* Host properties */
+    unsigned char localHostuuid[VIR_UUID_BUFLEN];
+    unsigned char remoteHostuuid[VIR_UUID_BUFLEN];
+    char *localHostname;
+    char *remoteHostname;
+
+    /* Guest properties */
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    char *name;
+
+    /* If (flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) */
+    char *lockState;
+    char *lockDriver;
+
+    /* If (flags & QEMU_MIGRATION_COOKIE_GRAPHICS) */
+    qemuMigrationCookieGraphicsPtr graphics;
+
+    /* If (flags & QEMU_MIGRATION_COOKIE_PERSISTENT) */
+    virDomainDefPtr persistent;
+
+    /* If (flags & QEMU_MIGRATION_COOKIE_NETWORK) */
+    qemuMigrationCookieNetworkPtr network;
+
+    /* If (flags & QEMU_MIGRATION_COOKIE_NBD) */
+    qemuMigrationCookieNBDPtr nbd;
+
+    /* If (flags & QEMU_MIGRATION_COOKIE_STATS) */
+    qemuDomainJobInfoPtr jobInfo;
+};
+
+
+int
+qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
+                        virQEMUDriverPtr driver,
+                        virDomainObjPtr dom,
+                        char **cookieout,
+                        int *cookieoutlen,
+                        unsigned int flags);
+
+qemuMigrationCookiePtr
+qemuMigrationEatCookie(virQEMUDriverPtr driver,
+                       virDomainObjPtr dom,
+                       const char *cookiein,
+                       int cookieinlen,
+                       unsigned int flags);
+
+void
+qemuMigrationCookieFree(qemuMigrationCookiePtr mig);
+
+int
+qemuMigrationCookieAddPersistent(qemuMigrationCookiePtr mig,
+                                 virDomainDefPtr def);
+
+virDomainDefPtr
+qemuMigrationCookieGetPersistent(qemuMigrationCookiePtr mig);
+
+#endif
-- 
2.12.1




More information about the libvir-list mailing list