[libvirt] [PATCH 1/3] qemu_migration: Add hooks to transport network port data during migration
Kyle Mestery (kmestery)
kmestery at cisco.com
Fri Sep 28 19:59:02 UTC 2012
On Sep 28, 2012, at 11:14 AM, Laine Stump wrote:
> On 09/21/2012 05:16 PM, 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.
>
> Functionally this all looks good (and sounds like it lives up to that in
> practice :-). There are some code style issues, though…
>
Thanks for all the comments Laine. I'll address them all and
send out new versions of the patches ASAP.
Kyle
>>
>> Signed-off-by: Kyle Mestery <kmestery at cisco.com>
>> ---
>> src/qemu/qemu_migration.c | 278 +++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 276 insertions(+), 2 deletions(-)
>>
>> diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
>> index 8e85875..06981db 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,35 @@ struct _qemuMigrationCookieGraphics {
>> char *tlsSubject;
>> };
>>
>> +VIR_ENUM_DECL(virInterface);
>> +VIR_ENUM_IMPL(virInterface, VIR_NETDEV_VPORT_PROFILE_LAST,
>> + "none",
>> + "802.1QBg",
>> + "802.1QBh",
>> + "openvswitch");
>
> (I'd never before considered using VIR_ENUM_*() and giving an enum type
> identifier that didn't match the enum being translated....)
>
> Since you're using the enum virNetDevVPortProfile for these values
> anyway, you should just use the already-existing functions:
>
> virNetDevVPortTypeFromString
> virNetDevVPortTypeToString
>
> (these are declared/defined in virnetdevvportprofile.h, and included in
> libvirt_private.syms).
>
>
>> +
>> +typedef struct _qemuMigrationCookieNetdata qemuMigrationCookieNetdata;
>> +typedef qemuMigrationCookieNetdata *qemuMigrationCookieNetdataPtr;
>> +struct _qemuMigrationCookieNetdata {
>> + /* What type is each interace ? virInterface above ... */
>> + char *interfacetype;
>
> 1) Since the xml attribute is called "vporttype", you should give the
> variable in the struct the same name, to avoid confusion.
>
>
> 2) Internally, we usually store the enum value rather than the string,
> and translate to/from string form during XML parse/format. (For some
> reason, normal usage is to specify it in the struct as an int, with a
> comment giving the actual type:
>
>
> int vporttype; /* enum virNetDevVPortProfile */
>
>
> I'm not sure why that is, to be truthful - Eric or Dan?)
>
>
>> +
>> + /*
>> + * 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 qemuMigrationCookie *qemuMigrationCookiePtr;
>> struct _qemuMigrationCookie {
>> @@ -120,6 +151,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 +166,28 @@ static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
>> }
>>
>>
>> +static void qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr
>> + network)
>> +{
>> + int i;
>> +
>> + if (!network)
>> + return;
>> +
>> + for (i = 0; i < network->nnets; i++) {
>> + if (network->net[i]) {
>> + if (network->net[i]->interfacetype)
>> + VIR_FREE(network->net[i]->interfacetype);
>
> VIR_FREE() automatically checks for NULL, and is a NOP if the pointer is
> NULL (it also sets the pointer to NULL when it's finished, so calling it
> twice doesn't create a problem. "make syntax-check" will actually find
> such redundant null-check-before-free and complain about it. So, you
> should remove all checks for NULL before calling VIR_FREE()
>
> (of course you need the outside "if (network->net[i])" to remain, though :-)
>
>> + if (network->net[i]->portdata)
>> + VIR_FREE(network->net[i]->portdata);
>> + VIR_FREE(network->net[i]);
>> + }
>> + }
>> + VIR_FREE(network->net);
>> + VIR_FREE(network);
>> +}
>> +
>> +
>> static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
>> {
>> if (!mig)
>> @@ -140,6 +196,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 +316,65 @@ error:
>> }
>>
>>
>> +static qemuMigrationCookieNetworkPtr
>> +qemuMigrationCookieNetworkAlloc(struct qemud_driver *driver ATTRIBUTE_UNUSED,
>> + virDomainDefPtr def)
>> +{
>> + qemuMigrationCookieNetworkPtr mig;
>> + int i;
>> + virDomainNetDefPtr netptr ATTRIBUTE_UNUSED;
>> + const char *interfacetype;
>> +
>> + 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;
>> + if (VIR_ALLOC(mig->net[i]->interfacetype) < 0)
>> + goto no_memory;
>> +
>> + interfacetype = virInterfaceTypeToString(vport->virtPortType);
>> + strncpy(mig->net[i]->interfacetype, interfacetype, strlen(interfacetype));
>
> Right here is where you should just do "mig->net[i]->vporttype =
> vport->virtPortType" instead
> of translating it into a string.
>
>> +
>> + switch (vport->virtPortType) {
>> + case VIR_NETDEV_VPORT_PROFILE_NONE:
>> + mig->net[i]->portdata = NULL;
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBG:
>> + mig->net[i]->portdata = NULL;
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBH:
>> + mig->net[i]->portdata = NULL;
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
>> + mig->net[i]->portdata = NULL;
>> + break;
>> + default:
>> + mig->net[i]->portdata = NULL;
>> + break;
>
>
> Applicable to all of the above: VIR_ALLOC guarantees that all allocated
> memory is initialized to 0's, so individual initialization of properties
> to 0/NULL isn't required (and having it there mistakenly implies to new
> coders that it *is* necessary). So you should remove this initialization.
>
>
>> + }
>> + }
>> + }
>> +
>> + return mig;
>> +
>> +no_memory:
>> + virReportOOMError();
>> + qemuMigrationCookieNetworkFree(mig);
>> + return NULL;
>> +}
>> +
>> +
>> static qemuMigrationCookiePtr
>> qemuMigrationCookieNew(virDomainObjPtr dom)
>> {
>> @@ -370,6 +489,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 >= 1) {
>
> (I would have just said " > 0", but I suppose there's no practical
> difference :-)
>
>> + 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 +529,28 @@ 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->interfacetype[i] is not set, there is nothing to transfer */
>> + if (optr->net[i] && optr->net[i]->interfacetype) {
>> + if (!optr->net[i]->portdata)
>> + virBufferAsprintf(buf, " <interface index='%d' vporttype='%s'/>\n",
>> + i, optr->net[i]->interfacetype);
>
>
> ... and here is where you would use
> "virNetDevVPortTypeToString(optr->net[i]->vporttype)" (to account for
> storing the enum directly in the object rather than a string)
>
>
>> + else
>> + virBufferAsprintf(buf, " <interface index='%d' vporttype='%s' portdata='%s'/>\n",
>> + i, optr->net[i]->interfacetype, optr->net[i]->portdata);
>
> Actually, is it necessary to add the <interface> element for an
> interface that has nothing other than the vporttype? Maybe you only need
> the 2nd virBufferAsprintf(), with a preceding "if
> (optr->net[i]->portdata)". Even if you decide that you should always
> have the <interface> element regardless of presence of portdata, you
> could eliminate the duplicated string formatting by doing it like this:
>
> virBufferAsprintf(buf, " <interface index='%d' vporttype='%s'",
> i, optr->net[i]->interfacetype);
> if (optr->net[i]->portdata)
> virBufferAsprintf(buf, " portdata='%s'", optr->net[i]->portdata);
> virBufferAddLit(buf, "/>\n");
>
>
> This would also allow for easier expansion if/when more option attributes are added to <interface> in the future.
>
>
>> + }
>> + }
>> +
>> + virBufferAddLit(buf, " </network>\n");
>> +}
>> +
>> +
>> static int
>> qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
>> virBufferPtr buf,
>> @@ -439,6 +601,10 @@ qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
>> virBufferAdjustIndent(buf, -2);
>> }
>>
>> + if ((mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) &&
>> + mig->network)
>
> I know it's serious nit-picking, but since the above two lines add up to
> < 80 columns, I'd rather have them on the same line (and if you do have
> an if () expression that takes up > 1 line, I like to put { } around
> the following statement even if it's not compound, just to avoid confusion).
>
>> + qemuMigrationCookieNetworkXMLFormat(buf, mig->network);
>> +
>> virBufferAddLit(buf, "</qemu-migration>\n");
>> return 0;
>> }
>> @@ -516,6 +682,60 @@ error:
>> }
>>
>>
>> +static qemuMigrationCookieNetworkPtr
>> +qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)
>> +{
>> + qemuMigrationCookieNetworkPtr optr;
>> + int i;
>> + int n;
>> + xmlNodePtr *interfaces = NULL;
>> +
>> + 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"));
>> + 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;
>> + if (VIR_ALLOC(optr->net[i]->portdata) < 0)
>> + goto no_memory;
>> +
>> + if (VIR_ALLOC(optr->net[i]->interfacetype) < 0)
>> + goto no_memory;
>> +
>> + /* portdata is optional, and may not exist */
>> + optr->net[i]->portdata = virXMLPropString(interfaces[i], "portdata");
>> +
>> + if (!(optr->net[i]->interfacetype = virXMLPropString(interfaces[i], "vporttype"))) {
>> + virReportError(VIR_ERR_INTERNAL_ERROR,
>> + "%s", _("missing vporttype attribute in migration data"));
>> + goto error;
>> + }
>> + }
>> +
>> + VIR_FREE(interfaces);
>> +
>> + return optr;
>> +
>> +no_memory:
>> + virReportOOMError();
>> +error:
>> + if (interfaces)
>
> Another redundant NULL check - just unconditionally call VIR_FREE ("make
> syntax-check" would have caught this)
>
>> + VIR_FREE(interfaces);
>> + qemuMigrationCookieNetworkFree(optr);
>> + return NULL;
>> +}
>> +
>> +
>> static int
>> qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
>> struct qemud_driver *driver,
>> @@ -662,6 +882,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 +946,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;
>>
>> @@ -1050,6 +1279,45 @@ 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;
>> + int interfacetype;
>> +
>> + for (i = 0; i < cookie->network->nnets; i++) {
>> + netptr = vm->def->nets[i];
>> +
>> + if (cookie->network->net[i]->interfacetype) {
>> + interfacetype = virInterfaceTypeFromString(cookie->network->net[i]->interfacetype);
>> + switch (interfacetype) {
>> + case VIR_NETDEV_VPORT_PROFILE_NONE:
>> + cookie->network->net[i]->portdata = NULL;
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBG:
>> + cookie->network->net[i]->portdata = NULL;
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_8021QBH:
>> + cookie->network->net[i]->portdata = NULL;
>> + break;
>> + case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
>> + cookie->network->net[i]->portdata = NULL;
>> + break;
>> + default:
>> + cookie->network->net[i]->portdata = NULL;
>> + break;
>
> Another case of unnecessary NULL initialization - either portdata has
> never been used and is guaranteed to be NULL already, or if it's not
> NULL, then it's pointing to something that needs to be freed (I haven't
> checked the use-case for this function to see which is the case).
>
>
>> + }
>> + }
>> + }
>> +
>> + 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
>> @@ -1994,7 +2262,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);
>> @@ -2929,6 +3198,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;
>>
>> @@ -2956,6 +3226,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)
More information about the libvir-list
mailing list