[libvirt] [PATCH 1/3] qemu_migration: Add hooks to transport network data during migration
Kyle Mestery (kmestery)
kmestery at cisco.com
Mon Oct 22 21:33:30 UTC 2012
On Oct 22, 2012, at 4:21 PM, Laine Stump <laine at laine.org> wrote:
> On 10/01/2012 11:18 AM, Kyle Mestery wrote:
>> 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.
>
> I changed this patch to 1) put the portdata in a separate element rather
> than having an extremely long attribute, 2) escape the portdata to avoid
> "surprises" if the data happened to have a character that has a
> syntactical meaning for xml, and 3) clean up miscellaneous other
> problems I've noted below.
>
> Because I'm unable to test these changes myself (and they are more than
> trivial), I'm reposting the series. Please test it and report back
> whether or not it works (ie ACK or NACK). If ACK, then I'll push the
> updated patches.
>
This is great, thanks Laine! I just now see your updated patches, I will pull those down,
apply, rebuild and let you know how my testing goes with a ACK/NACK reply.
Thanks!
Kyle
>>
>> Signed-off-by: Kyle Mestery <kmestery at cisco.com>
>> ---
>> src/qemu/qemu_migration.c | 246 +++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 244 insertions(+), 2 deletions(-)
>>
>> diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
>> index db69a0a..a17ccbd 100644
>> --- a/src/qemu/qemu_migration.c
>> +++ b/src/qemu/qemu_migration.c
>> @@ -70,6 +70,7 @@ 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_LAST
>> };
>> @@ -77,12 +78,13 @@ enum qemuMigrationCookieFlags {
>> VIR_ENUM_DECL(qemuMigrationCookieFlag);
>> VIR_ENUM_IMPL(qemuMigrationCookieFlag,
>> QEMU_MIGRATION_COOKIE_FLAG_LAST,
>> - "graphics", "lockstate", "persistent");
>> + "graphics", "lockstate", "persistent", "network");
>>
>> 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),
>> };
>>
>> typedef struct _qemuMigrationCookieGraphics qemuMigrationCookieGraphics;
>> @@ -95,6 +97,27 @@ struct _qemuMigrationCookieGraphics {
>> 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;
>
> I changed this to "qemuMigratiponCookieNetdataPtr net;" to reduce the
> levels of indirection. The effect is that this is now points to an array
> of qemuMigrationCookieNetdata, rather than pointing to an array of
> pointers to qemu.....Netdata. All the rest of the code (in this patch
> and in 3/3) was adjusted accordingly.
>
>> +};
>> +
>> typedef struct _qemuMigrationCookie qemuMigrationCookie;
>> typedef qemuMigrationCookie *qemuMigrationCookiePtr;
>> struct _qemuMigrationCookie {
>> @@ -120,6 +143,9 @@ struct _qemuMigrationCookie {
>>
>> /* If (flags & QEMU_MIGRATION_COOKIE_PERSISTENT) */
>> virDomainDefPtr persistent;
>> +
>> + /* If (flags & QEMU_MIGRATION_COOKIE_NETWORK) */
>> + qemuMigrationCookieNetworkPtr network;
>> };
>>
>> static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
>> @@ -132,6 +158,25 @@ static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
>> }
>>
>>
>> +static void qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr
>> + network)
>
> Here and in a few other places, I changed the formatting to this:
>
> static void
> functionName(xxxx xxx,
> xxxx xxx)
>
> (note the return type and function name on separate lines, and the first
> characters of the arg types lining up rather than being offset).
>
>> +{
>> + int i;
>> +
>> + if (!network)
>> + return;
>> +
>> + for (i = 0; i < network->nnets; i++) {
>> + if (network->net[i]) {
>> + VIR_FREE(network->net[i]->portdata);
>> + VIR_FREE(network->net[i]);
>> + }
>> + }
>
> This was adjusted for the one layer less of indirection.
>
>> + VIR_FREE(network->net);
>> + VIR_FREE(network);
>> +}
>> +
>> +
>> static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
>> {
>> if (!mig)
>> @@ -140,6 +185,10 @@ static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
>> if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS)
>> qemuMigrationCookieGraphicsFree(mig->graphics);
>>
>> + if (mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) {
>> + qemuMigrationCookieNetworkFree(mig->network);
>> + }
>> +
>> VIR_FREE(mig->localHostname);
>> VIR_FREE(mig->remoteHostname);
>> VIR_FREE(mig->name);
>> @@ -256,6 +305,57 @@ error:
>> }
>>
>>
>> +static qemuMigrationCookieNetworkPtr
>> +qemuMigrationCookieNetworkAlloc(struct qemud_driver *driver ATTRIBUTE_UNUSED,
>> + virDomainDefPtr def)
>> +{
>> + qemuMigrationCookieNetworkPtr mig;
>> + int i;
>> + virDomainNetDefPtr netptr ATTRIBUTE_UNUSED;
>
> I moved this into the for loop and eliminated the ATTRIBUTE_UNUSED (and
> actually *do* use it, as an arg to virDomainNetGetActualVirtPortProfile)
>
>> +
>> + 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++) {
>> + virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(def->nets[i]);
>> + netptr = def->nets[i];
>> +
>> + if (vport) {
>> + if (VIR_ALLOC(mig->net[i]) < 0)
>> + goto no_memory;
>> +
>> + mig->net[i]->vporttype = vport->virtPortType;
>> +
>> + switch (vport->virtPortType) {
>> + case VIR_NETDEV_VPORT_PROFILE_NONE:
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBG:
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBH:
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
>> + break;
>
> All those breaks were combined.
>
>> + default:
>> + mig->net[i]->portdata = NULL;
>
> This is unnecessary, as all newly allocated memory is initialized to 0.
>
>> + break;
>> + }
>> + }
>> + }
>> +
>> + return mig;
>> +
>> +no_memory:
>> + virReportOOMError();
>> + qemuMigrationCookieNetworkFree(mig);
>> + return NULL;
>> +}
>> +
>> +
>> static qemuMigrationCookiePtr
>> qemuMigrationCookieNew(virDomainObjPtr dom)
>> {
>> @@ -370,6 +470,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) {
>> + if (!(mig->network = qemuMigrationCookieNetworkAlloc(
>> + driver, dom->def)))
>> + return -1;
>> + mig->flags |= QEMU_MIGRATION_COOKIE_NETWORK;
>> + }
>> +
>> + return 0;
>> +}
>> +
>>
>> static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
>> qemuMigrationCookieGraphicsPtr grap)
>> @@ -389,6 +510,27 @@ static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
>> }
>>
>>
>> +static void qemuMigrationCookieNetworkXMLFormat(virBufferPtr buf,
>> + qemuMigrationCookieNetworkPtr optr)
>> +{
>> + int i;
>> +
>> + virBufferAsprintf(buf, " <network>\n");
>> + for (i = 0; i < optr->nnets; i++) {
>> + /* If optr->net->vporttype[i] is not set, there is nothing to transfer */
>> + if (optr->net[i] && optr->net[i]->vporttype != VIR_NETDEV_VPORT_PROFILE_NONE) {
>> + virBufferAsprintf(buf, " <interface index='%d' vporttype='%s'",
>> + i, virNetDevVPortTypeToString(optr->net[i]->vporttype));
>> + if (optr->net[i]->portdata)
>> + virBufferAsprintf(buf, " portdata='%s'", optr->net[i]->portdata);
>
> Changed this to fix the indent of the <interface> line (should be 4
> spaces) and put portdata into its own subelement, as well as escaping
> the portdata as its formatted into the buffer.
>
>> + virBufferAddLit(buf, "/>\n");
>> + }
>> + }
>> +
>> + virBufferAddLit(buf, " </network>\n");
>> +}
>> +
>> +
>> static int
>> qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
>> virBufferPtr buf,
>> @@ -439,6 +581,9 @@ qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
>> virBufferAdjustIndent(buf, -2);
>> }
>>
>> + if ((mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) && mig->network)
>> + qemuMigrationCookieNetworkXMLFormat(buf, mig->network);
>> +
>> virBufferAddLit(buf, "</qemu-migration>\n");
>> return 0;
>> }
>> @@ -516,6 +661,58 @@ error:
>> }
>>
>>
>> +static qemuMigrationCookieNetworkPtr
>> +qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)
>> +{
>> + qemuMigrationCookieNetworkPtr optr;
>> + int i;
>> + int n;
>> + xmlNodePtr *interfaces = NULL;
>> + char *vporttype;
>> +
>> + if (VIR_ALLOC(optr) < 0)
>> + goto no_memory;
>> +
>> + if ((n = virXPathNodeSet("./network/interface", ctxt, &interfaces)) < 0) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + "%s", _("missing interace information"));
>
> s/interrace/interface/ :-)
>
>
>> + goto error;
>> + }
>> +
>> + optr->nnets = n;
>> + if (VIR_ALLOC_N(optr->net, optr->nnets) <0)
>> + goto no_memory;
>> +
>> + for (i = 0; i < n; i++) {
>> + if (VIR_ALLOC(optr->net[i]) < 0)
>> + goto no_memory;
>
> This alloc is no longer necessary due to changing the data structure.
>
>> + if (VIR_ALLOC(optr->net[i]->portdata) < 0)
>> + goto no_memory;
>
> This alloc never was proper - it's just allocating a single character
> that is later leaked.
>
>
>> +
>> + /* portdata is optional, and may not exist */
>> + optr->net[i]->portdata = virXMLPropString(interfaces[i], "portdata");
>
> Get this with virXPathString("string(./portdata[1])", ctxt) instead
> (after setting ctxt->node = interfaces[i])
>
>> +
>> + 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);
>> +
>> + return optr;
>> +
>> +no_memory:
>> + virReportOOMError();
>> +error:
>> + VIR_FREE(interfaces);
>> + qemuMigrationCookieNetworkFree(optr);
>> + return NULL;
>> +}
>> +
>> +
>> static int
>> qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
>> struct qemud_driver *driver,
>> @@ -662,6 +859,11 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
>> VIR_FREE(nodes);
>> }
>>
>> + if ((flags & QEMU_MIGRATION_COOKIE_NETWORK) &&
>> + virXPathBoolean("count(./network) > 0", ctxt) &&
>> + (!(mig->network = qemuMigrationCookieNetworkXMLParse(ctxt))))
>> + goto error;
>> +
>> return 0;
>>
>> error:
>> @@ -721,6 +923,10 @@ qemuMigrationBakeCookie(qemuMigrationCookiePtr mig,
>> qemuMigrationCookieAddPersistent(mig, dom) < 0)
>> return -1;
>>
>> + if (flags & QEMU_MIGRATION_COOKIE_NETWORK &&
>> + qemuMigrationCookieAddNetwork(mig, driver, dom) < 0)
>> + return -1;
>> +
>> if (!(*cookieout = qemuMigrationCookieXMLFormatStr(driver, mig)))
>> return -1;
>>
>> @@ -1067,6 +1273,36 @@ 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:
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBG:
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBH:
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
>> + break;
>> + default:
>> + break;
>
> I combined all the breaks.
>
>> + }
>> + }
>> +
>> + return ret;
>> +}
>> +
>> +
>> /* 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
>> * to follow up with the next phase, that is, in between
>> @@ -2011,7 +2247,8 @@ cleanup:
>>
>> if (ret == 0 &&
>> 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");
>>
>> qemuMigrationCookieFree(mig);
>> @@ -2946,6 +3183,7 @@ qemuMigrationFinish(struct qemud_driver *driver,
>>
>> qemuDomainCleanupRemove(vm, qemuMigrationPrepareCleanup);
>>
>> + cookie_flags = QEMU_MIGRATION_COOKIE_NETWORK;
>> if (flags & VIR_MIGRATE_PERSIST_DEST)
>> cookie_flags |= QEMU_MIGRATION_COOKIE_PERSISTENT;
>>
>> @@ -2973,6 +3211,10 @@ qemuMigrationFinish(struct qemud_driver *driver,
>> 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) {
>> virDomainDefPtr vmdef;
>> if (vm->persistent)
>
> Otherwise good. counter-patch coming up!
>
More information about the libvir-list
mailing list