[libvirt] [PATCH 1/3] qemu_migration: Add hooks to transport network data during migration

Laine Stump laine at laine.org
Sat Sep 15 02:00:46 UTC 2012


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 at cisco.com>
> ---
>  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)




More information about the libvir-list mailing list