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

Laine Stump laine at laine.org
Tue Oct 23 16:11:34 UTC 2012


On 10/23/2012 08:07 AM, Michal Privoznik wrote:
> On 22.10.2012 23:30, Laine Stump wrote:
>> From: Kyle Mestery <kmestery at cisco.com>
>>
>> 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 | 238 +++++++++++++++++++++++++++++++++++++++++++++-
>>  1 file changed, 236 insertions(+), 2 deletions(-)
> ACK but see my comments below.
>
>> diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
>> index 487182e..67276f0 100644
>> --- a/src/qemu/qemu_migration.c
>> +++ b/src/qemu/qemu_migration.c
>> @@ -1,3 +1,4 @@
>> +
>>  /*
>>   * qemu_migration.c: QEMU migration handling
>>   *
>> @@ -70,6 +71,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 +79,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 +98,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;
>> +};
>> +
>>  typedef struct _qemuMigrationCookie qemuMigrationCookie;
>>  typedef qemuMigrationCookie *qemuMigrationCookiePtr;
>>  struct _qemuMigrationCookie {
>> @@ -120,6 +144,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 +159,23 @@ static void qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphicsPtr grap)
>>  }
>>  
>>  
>> +static void
>> +qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr network)
>> +{
>> +    int i;
>> +
>> +    if (!network)
>> +        return;
>> +
>> +    if (network->net) {
>> +        for (i = 0; i < network->nnets; i++)
>> +            VIR_FREE(network->net[i].portdata);
>> +    }
> You could have spared one level of nesting if you'd just only ... [1]

Well, what would have saved another level of nesting would be if nnets
was part of qemuMigrationCookie instead of qemuMigrationCookieNetwork. I
had already removed one extra layer of nesting when I updated Kyle's
patches, and thought of making this change as well, but kind of liked
the consistency of all the "sub-cookies" being a single pointer that
could be checked for NULL:

  struct _qemuMigrationCookieNetdata {
      int vporttype; /* enum virNetDevVPortProfile */
      char *portdata;
  };

  struct _qemuMigrationCookieNetwork {
      int nnets;
      qemuMigrationCookieNetdataPtr net;
  };

  struct _qemuMigrationCookie {
   ... 
      /* If (flags & QEMU_MIGRATION_COOKIE_GRAPHICS) */
      qemuMigrationCookieGraphicsPtr graphics;

      /* If (flags & QEMU_MIGRATION_COOKIE_PERSISTENT) */
      virDomainDefPtr persistent;

      /* If (flags & QEMU_MIGRATION_COOKIE_NETWORK) */
      qemuMigrationCookieNetworkPtr network;
  };

On the other hand, I noticed at the last minute that there is a
LOCKSTATE cookie that *doesn't* follow this pattern:

    /* If (flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) */
    char *lockState;
    char *lockDriver;

so I'd be just as happy with this:

  struct _qemuMigrationCookie {
   ...
     /* If (flags & QEMU_MIGRATION_COOKIE_GRAPHICS) */
     qemuMigrationCookieGraphicsPtr graphics;
     /* If (flags & QEMU_MIGRATION_COOKIE_PERSISTENT) */
     virDomainDefPtr persistent;
     /* If (flags & QEMU_MIGRATION_COOKIE_NETWORK) */
     size_t nnets;
     qemuMigrationCookieNetdataPtr net;
  };

(i.e. eliminating the qemuMigrationCookieNet object completely)


