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(a)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)