[libvirt] [PATCH 07/10] network: new XML to support virtual switch functionality

Laine Stump laine at laine.org
Tue Jul 5 07:45:55 UTC 2011


This implements the changes to <network> and domain <interface> XML that
were earlier specified in the RNG.

Each virDomainNetDef now also potentially has a virDomainActualNetDef
which is a private object (never exported/imported via the public API,
and not defined in the RNG) that is used to maintain information about
the physical device that was actually used for a NetDef that was of
type VIR_DOMAIN_NET_TYPE_NETWORK.

The virDomainActualNetDef will only be parsed/formatted if the
parse/format function is called with the VIR_DOMAIN_XML_ACTUAL_NET
flags set (which is only needed when saving/loading a running domain's
state info to the stateDir). To prevent this flag bit from
accidentally being used in the public API, a "RESERVED" placeholder
was put into the public flags enum (at the same time, I noticed there
was another private flag that hadn't been reserved, so I added that
one, making both of these flags #defined from the public RESERVED
flags, and since it was also only used in domain_conf.c, I unpolluted
domain_conf.h, putting both #defines in domain_conf.c.

A small change is also made to two functions in bridge_driver.c, to
prevent a bridge device name and mac address from being automatically
added to a network definition when it is of one of the new forward
types (none of which use bridge devices managed by libvirt).
---
 include/libvirt/libvirt.h.in |    2 +
 src/conf/domain_conf.c       |  276 +++++++++++++++++++++++++++++++++++-
 src/conf/domain_conf.h       |   46 ++++++-
 src/conf/network_conf.c      |  321 +++++++++++++++++++++++++++++++++++++-----
 src/conf/network_conf.h      |   34 ++++-
 src/libvirt_private.syms     |    8 +-
 src/network/bridge_driver.c  |   28 +++-
 7 files changed, 657 insertions(+), 58 deletions(-)

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 8e20f75..b88c96e 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1112,6 +1112,8 @@ typedef enum {
     VIR_DOMAIN_XML_SECURE       = (1 << 0), /* dump security sensitive information too */
     VIR_DOMAIN_XML_INACTIVE     = (1 << 1), /* dump inactive domain information */
     VIR_DOMAIN_XML_UPDATE_CPU   = (1 << 2), /* update guest CPU requirements according to host CPU */
+    VIR_DOMAIN_XML_RESERVED1    = (1 << 30), /* reserved for internal used */
+    VIR_DOMAIN_XML_RESERVED2    = (1 << 31), /* reserved for internal used */
 } virDomainXMLFlags;
 
 char *                  virDomainGetXMLDesc     (virDomainPtr domain,
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 66a7c59..d5a3387 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -489,6 +489,11 @@ VIR_ENUM_IMPL(virDomainNumatuneMemMode, VIR_DOMAIN_NUMATUNE_MEM_LAST,
 
 #define VIR_DOMAIN_XML_WRITE_FLAGS  VIR_DOMAIN_XML_SECURE
 #define VIR_DOMAIN_XML_READ_FLAGS   VIR_DOMAIN_XML_INACTIVE
+/* these flags are only used internally */
+/* dump internal domain status information */
+#define VIR_DOMAIN_XML_INTERNAL_STATUS VIR_DOMAIN_XML_RESERVED1
+/* dump virDomainActualNetDef contents */
+#define VIR_DOMAIN_XML_ACTUAL_NET VIR_DOMAIN_XML_RESERVED2
 
 static void
 virDomainObjListDataFree(void *payload, const void *name ATTRIBUTE_UNUSED)
@@ -714,6 +719,25 @@ void virDomainFSDefFree(virDomainFSDefPtr def)
     VIR_FREE(def);
 }
 
+void
+virDomainActualNetDefFree(virDomainActualNetDefPtr def)
+{
+    if (!def)
+        return;
+
+    switch (def->type) {
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+        VIR_FREE(def->data.bridge.brname);
+        break;
+    case VIR_DOMAIN_NET_TYPE_DIRECT:
+        VIR_FREE(def->data.direct.linkdev);
+        VIR_FREE(def->data.direct.virtPortProfile);
+        break;
+    default:
+        break;
+    }
+}
+
 void virDomainNetDefFree(virDomainNetDefPtr def)
 {
     if (!def)
@@ -736,6 +760,9 @@ void virDomainNetDefFree(virDomainNetDefPtr def)
 
     case VIR_DOMAIN_NET_TYPE_NETWORK:
         VIR_FREE(def->data.network.name);
+        VIR_FREE(def->data.network.portgroup);
+        VIR_FREE(def->data.network.virtPortProfile);
+        virDomainActualNetDefFree(def->data.network.actual);
         break;
 
     case VIR_DOMAIN_NET_TYPE_BRIDGE:
@@ -2566,6 +2593,85 @@ cleanup:
     goto cleanup;
 }
 
+static int
+virDomainActualNetDefParseXML(xmlNodePtr node,
+                              xmlXPathContextPtr ctxt,
+                              virDomainActualNetDefPtr *actual)
+{
+    int ret = -1;
+    xmlNodePtr save_ctxt = ctxt->node;
+    char *type = NULL;
+    char *mode = NULL;
+
+    if (VIR_ALLOC(*actual) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    ctxt->node = node;
+
+    type = virXMLPropString(node, "type");
+    if (!type) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                             _("missing type attribute in interface's <actual> element"));
+        goto error;
+    }
+    if ((int)((*actual)->type = virDomainNetTypeFromString(type)) < 0) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unknown type '%s' in interface's <actual> element"), type);
+        goto error;
+    }
+    if ((*actual)->type != VIR_DOMAIN_NET_TYPE_BRIDGE &&
+        (*actual)->type != VIR_DOMAIN_NET_TYPE_DIRECT) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unsupported type '%s' in interface's <actual> element"),
+                             type);
+        goto error;
+    }
+
+    if ((*actual)->type == VIR_DOMAIN_NET_TYPE_BRIDGE) {
+
+        (*actual)->data.bridge.brname = virXPathString("string(./source[1]/@bridge)",
+                                                       ctxt);
+
+    } else if ((*actual)->type == VIR_DOMAIN_NET_TYPE_DIRECT) {
+        xmlNodePtr virtPortNode;
+        const char *errmsg;
+
+        (*actual)->data.direct.linkdev = virXPathString("string(./source[1]/@dev)", ctxt);
+
+        mode = virXPathString("string(./source[1]/@mode)", ctxt);
+        if (mode) {
+            int m;
+            if ((m = virMacvtapModeTypeFromString(mode)) < 0) {
+                virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                                     _("Unkown mode '%s' in interface <actual> element"),
+                                     mode);
+                goto error;
+            }
+            (*actual)->data.direct.mode = m;
+        }
+
+        virtPortNode = virXPathNode("./virtualport", ctxt);
+        if (virtPortNode &&
+            virVirtualPortProfileParamsParseXML(virtPortNode,
+                                                &(*actual)->data.direct.virtPortProfile,
+                                                &errmsg) < 0) {
+            if (errmsg)
+                virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s in interface <actual>",
+                                     errmsg);
+            goto error;
+        }
+    }
+
+    ret = 0;
+error:
+    VIR_FREE(type);
+    VIR_FREE(mode);
+
+    ctxt->node = save_ctxt;
+    return ret;
+}
 
 /* Parse the XML definition for a network interface
  * @param node XML nodeset to parse for net definition
@@ -2583,6 +2689,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
     char *macaddr = NULL;
     char *type = NULL;
     char *network = NULL;
+    char *portgroup = NULL;
     char *bridge = NULL;
     char *dev = NULL;
     char *ifname = NULL;
@@ -2599,6 +2706,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
     char *mode = NULL;
     virNWFilterHashTablePtr filterparams = NULL;
     virVirtualPortProfileParamsPtr virtPort = NULL;
+    virDomainActualNetDefPtr actual = NULL;
     xmlNodePtr oldnode = ctxt->node;
     int ret;
 
@@ -2630,6 +2738,7 @@ virDomainNetDefParseXML(virCapsPtr caps,
                        (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) &&
                        (xmlStrEqual(cur->name, BAD_CAST "source"))) {
                 network = virXMLPropString(cur, "network");
+                portgroup = virXMLPropString(cur, "portgroup");
             } else if ((internal == NULL) &&
                        (def->type == VIR_DOMAIN_NET_TYPE_INTERNAL) &&
                        (xmlStrEqual(cur->name, BAD_CAST "source"))) {
@@ -2645,7 +2754,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
                 dev  = virXMLPropString(cur, "dev");
                 mode = virXMLPropString(cur, "mode");
             } else if ((virtPort == NULL) &&
-                       (def->type == VIR_DOMAIN_NET_TYPE_DIRECT) &&
+                       ((def->type == VIR_DOMAIN_NET_TYPE_DIRECT) ||
+                        (def->type == VIR_DOMAIN_NET_TYPE_NETWORK)) &&
                        xmlStrEqual(cur->name, BAD_CAST "virtualport")) {
                 const char *errmsg;
                 if (virVirtualPortProfileParamsParseXML(cur, &virtPort, &errmsg) < 0) {
@@ -2697,6 +2807,12 @@ virDomainNetDefParseXML(virCapsPtr caps,
                 if (virDomainDeviceBootParseXML(cur, &def->bootIndex,
                                                 bootMap))
                     goto error;
+            } else if ((actual == NULL) &&
+                       (flags & VIR_DOMAIN_XML_ACTUAL_NET) &&
+                       (def->type == VIR_DOMAIN_NET_TYPE_NETWORK) &&
+                       xmlStrEqual(cur->name, BAD_CAST "actual")) {
+                if (virDomainActualNetDefParseXML(cur, ctxt, &actual) < 0)
+                    goto error;
             }
         }
         cur = cur->next;
@@ -2745,6 +2861,12 @@ virDomainNetDefParseXML(virCapsPtr caps,
         }
         def->data.network.name = network;
         network = NULL;
+        def->data.network.portgroup = portgroup;
+        portgroup = NULL;
+        def->data.network.virtPortProfile = virtPort;
+        virtPort = NULL;
+        def->data.network.actual = actual;
+        actual = NULL;
         break;
 
     case VIR_DOMAIN_NET_TYPE_ETHERNET:
@@ -2836,10 +2958,8 @@ virDomainNetDefParseXML(virCapsPtr caps,
         } else
             def->data.direct.mode = VIR_MACVTAP_MODE_VEPA;
 
-        if (virtPort) {
-            def->data.direct.virtPortProfile = virtPort;
-            virtPort = NULL;
-        }
+        def->data.direct.virtPortProfile = virtPort;
+        virtPort = NULL;
 
         def->data.direct.linkdev = dev;
         dev = NULL;
@@ -2943,11 +3063,13 @@ cleanup:
     ctxt->node = oldnode;
     VIR_FREE(macaddr);
     VIR_FREE(network);
+    VIR_FREE(portgroup);
     VIR_FREE(address);
     VIR_FREE(port);
     VIR_FREE(ifname);
     VIR_FREE(dev);
     VIR_FREE(virtPort);
+    virDomainActualNetDefFree(actual);
     VIR_FREE(script);
     VIR_FREE(bridge);
     VIR_FREE(model);
@@ -8421,6 +8543,67 @@ virDomainFSDefFormat(virBufferPtr buf,
 }
 
 static int
+virDomainActualNetDefFormat(virBufferPtr buf,
+                            virDomainActualNetDefPtr def,
+                            int flags) {
+    int ret = -1;
+    const char *type;
+    const char *mode;
+
+    if (!(def && (flags & VIR_DOMAIN_XML_INTERNAL_STATUS)))
+        return 0;
+
+    type = virDomainNetTypeToString(def->type);
+    if (!type) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected net type %d"), def->type);
+        return ret;
+    }
+
+    if (def->type != VIR_DOMAIN_NET_TYPE_BRIDGE &&
+        def->type != VIR_DOMAIN_NET_TYPE_DIRECT) {
+        virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                             _("unexpected net type %s"), type);
+        goto error;
+    }
+    virBufferAsprintf(buf, "      <actual type='%s'>\n", type);
+
+    switch (def->type) {
+    case VIR_DOMAIN_NET_TYPE_BRIDGE:
+        if (def->data.bridge.brname) {
+            virBufferEscapeString(buf, "        <source bridge='%s'/>\n",
+                                  def->data.bridge.brname);
+        }
+        break;
+
+    case VIR_DOMAIN_NET_TYPE_DIRECT:
+        virBufferAddLit(buf, "        <source");
+        if (def->data.direct.linkdev)
+            virBufferEscapeString(buf, " dev='%s'",
+                                  def->data.direct.linkdev);
+
+        mode = virMacvtapModeTypeToString(def->data.direct.mode);
+        if (!mode) {
+            virDomainReportError(VIR_ERR_INTERNAL_ERROR,
+                                 _("unexpected source mode %d"),
+                                 def->data.direct.mode);
+            return ret;
+        }
+        virBufferAsprintf(buf, " mode='%s'/>\n", mode);
+        virVirtualPortProfileFormat(buf, def->data.direct.virtPortProfile,
+                                    "        ");
+        break;
+    default:
+        break;
+    }
+    virBufferAddLit(buf, "      </actual>\n");
+
+    ret = 0;
+error:
+    return ret;
+}
+
+static int
 virDomainNetDefFormat(virBufferPtr buf,
                       virDomainNetDefPtr def,
                       int flags)
@@ -8443,8 +8626,17 @@ virDomainNetDefFormat(virBufferPtr buf,
 
     switch (def->type) {
     case VIR_DOMAIN_NET_TYPE_NETWORK:
-        virBufferEscapeString(buf, "      <source network='%s'/>\n",
+        virBufferEscapeString(buf, "      <source network='%s'",
                               def->data.network.name);
+        if (def->data.network.portgroup) {
+           virBufferEscapeString(buf, " portgroup='%s'",
+                                 def->data.network.portgroup);
+        }
+        virBufferAddLit(buf, "/>\n");
+        virVirtualPortProfileFormat(buf, def->data.network.virtPortProfile,
+                                    "      ");
+        if (virDomainActualNetDefFormat(buf, def->data.network.actual, flags) < 0)
+            return -1;
         break;
 
     case VIR_DOMAIN_NET_TYPE_ETHERNET:
@@ -9871,7 +10063,9 @@ int virDomainSaveStatus(virCapsPtr caps,
                         const char *statusDir,
                         virDomainObjPtr obj)
 {
-    int flags = VIR_DOMAIN_XML_SECURE|VIR_DOMAIN_XML_INTERNAL_STATUS;
+    int flags = VIR_DOMAIN_XML_SECURE |
+       VIR_DOMAIN_XML_INTERNAL_STATUS |
+       VIR_DOMAIN_XML_ACTUAL_NET;
     int ret = -1;
     char *xml;
 
@@ -9960,7 +10154,8 @@ static virDomainObjPtr virDomainLoadStatus(virCapsPtr caps,
         goto error;
 
     if (!(obj = virDomainObjParseFile(caps, statusFile,
-                                      VIR_DOMAIN_XML_INTERNAL_STATUS)))
+                                      VIR_DOMAIN_XML_INTERNAL_STATUS |
+                                      VIR_DOMAIN_XML_ACTUAL_NET)))
         goto error;
 
     virUUIDFormat(obj->def->uuid, uuidstr);
@@ -10946,3 +11141,68 @@ virDomainStateReasonFromString(virDomainState state, const char *reason)
 
     return -1;
 }
+
+
+/* some access functions to gloss over the difference between NetDef
+ * (<interface>) and ActualNetDef (<actual>). If the NetDef has an
+ * ActualNetDef, return the requested value from the ActualNetDef,
+ * otherwise return the value from the NetDef.
+ */
+
+int
+virDomainNetGetActualType(virDomainNetDefPtr iface)
+{
+    if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+        return iface->type;
+    if (!iface->data.network.actual)
+        return iface->type;
+    return iface->data.network.actual->type;
+}
+
+char *
+virDomainNetGetActualBridgeName(virDomainNetDefPtr iface)
+{
+    if (iface->type == VIR_DOMAIN_NET_TYPE_BRIDGE)
+        return iface->data.bridge.brname;
+    if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+        return NULL;
+    if (!iface->data.network.actual)
+        return NULL;
+    return iface->data.network.actual->data.bridge.brname;
+}
+
+char *
+virDomainNetGetActualDirectDev(virDomainNetDefPtr iface)
+{
+    if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+        return iface->data.direct.linkdev;
+    if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+        return NULL;
+    if (!iface->data.network.actual)
+        return NULL;
+    return iface->data.network.actual->data.direct.linkdev;
+}
+
+int
+virDomainNetGetActualDirectMode(virDomainNetDefPtr iface)
+{
+    if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+        return iface->data.direct.mode;
+    if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+        return 0;
+    if (!iface->data.network.actual)
+        return 0;
+    return iface->data.network.actual->data.direct.mode;
+}
+
+virVirtualPortProfileParamsPtr
+virDomainNetGetActualDirectVirtPortProfile(virDomainNetDefPtr iface)
+{
+    if (iface->type == VIR_DOMAIN_NET_TYPE_DIRECT)
+        return iface->data.direct.virtPortProfile;
+    if (iface->type != VIR_DOMAIN_NET_TYPE_NETWORK)
+        return NULL;
+    if (!iface->data.network.actual)
+        return NULL;
+    return iface->data.network.actual->data.direct.virtPortProfile;
+}
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index f8771a9..5807e77 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1,3 +1,4 @@
+
 /*
  * domain_conf.h: domain XML processing
  *
@@ -41,11 +42,6 @@
 # include "macvtap.h"
 # include "sysinfo.h"
 
-/* Private component of virDomainXMLFlags */
-typedef enum {
-   VIR_DOMAIN_XML_INTERNAL_STATUS = (1<<16), /* dump internal domain status information */
-} virDomainXMLInternalFlags;
-
 /* Different types of hypervisor */
 /* NB: Keep in sync with virDomainVirtTypeToString impl */
 enum virDomainVirtType {
@@ -348,6 +344,27 @@ enum virDomainNetVirtioTxModeType {
     VIR_DOMAIN_NET_VIRTIO_TX_MODE_LAST,
 };
 
+/* Config that was actually used to bring up interface, after
+ * resolving network reference. This is private data, only used within
+ * libvirt, but still must maintain backward compatibility, because
+ * different versions of libvirt may read the same data file.
+ */
+typedef struct _virDomainActualNetDef virDomainActualNetDef;
+typedef virDomainActualNetDef *virDomainActualNetDefPtr;
+struct _virDomainActualNetDef {
+    enum virDomainNetType type;
+    union {
+        struct {
+            char *brname;
+        } bridge;
+        struct {
+            char *linkdev;
+            int mode; /* enum virMacvtapMode from util/macvtap.h */
+            virVirtualPortProfileParamsPtr virtPortProfile;
+        } direct;
+    } data;
+};
+
 /* Stores the virtual network interface configuration */
 typedef struct _virDomainNetDef virDomainNetDef;
 typedef virDomainNetDef *virDomainNetDefPtr;
@@ -374,6 +391,17 @@ struct _virDomainNetDef {
         } socket; /* any of NET_CLIENT or NET_SERVER or NET_MCAST */
         struct {
             char *name;
+            char *portgroup;
+            virVirtualPortProfileParamsPtr virtPortProfile;
+            /* actual has info about the currently used physical
+             * device (if the network is of type
+             * bridge/private/vepa/passthrough). This is saved in the
+             * domain state, but never written to persistent config,
+             * since it needs to be re-allocated whenever the domain
+             * is restarted. It is also never shown to the user, and
+             * the user cannot specify it in XML documents.
+             */
+            virDomainActualNetDefPtr actual;
         } network;
         struct {
             char *brname;
@@ -1319,6 +1347,7 @@ void virDomainDiskDefFree(virDomainDiskDefPtr def);
 void virDomainDiskHostDefFree(virDomainDiskHostDefPtr def);
 void virDomainControllerDefFree(virDomainControllerDefPtr def);
 void virDomainFSDefFree(virDomainFSDefPtr def);
+void virDomainActualNetDefFree(virDomainActualNetDefPtr def);
 void virDomainNetDefFree(virDomainNetDefPtr def);
 void virDomainSmartcardDefFree(virDomainSmartcardDefPtr def);
 void virDomainChrDefFree(virDomainChrDefPtr def);
@@ -1430,6 +1459,13 @@ int virDomainNetIndexByMac(virDomainDefPtr def, const unsigned char *mac);
 int virDomainNetInsert(virDomainDefPtr def, virDomainNetDefPtr net);
 int virDomainNetRemoveByMac(virDomainDefPtr def, const unsigned char *mac);
 
+int virDomainNetGetActualType(virDomainNetDefPtr iface);
+char *virDomainNetGetActualBridgeName(virDomainNetDefPtr iface);
+char *virDomainNetGetActualDirectDev(virDomainNetDefPtr iface);
+int virDomainNetGetActualDirectMode(virDomainNetDefPtr iface);
+virVirtualPortProfileParamsPtr
+virDomainNetGetActualDirectVirtPortProfile(virDomainNetDefPtr iface);
+
 int virDomainControllerInsert(virDomainDefPtr def,
                               virDomainControllerDefPtr controller);
 void virDomainControllerInsertPreAlloced(virDomainDefPtr def,
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 45ddee2..3898945 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -50,7 +50,7 @@ VIR_ENUM_DECL(virNetworkForward)
 
 VIR_ENUM_IMPL(virNetworkForward,
               VIR_NETWORK_FORWARD_LAST,
-              "none", "nat", "route" )
+              "none", "nat", "route", "bridge", "private", "vepa", "passthrough" )
 
 #define virNetworkReportError(code, ...)                                \
     virReportErrorHelper(VIR_FROM_NETWORK, code, __FILE__,              \
@@ -87,6 +87,19 @@ virNetworkObjPtr virNetworkFindByName(const virNetworkObjListPtr nets,
 }
 
 
+static void
+virPortGroupDefClear(virPortGroupDefPtr def)
+{
+    VIR_FREE(def->name);
+    VIR_FREE(def->virtPortProfile);
+}
+
+static void
+virNetworkForwardIfDefClear(virNetworkForwardIfDefPtr def)
+{
+    VIR_FREE(def->dev);
+}
+
 static void virNetworkIpDefClear(virNetworkIpDefPtr def)
 {
     int ii;
@@ -138,11 +151,21 @@ void virNetworkDefFree(virNetworkDefPtr def)
     VIR_FREE(def->forwardDev);
     VIR_FREE(def->domain);
 
+    for (ii = 0 ; ii < def->nForwardIfs && def->forwardIfs ; ii++) {
+        virNetworkForwardIfDefClear(&def->forwardIfs[ii]);
+    }
+    VIR_FREE(def->forwardIfs);
+
     for (ii = 0 ; ii < def->nips && def->ips ; ii++) {
         virNetworkIpDefClear(&def->ips[ii]);
     }
     VIR_FREE(def->ips);
 
+    for (ii = 0; ii < def->nPortGroups && def->portGroups; ii++) {
+        virPortGroupDefClear(&def->portGroups[ii]);
+    }
+    VIR_FREE(def->portGroups);
+
     virNetworkDNSDefFree(def->dns);
 
     VIR_FREE(def);
@@ -735,14 +758,69 @@ error:
     return result;
 }
 
+static int
+virNetworkPortGroupParseXML(const char *networkName,
+                            virPortGroupDefPtr def,
+                            xmlNodePtr node,
+                            xmlXPathContextPtr ctxt)
+{
+    /*
+     * virPortGroupDef object is already allocated as part of an array.
+     * On failure clear it out, but don't free it.
+     */
+
+    xmlNodePtr save;
+    xmlNodePtr virtPortNode;
+    char *isDefault = NULL;
+
+    int result = -1;
+
+    save = ctxt->node;
+    ctxt->node = node;
+
+    /* grab raw data from XML */
+    def->name = virXPathString("string(./@name)", ctxt);
+    isDefault = virXPathString("string(./@default)", ctxt);
+    def->isDefault = isDefault && STRCASEEQ(isDefault, "yes");
+
+    virtPortNode = virXPathNode("./virtualport", ctxt);
+    if (virtPortNode) {
+        const char *errmsg;
+        if (virVirtualPortProfileParamsParseXML(virtPortNode,
+                                                &def->virtPortProfile,
+                                                &errmsg) < 0) {
+            if (errmsg)
+                virNetworkReportError(VIR_ERR_XML_ERROR, "%s (in network %s)",
+                                      errmsg, networkName);
+            goto error;
+        }
+    }
+
+    result = 0;
+error:
+    if (result < 0) {
+        virPortGroupDefClear(def);
+    }
+    VIR_FREE(isDefault);
+
+    ctxt->node = save;
+    return result;
+}
+
 static virNetworkDefPtr
 virNetworkDefParseXML(xmlXPathContextPtr ctxt)
 {
     virNetworkDefPtr def;
     char *tmp;
+    char *stp = NULL;
     xmlNodePtr *ipNodes = NULL;
+    xmlNodePtr *portGroupNodes = NULL;
+    xmlNodePtr *forwardIfNodes = NULL;
     xmlNodePtr dnsNode = NULL;
-    int nIps;
+    xmlNodePtr virtPortNode = NULL;
+    xmlNodePtr forwardNode = NULL;
+    int nIps, nPortGroups, nForwardIfs;
+    xmlNodePtr save = ctxt->node;
 
     if (VIR_ALLOC(def) < 0) {
         virReportOOMError();
@@ -779,9 +857,7 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
 
     /* Parse bridge information */
     def->bridge = virXPathString("string(./bridge[1]/@name)", ctxt);
-    tmp = virXPathString("string(./bridge[1]/@stp)", ctxt);
-    def->stp = (tmp && STREQ(tmp, "off")) ? 0 : 1;
-    VIR_FREE(tmp);
+    stp = virXPathString("string(./bridge[1]/@stp)", ctxt);
 
     if (virXPathULong("string(./bridge[1]/@delay)", ctxt, &def->delay) < 0)
         def->delay = 0;
@@ -805,6 +881,42 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
             goto error;
     }
 
+    virtPortNode = virXPathNode("./virtualport", ctxt);
+    if (virtPortNode) {
+        const char *errmsg;
+        if (virVirtualPortProfileParamsParseXML(virtPortNode,
+                                                &def->virtPortProfile,
+                                                &errmsg) < 0) {
+            if (errmsg)
+                virNetworkReportError(VIR_ERR_XML_ERROR, "%s", errmsg);
+            goto error;
+        }
+    }
+
+    nPortGroups = virXPathNodeSet("./portgroup", ctxt, &portGroupNodes);
+    if (nPortGroups < 0)
+        goto error;
+
+    if (nPortGroups > 0) {
+        int ii;
+
+        /* allocate array to hold all the portgroups */
+        if (VIR_ALLOC_N(def->portGroups, nPortGroups) < 0) {
+            virReportOOMError();
+            goto error;
+        }
+        /* parse each portgroup */
+        for (ii = 0; ii < nPortGroups; ii++) {
+            int ret = virNetworkPortGroupParseXML(def->name,
+                                                  &def->portGroups[ii],
+                                                  portGroupNodes[ii], ctxt);
+            if (ret < 0)
+                goto error;
+            def->nPortGroups++;
+        }
+    }
+    VIR_FREE(portGroupNodes);
+
     nIps = virXPathNodeSet("./ip", ctxt, &ipNodes);
     if (nIps < 0)
         goto error;
@@ -828,17 +940,16 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
     }
     VIR_FREE(ipNodes);
 
-    /* IPv4 forwarding setup */
-    if (virXPathBoolean("count(./forward) > 0", ctxt)) {
-        if (def->nips == 0) {
-            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
-                                  "%s", _("Forwarding requested, but no IP address provided"));
-            goto error;
-        }
-        tmp = virXPathString("string(./forward[1]/@mode)", ctxt);
+    forwardNode = virXPathNode("./forward", ctxt);
+    if (!forwardNode) {
+        def->forwardType = VIR_NETWORK_FORWARD_NONE;
+        def->stp = (stp && STREQ(stp, "off")) ? 0 : 1;
+    } else {
+        ctxt->node = forwardNode;
+        tmp = virXPathString("string(./@mode)", ctxt);
         if (tmp) {
             if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) {
-                virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
+                virNetworkReportError(VIR_ERR_XML_ERROR,
                                       _("unknown forwarding type '%s'"), tmp);
                 VIR_FREE(tmp);
                 goto error;
@@ -848,17 +959,85 @@ virNetworkDefParseXML(xmlXPathContextPtr ctxt)
             def->forwardType = VIR_NETWORK_FORWARD_NAT;
         }
 
+        def->forwardDev = virXPathString("string(./@dev)", ctxt);
 
-        def->forwardDev = virXPathString("string(./forward[1]/@dev)", ctxt);
-    } else {
-        def->forwardType = VIR_NETWORK_FORWARD_NONE;
-    }
+        switch (def->forwardType) {
+        case VIR_NETWORK_FORWARD_ROUTE:
+        case VIR_NETWORK_FORWARD_NAT:
+            /* It's pointless to specify L3 forwarding without specifying
+             * the network we're on.
+             */
+            if (def->nips == 0) {
+                virNetworkReportError(VIR_ERR_XML_ERROR,
+                                      _("%s forwarding requested, but no IP address provided for network '%s'"),
+                                      virNetworkForwardTypeToString(def->forwardType),
+                                      def->name);
+                goto error;
+            }
+            def->stp = (stp && STREQ(stp, "off")) ? 0 : 1;
+            break;
+        case VIR_NETWORK_FORWARD_PRIVATE:
+        case VIR_NETWORK_FORWARD_VEPA:
+        case VIR_NETWORK_FORWARD_PASSTHROUGH:
+            if (def->bridge) {
+                virNetworkReportError(VIR_ERR_XML_ERROR,
+                                      _("bridge name not allowed in %s mode (network '%s'"),
+                                      virNetworkForwardTypeToString(def->forwardType),
+                                      def->name);
+                goto error;
+            }
+            /* Fall through to next case */
+        case VIR_NETWORK_FORWARD_BRIDGE:
+            if (def->delay || stp) {
+                virNetworkReportError(VIR_ERR_XML_ERROR,
+                                      _("bridge delay/stp options only allowed in route, nat, and isolated mode, not in %s (network '%s')"),
+                                      virNetworkForwardTypeToString(def->forwardType),
+                                      def->name);
+                goto error;
+            }
+            /* all of these modes can use a pool of physical interfaces */
+            nForwardIfs = virXPathNodeSet("./interface", ctxt, &forwardIfNodes);
+            if (nForwardIfs < 0)
+                goto error;
+
+            if (nForwardIfs > 0) {
+                int ii;
 
+                /* allocate array to hold all the portgroups */
+                if (VIR_ALLOC_N(def->forwardIfs, nForwardIfs) < 0) {
+                    virReportOOMError();
+                    goto error;
+                }
+
+                /* parse each forwardIf */
+                for (ii = 0; ii < nForwardIfs; ii++) {
+                    def->forwardIfs[ii].usageCount = 0;
+                    def->forwardIfs[ii].dev = virXMLPropString(forwardIfNodes[ii],
+                                                               "dev");
+                    if (!def->forwardIfs[ii].dev) {
+                        virNetworkReportError(VIR_ERR_XML_ERROR,
+                                              _("Missing required dev attribute in network '%s' forward interface"),
+                                              def->name);
+                        goto error;
+                    }
+                    def->nForwardIfs++;
+                }
+            }
+            VIR_FREE(forwardIfNodes);
+            break;
+        }
+    }
+    VIR_FREE(stp);
+    ctxt->node = save;
     return def;
 
  error:
+    VIR_FREE(stp);
     virNetworkDefFree(def);
     VIR_FREE(ipNodes);
+    VIR_FREE(portGroupNodes);
+    VIR_FREE(forwardIfNodes);
+    ctxt->node = save;
     return NULL;
 }
 
@@ -1043,6 +1222,19 @@ error:
     return result;
 }
 
