mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-06 13:05:20 +00:00
qemu_migration: Add hooks to transport network data during migration
Add the ability for the Qemu V3 migration protocol to include transporting network configuration. A generic framework is proposed with this patch to allow for the transfer of opaque data. Signed-off-by: Kyle Mestery <kmestery@cisco.com> Signed-off-by: Laine Stump <laine@laine.org>
This commit is contained in:
parent
9785f2b6f2
commit
694d0c520b
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* qemu_migration.c: QEMU migration handling
|
* qemu_migration.c: QEMU migration handling
|
||||||
*
|
*
|
||||||
@ -70,6 +71,7 @@ enum qemuMigrationCookieFlags {
|
|||||||
QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS,
|
QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS,
|
||||||
QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE,
|
QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE,
|
||||||
QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT,
|
QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT,
|
||||||
|
QEMU_MIGRATION_COOKIE_FLAG_NETWORK,
|
||||||
|
|
||||||
QEMU_MIGRATION_COOKIE_FLAG_LAST
|
QEMU_MIGRATION_COOKIE_FLAG_LAST
|
||||||
};
|
};
|
||||||
@ -77,12 +79,13 @@ enum qemuMigrationCookieFlags {
|
|||||||
VIR_ENUM_DECL(qemuMigrationCookieFlag);
|
VIR_ENUM_DECL(qemuMigrationCookieFlag);
|
||||||
VIR_ENUM_IMPL(qemuMigrationCookieFlag,
|
VIR_ENUM_IMPL(qemuMigrationCookieFlag,
|
||||||
QEMU_MIGRATION_COOKIE_FLAG_LAST,
|
QEMU_MIGRATION_COOKIE_FLAG_LAST,
|
||||||
"graphics", "lockstate", "persistent");
|
"graphics", "lockstate", "persistent", "network");
|
||||||
|
|
||||||
enum qemuMigrationCookieFeatures {
|
enum qemuMigrationCookieFeatures {
|
||||||
QEMU_MIGRATION_COOKIE_GRAPHICS = (1 << QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS),
|
QEMU_MIGRATION_COOKIE_GRAPHICS = (1 << QEMU_MIGRATION_COOKIE_FLAG_GRAPHICS),
|
||||||
QEMU_MIGRATION_COOKIE_LOCKSTATE = (1 << QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE),
|
QEMU_MIGRATION_COOKIE_LOCKSTATE = (1 << QEMU_MIGRATION_COOKIE_FLAG_LOCKSTATE),
|
||||||
QEMU_MIGRATION_COOKIE_PERSISTENT = (1 << QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT),
|
QEMU_MIGRATION_COOKIE_PERSISTENT = (1 << QEMU_MIGRATION_COOKIE_FLAG_PERSISTENT),
|
||||||
|
QEMU_MIGRATION_COOKIE_NETWORK = (1 << QEMU_MIGRATION_COOKIE_FLAG_NETWORK),
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
|
typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
|
||||||
@ -95,6 +98,27 @@ struct _qemuMigrationCookieGraphics {
|
|||||||
char *tlsSubject;
|
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 it's 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 _qemuMigrationCookie qemuMigrationCookie;
|
typedef struct _qemuMigrationCookie qemuMigrationCookie;
|
||||||
typedef qemuMigrationCookie *qemuMigrationCookiePtr;
|
typedef qemuMigrationCookie *qemuMigrationCookiePtr;
|
||||||
struct _qemuMigrationCookie {
|
struct _qemuMigrationCookie {
|
||||||
@ -120,6 +144,9 @@ struct _qemuMigrationCookie {
|
|||||||
|
|
||||||
/* If (flags & QEMU_MIGRATION_COOKIE_PERSISTENT) */
|
/* If (flags & QEMU_MIGRATION_COOKIE_PERSISTENT) */
|
||||||
virDomainDefPtr persistent;
|
virDomainDefPtr persistent;
|
||||||
|
|
||||||
|
/* If (flags & QEMU_MIGRATION_COOKIE_NETWORK) */
|
||||||
|
qemuMigrationCookieNetworkPtr network;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
|
static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
|
||||||
@ -132,6 +159,23 @@ static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr network)
|
||||||
|
{
|
||||||
|
int 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 qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
|
static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
|
||||||
{
|
{
|
||||||
if (!mig)
|
if (!mig)
|
||||||
@ -140,6 +184,9 @@ static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
|
|||||||
if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS)
|
if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS)
|
||||||
qemuMigrationCookieGraphicsFree(mig->graphics);
|
qemuMigrationCookieGraphicsFree(mig->graphics);
|
||||||
|
|
||||||
|
if (mig->flags & QEMU_MIGRATION_COOKIE_NETWORK)
|
||||||
|
qemuMigrationCookieNetworkFree(mig->network);
|
||||||
|
|
||||||
VIR_FREE(mig->localHostname);
|
VIR_FREE(mig->localHostname);
|
||||||
VIR_FREE(mig->remoteHostname);
|
VIR_FREE(mig->remoteHostname);
|
||||||
VIR_FREE(mig->name);
|
VIR_FREE(mig->name);
|
||||||
@ -256,6 +303,49 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static qemuMigrationCookieNetworkPtr
|
||||||
|
qemuMigrationCookieNetworkAlloc(struct qemud_driver *driver ATTRIBUTE_UNUSED,
|
||||||
|
virDomainDefPtr def)
|
||||||
|
{
|
||||||
|
qemuMigrationCookieNetworkPtr mig;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (VIR_ALLOC(mig) < 0)
|
||||||
|
goto no_memory;
|
||||||
|
|
||||||
|
mig->nnets = def->nnets;
|
||||||
|
|
||||||
|
if (VIR_ALLOC_N(mig->net, def->nnets) <0)
|
||||||
|
goto no_memory;
|
||||||
|
|
||||||
|
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:
|
||||||
|
case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mig;
|
||||||
|
|
||||||
|
no_memory:
|
||||||
|
virReportOOMError();
|
||||||
|
qemuMigrationCookieNetworkFree(mig);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static qemuMigrationCookiePtr
|
static qemuMigrationCookiePtr
|
||||||
qemuMigrationCookieNew(virDomainObjPtr dom)
|
qemuMigrationCookieNew(virDomainObjPtr dom)
|
||||||
{
|
{
|
||||||
@ -370,6 +460,27 @@ qemuMigrationCookieAddPersistent(qemuMigrationCookiePtr mig,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuMigrationCookieAddNetwork(qemuMigrationCookiePtr mig,
|
||||||
|
struct qemud_driver *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 void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
|
static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
|
||||||
qemuMigrationCookieGraphicsPtr grap)
|
qemuMigrationCookieGraphicsPtr grap)
|
||||||
@ -389,6 +500,37 @@ static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
qemuMigrationCookieNetworkXMLFormat(virBufferPtr buf,
|
||||||
|
qemuMigrationCookieNetworkPtr optr)
|
||||||
|
{
|
||||||
|
int 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) {
|
||||||
|
virBufferAsprintf(buf, " <network>\n");
|
||||||
|
empty = false;
|
||||||
|
}
|
||||||
|
virBufferAsprintf(buf, " <interface index='%d' vporttype='%s'",
|
||||||
|
i, virNetDevVPortTypeToString(optr->net[i].vporttype));
|
||||||
|
if (optr->net[i].portdata) {
|
||||||
|
virBufferAddLit(buf, ">\n");
|
||||||
|
virBufferEscapeString(buf, " <portdata>%s</portdata>\n",
|
||||||
|
optr->net[i].portdata);
|
||||||
|
virBufferAddLit(buf, " </interface>\n");
|
||||||
|
} else {
|
||||||
|
virBufferAddLit(buf, "/>\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty)
|
||||||
|
virBufferAddLit(buf, " </network>\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
|
qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
|
||||||
virBufferPtr buf,
|
virBufferPtr buf,
|
||||||
@ -439,6 +581,9 @@ qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
|
|||||||
virBufferAdjustIndent(buf, -2);
|
virBufferAdjustIndent(buf, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) && mig->network)
|
||||||
|
qemuMigrationCookieNetworkXMLFormat(buf, mig->network);
|
||||||
|
|
||||||
virBufferAddLit(buf, "</qemu-migration>\n");
|
virBufferAddLit(buf, "</qemu-migration>\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -516,6 +661,58 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static qemuMigrationCookieNetworkPtr
|
||||||
|
qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)
|
||||||
|
{
|
||||||
|
qemuMigrationCookieNetworkPtr optr;
|
||||||
|
int i;
|
||||||
|
int n;
|
||||||
|
xmlNodePtr *interfaces = NULL;
|
||||||
|
char *vporttype;
|
||||||
|
xmlNodePtr save_ctxt = ctxt->node;
|
||||||
|
|
||||||
|
if (VIR_ALLOC(optr) < 0)
|
||||||
|
goto no_memory;
|
||||||
|
|
||||||
|
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 no_memory;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
no_memory:
|
||||||
|
virReportOOMError();
|
||||||
|
error:
|
||||||
|
VIR_FREE(interfaces);
|
||||||
|
qemuMigrationCookieNetworkFree(optr);
|
||||||
|
optr = NULL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
|
qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
|
||||||
struct qemud_driver *driver,
|
struct qemud_driver *driver,
|
||||||
@ -663,6 +860,11 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
|
|||||||
VIR_FREE(nodes);
|
VIR_FREE(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((flags & QEMU_MIGRATION_COOKIE_NETWORK) &&
|
||||||
|
virXPathBoolean("count(./network) > 0", ctxt) &&
|
||||||
|
(!(mig->network = qemuMigrationCookieNetworkXMLParse(ctxt))))
|
||||||
|
goto error;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -722,6 +924,11 @@ qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
|
|||||||
qemuMigrationCookieAddPersistent(mig, dom) < 0)
|
qemuMigrationCookieAddPersistent(mig, dom) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (flags & QEMU_MIGRATION_COOKIE_NETWORK &&
|
||||||
|
qemuMigrationCookieAddNetwork(mig, driver, dom) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(*cookieout = qemuMigrationCookieXMLFormatStr(driver, mig)))
|
if (!(*cookieout = qemuMigrationCookieXMLFormatStr(driver, mig)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -1084,6 +1291,32 @@ qemuDomainMigrateGraphicsRelocate(struct qemud_driver *driver,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
qemuDomainMigrateOPDRelocate(struct qemud_driver *driver ATTRIBUTE_UNUSED,
|
||||||
|
virDomainObjPtr vm,
|
||||||
|
qemuMigrationCookiePtr cookie)
|
||||||
|
{
|
||||||
|
virDomainNetDefPtr netptr ATTRIBUTE_UNUSED;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < cookie->network->nnets; i++) {
|
||||||
|
netptr = vm->def->nets[i];
|
||||||
|
|
||||||
|
switch (cookie->network->net[i].vporttype) {
|
||||||
|
case VIR_NETDEV_VPORT_PROFILE_NONE:
|
||||||
|
case VIR_NETDEV_VPORT_PROFILE_8021QBG:
|
||||||
|
case VIR_NETDEV_VPORT_PROFILE_8021QBH:
|
||||||
|
case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This is called for outgoing non-p2p migrations when a connection to the
|
/* This is called for outgoing non-p2p migrations when a connection to the
|
||||||
* client which initiated the migration was closed but we were waiting for it
|
* client which initiated the migration was closed but we were waiting for it
|
||||||
* to follow up with the next phase, that is, in between
|
* to follow up with the next phase, that is, in between
|
||||||
@ -2029,8 +2262,10 @@ cleanup:
|
|||||||
|
|
||||||
if (ret == 0 &&
|
if (ret == 0 &&
|
||||||
qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
|
qemuMigrationBakeCookie(mig, driver, vm, cookieout, cookieoutlen,
|
||||||
QEMU_MIGRATION_COOKIE_PERSISTENT ) < 0)
|
QEMU_MIGRATION_COOKIE_PERSISTENT |
|
||||||
|
QEMU_MIGRATION_COOKIE_NETWORK) < 0) {
|
||||||
VIR_WARN("Unable to encode migration cookie");
|
VIR_WARN("Unable to encode migration cookie");
|
||||||
|
}
|
||||||
|
|
||||||
qemuMigrationCookieFree(mig);
|
qemuMigrationCookieFree(mig);
|
||||||
|
|
||||||
@ -2963,6 +3198,7 @@ qemuMigrationFinish(struct qemud_driver *driver,
|
|||||||
|
|
||||||
qemuDomainCleanupRemove(vm, qemuMigrationPrepareCleanup);
|
qemuDomainCleanupRemove(vm, qemuMigrationPrepareCleanup);
|
||||||
|
|
||||||
|
cookie_flags = QEMU_MIGRATION_COOKIE_NETWORK;
|
||||||
if (flags & VIR_MIGRATE_PERSIST_DEST)
|
if (flags & VIR_MIGRATE_PERSIST_DEST)
|
||||||
cookie_flags |= QEMU_MIGRATION_COOKIE_PERSISTENT;
|
cookie_flags |= QEMU_MIGRATION_COOKIE_PERSISTENT;
|
||||||
|
|
||||||
@ -2990,6 +3226,10 @@ qemuMigrationFinish(struct qemud_driver *driver,
|
|||||||
goto endjob;
|
goto endjob;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mig->network)
|
||||||
|
if (qemuDomainMigrateOPDRelocate(driver, vm, mig) < 0)
|
||||||
|
VIR_WARN("unable to provide network data for relocation");
|
||||||
|
|
||||||
if (flags & VIR_MIGRATE_PERSIST_DEST) {
|
if (flags & VIR_MIGRATE_PERSIST_DEST) {
|
||||||
virDomainDefPtr vmdef;
|
virDomainDefPtr vmdef;
|
||||||
if (vm->persistent)
|
if (vm->persistent)
|
||||||
|
Loading…
Reference in New Issue
Block a user