On 09/14/2012 12:38 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. > > Signed-off-by: Kyle Mestery <kmestery@xxxxxxxxx> > --- > src/qemu/qemu_migration.c | 260 +++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 258 insertions(+), 2 deletions(-) > > diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c > index 1b21ef6..539b361 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,22 @@ struct _qemuMigrationCookieGraphics { > char *tlsSubject; > }; > > +typedef struct _qemuMigrationCookieNetwork qemuMigrationCookieNetwork; > +typedef qemuMigrationCookieNetwork *qemuMigrationCookieNetworkPtr; > +struct _qemuMigrationCookieNetwork { > + /* How many virtual NICs are we saving data for? */ > + int nnets; > + > + /* > + * Array of pointers to saved data. Each VIF will have it's own > + * data to transfer. > + */ > + char **netdata; > + > + /* What type is each VIF? */ > + int **viftype; Since each netdata has a matching viftype, please put the two of them together in a struct (e.g. qemuMigrationCookieNetdata), then replace the "char **netdata; int **viftype;" with "qemuMigrationCookieNetdata **net" Also, what exactly is viftype? If it's describing what type of interface, use a string rather than a cryptic integer value. ENUM_DECL() and ENUM_IMPL() (and the XXXToString and XXXFromString functions they define) are a big help with that. Also, why "vif"? why not "interface"? Are you concerned the types will be confused with the types of a domain's <interface> device? If so, I wouldn't worry about that, especially if you use strings rather than integers for the type. > +}; > + > typedef struct _qemuMigrationCookie qemuMigrationCookie; > typedef qemuMigrationCookie *qemuMigrationCookiePtr; > struct _qemuMigrationCookie { > @@ -120,6 +138,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 +153,27 @@ static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap) > } > > > +static void qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr > + network) > +{ > + int i; > + > + if (!network) > + return; > + > + for (i = 0; i < network->nnets; i++) { > + VIR_FREE(network->viftype[i]); > + VIR_FREE(network->netdata[i]); > + } > + > + VIR_FREE(network->viftype); > + > + VIR_FREE(network->netdata); > + > + VIR_FREE(network); There's a lot of extra blank lines in there :-) (Of course if you put viftype and netdata into a single struct, this code will be redone anyway). > +} > + > + > static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig) > { > if (!mig) > @@ -140,6 +182,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 +302,59 @@ error: > } > > > +static qemuMigrationCookieNetworkPtr > +qemuMigrationCookieNetworkAlloc(struct qemud_driver *driver ATTRIBUTE_UNUSED, > + virDomainDefPtr def) > +{ > + qemuMigrationCookieNetworkPtr mig; > + int i; > + virDomainNetDefPtr netptr ATTRIBUTE_UNUSED; > + > + if (VIR_ALLOC(mig) < 0) > + goto no_memory; > + > + mig->nnets = def->nnets; > + > + if (VIR_ALLOC_N(mig->netdata, def->nnets) < 0) > + goto no_memory; > + > + if (VIR_ALLOC_N(mig->viftype, def->nnets) < 0) > + goto no_memory; Same here -> this will be a single VIR_ALLOC_N(mig->net, def->nnets) > + > + for (i = 0; i < def->nnets; i++) { > + virNetDevVPortProfilePtr vport = virDomainNetGetActualVirtPortProfile(def->nets[i]); > + netptr = def->nets[i]; > + > + if (vport) { > + if (VIR_ALLOC(mig->viftype[i]) < 0) > + goto no_memory; > + > + *mig->viftype[i] = vport->virtPortType; Ah, I see - you're directly putting the integer equivalent of the virtPortType in here. That's fine for storing it in memory, but when you get to the Format function, you should use virNetDevVPortTypeToString(type) to put it in the XML as a mnemonic. That way it won't be broken if someone messes with the enum values. > + > + 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; > + default: > + break; > + } > + } > + } > + > + return mig; > + > +no_memory: > + virReportOOMError(); > + qemuMigrationCookieNetworkFree(mig); > + return NULL; > +} > + > + > static qemuMigrationCookiePtr > qemuMigrationCookieNew(virDomainObjPtr dom) > { > @@ -370,6 +469,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) { > + 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 +509,25 @@ static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf, > } > > > +static void qemuMigrationCookieNetworkXMLFormat(virBufferPtr buf, > + qemuMigrationCookieNetworkPtr optr) > +{ > + int i; > + > + virBufferAsprintf(buf, " <network nnets='%d'>\n", optr->nnets); Did you say last time why you need to put nnets in the XML? Seems like it could be deduced from the number/index of the <vif> elements. > + if (optr->nnets > 0) > + virBufferAsprintf(buf, " <vifs>\n"); Same question as last time - why not just put the <vif> elements directly inside <network> rather than having an extra level? (and, as I asked above, is there a reason to call it <vif> instead of <interface>? > + for (i = 0; i < optr->nnets; i++) { > + virBufferAsprintf(buf, " <vif num='%d' viftype='%d' netdata='%s'/>\n", > + i, *optr->viftype[i], optr->netdata[i]); Instead of num='n', maybe index='n'? num might be misinterpreted as "there are 'n' of [something]" rather than "this is for the 'n'th interface in the domain's list" (actually, this points out the usefulness of calling the element <interface> rather than <vif> - makes it clearer that this information is associated with the <interface> element of the domain. Also, the "viftype='%d'" should use '%s' and virNetDevVPortTypeToString(*optr->viftype) instead. At the end, this example seems to me to have the same information you have, but is more compact, just as easy to parse, and makes it more clear where the info came from / what it's for: <network> <interface index='1' vporttype='openvswitch' netdata='blah blah blah'/> <interface index='7' vporttype='openvswitch' netdata='blah blah blah'/> </network> (or maybe "portdata" or "openvswitchdata" instead of "netdata"?). > + } > + if (optr->nnets > 0) > + virBufferAsprintf(buf, " </vifs>\n"); > + > + virBufferAddLit(buf, " </network>\n"); > +} > + > + > static int > qemuMigrationCookieXMLFormat(struct qemud_driver *driver, > virBufferPtr buf, > @@ -439,6 +578,10 @@ 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 +659,74 @@ error: > } > > > +static qemuMigrationCookieNetworkPtr > +qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt) > +{ > + qemuMigrationCookieNetworkPtr optr; > + int i; > + int n; > + xmlNodePtr *vifs = NULL; > + char *viftype; > + > + if (VIR_ALLOC(optr) < 0) > + goto no_memory; > + > + if (virXPathInt("string(./network/@nnets)", ctxt, &optr->nnets) < 0) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + "%s", _("missing nnets attribute in migration data")); > + goto error; > + } > + > + if (VIR_ALLOC_N(optr->netdata, optr->nnets) < 0) > + goto no_memory; > + > + if (VIR_ALLOC_N(optr->viftype, optr->nnets) < 0) > + goto no_memory; > + > + if ((n = virXPathNodeSet("./network/vifs/vif", ctxt, &vifs)) < 0) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + "%s", _("missing vif information")); > + goto error; > + } > + > + for (i = 0; i < n; i++) { > + if (VIR_ALLOC(optr->netdata[i]) < 0) > + goto no_memory; > + > + if (VIR_ALLOC(optr->viftype[i]) < 0) > + goto no_memory; > + > + if (!(optr->netdata[i] = virXMLPropString(vifs[i], "netdata"))) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + "%s", _("missing netdata attribute in migration data")); > + goto error; > + } > + > + if (!(viftype = virXMLPropString(vifs[i], "viftype"))) { > + virReportError(VIR_ERR_INTERNAL_ERROR, > + "%s", _("missing viftype attribute in migration data")); > + goto error; > + } > + > + *optr->viftype[i] = atoi(viftype); > + > + VIR_FREE(viftype); > + } > + > + VIR_FREE(vifs); > + > + return optr; > + > +no_memory: > + virReportOOMError(); > +error: > + if (vifs) > + VIR_FREE(vifs); > + qemuMigrationCookieNetworkFree(optr); > + return NULL; > +} > + > + > static int > qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig, > struct qemud_driver *driver, > @@ -662,6 +873,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 +937,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 +1270,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->viftype[i]) { > + 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; You can just reduce these down to a single break; > + } > + } > + > + 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 +2244,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 +3180,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 +3208,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) -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list