+static void
+virPortGroupDefFormat(virBufferPtr buf,
+                      const virPortGroupDefPtr def)
+{
+    virBufferAsprintf(buf, "  <portgroup name='%s'", def->name);
+    if (def->isDefault) {
+        virBufferAddLit(buf, " default='yes'");
+    }
+    virBufferAddLit(buf, ">\n");
+    virVirtualPortProfileFormat(buf, def->virtPortProfile, "    ");
+    virBufferAddLit(buf, "  </portgroup>\n");
+}
+
 char *virNetworkDefFormat(const virNetworkDefPtr def)
 {
     virBuffer buf = VIR_BUFFER_INITIALIZER;
@@ -1058,24 +1250,55 @@ char *virNetworkDefFormat(const virNetworkDefPtr def)
     virBufferAsprintf(&buf, "  <uuid>%s</uuid>\n", uuidstr);
 
     if (def->forwardType != VIR_NETWORK_FORWARD_NONE) {
+        char *dev = def->forwardDev;
         const char *mode = virNetworkForwardTypeToString(def->forwardType);
-        if (mode) {
-            if (def->forwardDev) {
-                virBufferEscapeString(&buf, "  <forward dev='%s'",
-                                      def->forwardDev);
-            } else {
-                virBufferAddLit(&buf, "  <forward");
+        if (!mode) {
+            virNetworkReportError(VIR_ERR_INTERNAL_ERROR,
+                                  _("Unknown forward type %d in network '%s'"),
+                                  def->forwardType, def->name);
+            goto error;
+        }
+        virBufferAddLit(&buf, "  <forward");
+
+        /* Duplicate the first interface from the pool into <forward
+         * dev=xxx for convenience.
+         */
+        if (!dev && def->nForwardIfs &&
+            def->forwardIfs && def->forwardIfs[0].dev) {
+            dev = def->forwardIfs[0].dev;
+        }
+        if (dev)
+            virBufferEscapeString(&buf, " dev='%s'", dev);
+        virBufferAsprintf(&buf, " mode='%s'%s>\n", mode,
+                          def->nForwardIfs ? "" : "/");
+
+        if (def->nForwardIfs) {
+            for (ii = 0; ii < def->nForwardIfs; ii++) {
+                if (def->forwardIfs[ii].dev) {
+                    virBufferEscapeString(&buf, "    <interface dev='%s'/>\n",
+                                          def->forwardIfs[ii].dev);
+                }
             }
-            virBufferAsprintf(&buf, " mode='%s'/>\n", mode);
+            virBufferAddLit(&buf, "  </forward>\n");
         }
     }
 
-    virBufferAddLit(&buf, "  <bridge");
-    if (def->bridge)
-        virBufferEscapeString(&buf, " name='%s'", def->bridge);
-    virBufferAsprintf(&buf, " stp='%s' delay='%ld' />\n",
-                      def->stp ? "on" : "off",
-                      def->delay);
+    if (def->forwardType == VIR_NETWORK_FORWARD_NONE ||
+         def->forwardType == VIR_NETWORK_FORWARD_NAT ||
+         def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
+
+        virBufferAddLit(&buf, "  <bridge");
+        if (def->bridge)
+            virBufferEscapeString(&buf, " name='%s'", def->bridge);
+        virBufferAsprintf(&buf, " stp='%s' delay='%ld' />\n",
+                          def->stp ? "on" : "off",
+                          def->delay);
+    } else if (def->forwardType == VIR_NETWORK_FORWARD_BRIDGE &&
+               def->bridge) {
+       virBufferEscapeString(&buf, "  <bridge name='%s' />\n", def->bridge);
+    }
+
+
     if (def->mac_specified) {
         char macaddr[VIR_MAC_STRING_BUFLEN];
         virFormatMacAddr(def->mac, macaddr);
@@ -1093,6 +1316,11 @@ char *virNetworkDefFormat(const virNetworkDefPtr def)
             goto error;
     }
 
+    virVirtualPortProfileFormat(&buf, def->virtPortProfile, "  ");
+
+    for (ii = 0; ii < def->nPortGroups; ii++)
+        virPortGroupDefFormat(&buf, &def->portGroups[ii]);
+
     virBufferAddLit(&buf, "</network>\n");
 
     if (virBufferError(&buf))
@@ -1107,6 +1335,22 @@ char *virNetworkDefFormat(const virNetworkDefPtr def)
     return NULL;
 }
 
+virPortGroupDefPtr virPortGroupFindByName(virNetworkDefPtr net,
+                                          const char *portgroup)
+{
+    int ii;
+    for (ii = 0; ii < net->nPortGroups; ii++) {
+        if (portgroup) {
+            if (STREQ(portgroup, net->portGroups[ii].name))
+                return &net->portGroups[ii];
+        } else {
+            if (net->portGroups[ii].isDefault)
+                return &net->portGroups[ii];
+        }
+    }
+    return NULL;
+}
+
 int virNetworkSaveXML(const char *configDir,
                       virNetworkDefPtr def,
                       const char *xml)
@@ -1210,11 +1454,16 @@ virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets,
         goto error;
     }
 
-    /* Generate a bridge if none is specified, but don't check for collisions
-     * if a bridge is hardcoded, so the network is at least defined
-     */
-    if (virNetworkSetBridgeName(nets, def, 0))
-        goto error;
+    if (def->forwardType == VIR_NETWORK_FORWARD_NONE ||
+        def->forwardType == VIR_NETWORK_FORWARD_NAT ||
+        def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
+
+        /* Generate a bridge if none is specified, but don't check for collisions
+         * if a bridge is hardcoded, so the network is at least defined.
+         */
+        if (virNetworkSetBridgeName(nets, def, 0))
+            goto error;
+    }
 
     if (!(net = virNetworkAssignDef(nets, def)))
         goto error;
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index d7d2951..5df6724 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -33,11 +33,14 @@
 # include "network.h"
 # include "util.h"
 
-/* 2 possible types of forwarding */
 enum virNetworkForwardType {
     VIR_NETWORK_FORWARD_NONE   = 0,
     VIR_NETWORK_FORWARD_NAT,
     VIR_NETWORK_FORWARD_ROUTE,
+    VIR_NETWORK_FORWARD_BRIDGE,
+    VIR_NETWORK_FORWARD_PRIVATE,
+    VIR_NETWORK_FORWARD_VEPA,
+    VIR_NETWORK_FORWARD_PASSTHROUGH,
 
     VIR_NETWORK_FORWARD_LAST,
 };
@@ -107,6 +110,21 @@ struct _virNetworkIpDef {
     virSocketAddr bootserver;
    };
 
+typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef;
+typedef virNetworkForwardIfDef *virNetworkForwardIfDefPtr;
+struct _virNetworkForwardIfDef {
+    char *dev;      /* name of device */
+    int usageCount; /* how many guest interfaces are bound to this device? */
+};
+
+typedef struct _virPortGroupDef virPortGroupDef;
+typedef virPortGroupDef *virPortGroupDefPtr;
+struct _virPortGroupDef {
+    char *name;
+    bool isDefault;
+    virVirtualPortProfileParamsPtr virtPortProfile;
+};
+
 typedef struct _virNetworkDef virNetworkDef;
 typedef virNetworkDef *virNetworkDefPtr;
 struct _virNetworkDef {
@@ -121,12 +139,22 @@ struct _virNetworkDef {
     bool mac_specified;
 
     int forwardType;    /* One of virNetworkForwardType constants */
-    char *forwardDev;   /* Destination device for forwarding */
+    char *forwardDev;   /* Destination device for forwarding (if just one) */
+
+    /* If there are multiple forward devices (i.e. a pool of
+     * interfaces), they will be listed here.
+     */
+    size_t nForwardIfs;
+    virNetworkForwardIfDefPtr forwardIfs;
 
     size_t nips;
     virNetworkIpDefPtr ips; /* ptr to array of IP addresses on this network */
 
     virNetworkDNSDefPtr dns; /* ptr to dns related configuration */
+    virVirtualPortProfileParamsPtr virtPortProfile;
+
+    size_t nPortGroups;
+    virPortGroupDefPtr portGroups;
 };
 
 typedef struct _virNetworkObj virNetworkObj;
@@ -151,6 +179,8 @@ struct _virNetworkObjList {
     virNetworkObjPtr *objs;
 };
 
+virPortGroupDefPtr virPortGroupFindByName(virNetworkDefPtr net,
+                                          const char *portgroup);
 static inline int
 virNetworkObjIsActive(const virNetworkObjPtr net)
 {
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index a7540d2..3b8214a 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -205,6 +205,7 @@ dnsmasqSave;
 # domain_conf.h
 virDiskNameToBusDeviceIndex;
 virDiskNameToIndex;
+virDomainActualNetDefFree;
 virDomainAssignDef;
 virDomainChrConsoleTargetTypeFromString;
 virDomainChrConsoleTargetTypeToString;
@@ -309,6 +310,11 @@ virDomainLoadAllConfigs;
 virDomainMemballoonModelTypeFromString;
 virDomainMemballoonModelTypeToString;
 virDomainNetDefFree;
+virDomainNetGetActualBridgeName;
+virDomainNetGetActualDirectDev;
+virDomainNetGetActualDirectMode;
+virDomainNetGetActualType;
+virDomainNetGetActualDirectVirtPortProfile;
 virDomainNetIndexByMac;
 virDomainNetInsert;
 virDomainNetRemoveByMac;
@@ -722,7 +728,7 @@ virNetworkRemoveInactive;
 virNetworkSaveConfig;
 virNetworkSetBridgeMacAddr;
 virNetworkSetBridgeName;
-
+virPortGroupFindByName;
 
 # node_device_conf.h
 virNodeDevCapTypeToString;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 660dd65..cb49356 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -2169,10 +2169,18 @@ static virNetworkPtr networkCreate(virConnectPtr conn, const char *xml) {
     if (virNetworkObjIsDuplicate(&driver->networks, def, 1) < 0)
         goto cleanup;
 
-    if (virNetworkSetBridgeName(&driver->networks, def, 1))
-        goto cleanup;
+    /* Only the three L3 network types that are configured by libvirt
+     * need to have a bridge device name / mac address provided
+     */
+    if (def->forwardType == VIR_NETWORK_FORWARD_NONE ||
+        def->forwardType == VIR_NETWORK_FORWARD_NAT ||
+        def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
 
-    virNetworkSetBridgeMacAddr(def);
+        if (virNetworkSetBridgeName(&driver->networks, def, 1))
+            goto cleanup;
+
+        virNetworkSetBridgeMacAddr(def);
+    }
 
     if (!(network = virNetworkAssignDef(&driver->networks,
                                         def)))
@@ -2214,10 +2222,18 @@ static virNetworkPtr networkDefine(virConnectPtr conn, const char *xml) {
     if (virNetworkObjIsDuplicate(&driver->networks, def, 0) < 0)
         goto cleanup;
 
-    if (virNetworkSetBridgeName(&driver->networks, def, 1))
-        goto cleanup;
+    /* Only the three L3 network types that are configured by libvirt
+     * need to have a bridge device name / mac address provided
+     */
+    if (def->forwardType == VIR_NETWORK_FORWARD_NONE ||
+        def->forwardType == VIR_NETWORK_FORWARD_NAT ||
+        def->forwardType == VIR_NETWORK_FORWARD_ROUTE) {
 
-    virNetworkSetBridgeMacAddr(def);
+        if (virNetworkSetBridgeName(&driver->networks, def, 1))
+            goto cleanup;
+
+        virNetworkSetBridgeMacAddr(def);
+    }
 
     if (!(network = virNetworkAssignDef(&driver->networks,
                                         def)))
-- 
1.7.3.4




More information about the libvir-list mailing list