>> +    VIR_FREE(network->net);
>> +    VIR_FREE(network);
>> +}
>> +
>> +
>>  static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig)
>>  {
>>      if (!mig)
>> @@ -140,6 +184,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);
>> +    }
> These curly braces aren't required. But it is not against coding style
> neither.
>
>> +
>>      VIR_FREE(mig->localHostname);
>>      VIR_FREE(mig->remoteHostname);
>>      VIR_FREE(mig->name);
>> @@ -256,6 +304,49 @@ error:
>>  }
>>  
>>  
>> +static qemuMigrationCookieNetworkPtr
>> +qemuMigrationCookieNetworkAlloc(struct qemud_driver *driver ATTRIBUTE_UNUSED,
>> +                                virDomainDefPtr def)
>> +{
>> +    qemuMigrationCookieNetworkPtr mig;
>> +    int i;
>> +
>> +    if (VIR_ALLOC(mig) < 0)
>> +        goto no_memory;
>> +
>> +    mig->nnets = def->nnets;
> [1]: ... set this after the VIR_ALLOC_N below.

Not sure how that by itself solves the problem.


>> +
>> +    if (VIR_ALLOC_N(mig->net, def->nnets) <0)
>> +        goto no_memory;
>> +
>> +    for (i = 0; i < def->nnets; i++) {
>> +        virDomainNetDefPtr netptr;
>> +        virNetDevVPortProfilePtr vport;
>> +
>> +        netptr = def->nets[i];
>> +        vport = virDomainNetGetActualVirtPortProfile(netptr);
>> +
>> +        if (vport) {
>> +            mig->net[i].vporttype = vport->virtPortType;
>> +
>> +            switch (vport->virtPortType) {
>> +            case VIR_NETDEV_VPORT_PROFILE_NONE:
>> +            case VIR_NETDEV_VPORT_PROFILE_8021QBG:
>> +            case VIR_NETDEV_VPORT_PROFILE_8021QBH:
>> +            case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
>> +            default:
>> +                break;
>> +            }
>> +        }
>> +    }
>> +    return mig;
>> +
>> +no_memory:
>> +    virReportOOMError();
>> +    qemuMigrationCookieNetworkFree(mig);
>> +    return NULL;
>> +}
>> +
>>  static qemuMigrationCookiePtr
>>  qemuMigrationCookieNew(virDomainObjPtr dom)
>>  {
>> @@ -370,6 +461,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) {
>> +        mig->network = qemuMigrationCookieNetworkAlloc(driver, dom->def);
>> +        if (!mig->network)
>> +            return -1;
>> +        mig->flags |= QEMU_MIGRATION_COOKIE_NETWORK;
>> +    }
>> +
>> +    return 0;
>> +}
>> +
>>  
>>  static void qemuMigrationCookieGraphicsXMLFormat(virBufferPtr buf,
>>                                                   qemuMigrationCookieGraphicsPtr grap)
>> @@ -389,6 +501,32 @@ 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 */
> I believe '[i]' wants to be moved to 'net' to match the condition below.
>
>> +        if (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) {
>> +                virBufferAddLit(buf, ">\n");
>> +                virBufferEscapeString(buf, "      <portdata>%s</portdata>\n",
>> +                                      optr->net[i].portdata);
>> +                virBufferAddLit(buf, "    </interface>\n");
>> +            } else {
>> +                virBufferAddLit(buf, "/>\n");
>> +            }
>> +        }
>> +    }
>> +    virBufferAddLit(buf, "  </network>\n");
>> +}
>> +
>> +
> Maybe if all 'net[i].vporttype' are VIR_NETDEV_VPORT_PROFILE_NONE so
> there is no <interface/> added, we don't need to add empty <network/>
> neither. But that just cosmetics and I can live with this version as-is.


I was a bit bothered by that extra object in there anyway - I'm going to
respin to remove it - the array will be directly off the main cookie object.


>>  static int
>>  qemuMigrationCookieXMLFormat(struct qemud_driver *driver,
>>                               virBufferPtr buf,
>> @@ -439,6 +577,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 +657,58 @@ error:
>>  }
>>  
>>  
>> +static qemuMigrationCookieNetworkPtr
>> +qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)
>> +{
>> +    qemuMigrationCookieNetworkPtr optr;
>> +    int i;
>> +    int n;
>> +    xmlNodePtr *interfaces = NULL;
>> +    char *vporttype;
>> +    xmlNodePtr save_ctxt = ctxt->node;
>> +
>> +    if (VIR_ALLOC(optr) < 0)
>> +        goto no_memory;
>> +
>> +    if ((n = virXPathNodeSet("./network/interface", ctxt, &interfaces)) < 0) {
>> +        virReportError(VIR_ERR_INTERNAL_ERROR,
>> +                       "%s", _("missing interface information"));
>> +        goto error;
>> +    }
>> +
>> +    optr->nnets = n;
>> +    if (VIR_ALLOC_N(optr->net, optr->nnets) <0)
>> +        goto no_memory;
>> +
>> +    for (i = 0; i < n; i++) {
>> +        /* portdata is optional, and may not exist */
>> +        ctxt->node = interfaces[i];
>> +        optr->net[i].portdata = virXPathString("string(./portdata[1])", ctxt);
>> +
>> +        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);
>> +
>> +cleanup:
>> +    ctxt->node = save_ctxt;
>> +    return optr;
>> +
>> +no_memory:
>> +    virReportOOMError();
>> +error:
>> +    VIR_FREE(interfaces);
>> +    qemuMigrationCookieNetworkFree(optr);
>> +    optr = NULL;
>> +    goto cleanup;
>> +}
>> +
>> +
>>  static int
>>  qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig,
>>                              struct qemud_driver *driver,
>> @@ -663,6 +856,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:
>> @@ -722,6 +920,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;
>>  
>> @@ -1084,6 +1286,32 @@ 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:
>> +        case VIR_NETDEV_VPORT_PROFILE_8021QBG:
>> +        case VIR_NETDEV_VPORT_PROFILE_8021QBH:
>> +        case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
>> +        default:
>> +            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
>> @@ -2029,7 +2257,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);
>> @@ -2963,6 +3192,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;
>>  
>> @@ -2990,6 +3220,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