[libvirt] [PATCH v5 02/24] conf: introduce virNetworkPortDefPtr struct and XML support

Daniel P. Berrangé berrange at redhat.com
Tue May 14 15:48:14 UTC 2019


Introduce a virNetworkPortDefPtr struct to represent the data associated
with a virtual network port. Add APIs for parsing/formatting XML docs
with the data.

Signed-off-by: Daniel P. Berrangé <berrange at redhat.com>
---
 docs/docs.html.in                             |   1 +
 docs/formatnetworkport.html.in                | 212 ++++++++
 docs/schemas/networkport.rng                  | 165 ++++++
 src/conf/Makefile.inc.am                      |   2 +
 src/conf/virnetworkportdef.c                  | 509 ++++++++++++++++++
 src/conf/virnetworkportdef.h                  | 113 ++++
 src/libvirt_private.syms                      |  10 +
 tests/Makefile.am                             |   7 +
 .../plug-bridge-mactbl.xml                    |   9 +
 .../virnetworkportxml2xmldata/plug-bridge.xml |  15 +
 .../virnetworkportxml2xmldata/plug-direct.xml |  12 +
 .../plug-hostdev-pci.xml                      |  12 +
 .../plug-network.xml                          |  16 +
 tests/virnetworkportxml2xmldata/plug-none.xml |   8 +
 tests/virnetworkportxml2xmltest.c             | 104 ++++
 tests/virschematest.c                         |   1 +
 16 files changed, 1196 insertions(+)
 create mode 100644 docs/formatnetworkport.html.in
 create mode 100644 docs/schemas/networkport.rng
 create mode 100644 src/conf/virnetworkportdef.c
 create mode 100644 src/conf/virnetworkportdef.h
 create mode 100644 tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml
 create mode 100644 tests/virnetworkportxml2xmldata/plug-bridge.xml
 create mode 100644 tests/virnetworkportxml2xmldata/plug-direct.xml
 create mode 100644 tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml
 create mode 100644 tests/virnetworkportxml2xmldata/plug-network.xml
 create mode 100644 tests/virnetworkportxml2xmldata/plug-none.xml
 create mode 100644 tests/virnetworkportxml2xmltest.c

diff --git a/docs/docs.html.in b/docs/docs.html.in
index d0ff844d0c..c8674e1457 100644
--- a/docs/docs.html.in
+++ b/docs/docs.html.in
@@ -72,6 +72,7 @@
         <dd>Description of the XML schemas for
           <a href="formatdomain.html">domains</a>,
           <a href="formatnetwork.html">networks</a>,
+          <a href="formatnetworkport.html">network ports</a>,
           <a href="formatnwfilter.html">network filtering</a>,
           <a href="formatstorage.html">storage</a>,
           <a href="formatstorageencryption.html">storage encryption</a>,
diff --git a/docs/formatnetworkport.html.in b/docs/formatnetworkport.html.in
new file mode 100644
index 0000000000..0211518a6a
--- /dev/null
+++ b/docs/formatnetworkport.html.in
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <body>
+    <h1>Network XML format</h1>
+
+    <ul id="toc">
+    </ul>
+
+    <p>
+      This page provides an introduction to the network port XML format.
+      This stores information about the connection between an virtual
+      interface on a virtual domain's, and the virtual network it is
+      attached to.
+    </p>
+
+    <h2><a id="elements">Element and attribute overview</a></h2>
+
+    <p>
+      The root element required for all virtual network ports is
+      named <code>networkport</code> and has no configurable attributes
+      The network port XML format is available <span class="since">since
+      5.4.0</span>
+    </p>
+
+    <h3><a id="elementsMetadata">General metadata</a></h3>
+
+    <p>
+      The first elements provide basic metadata about the virtual
+      network port.
+    </p>
+
+    <pre>
+<networkport
+  <uuid>7ae63b5f-fe96-4af0-a7c3-da04ba1b3f54</uuid>
+  <owner>
+    <uuid>06578fc1-c686-46fa-bc2c-220893b466a6</uuid>
+    <name>myguest<name>
+  </owner>
+  <group>webfront<group>
+  <mac address='52:54:0:7b:35:93'/>
+  ...</pre>
+
+    <dl>
+      <dt><code>uuid</code></dt>
+      <dd>The content of the <code>uuid</code> element provides
+        a globally unique identifier for the virtual network port.
+        The format must be RFC 4122 compliant, eg <code>3e3fce45-4f53-4fa7-bb32-11f34168b82b</code>.
+        If omitted when defining/creating a new network port, a random
+        UUID is generated.</dd>
+      <dd>The <code>owner</code> node records the domain object that
+        is the owner of the network port. It contains two child nodes:
+        <dl>
+          <dt><code>uuid</code></dt>
+          <dd>The content of the <code>uuid</code> element provides
+            a globally unique identifier for the virtual domain.</dd>
+          <dt><code>name</code></dt>
+          <dd>The unique name of the virtual domain</dd>
+        </dl>
+      </dd>
+      <dt><code>group</code></dt>
+      <dd>The port group in the virtual network to which the
+        port belongs. Can be omitted if no port groups are
+        defined on the network.</dd>
+      <dt><code>mac</code></dt>
+      <dd>The <code>address</code> attribute provides the MAC
+        address of the virtual port that will be see by the
+        guest. The MAC address must not start with 0xFE as this
+        byte is reserved for use on the host side of the port.
+      </dd>
+    </dl>
+
+    <h3><a id="elementsCommon">Common elements</a></h3>
+
+    <p>
+      The following elements are common to one of more of the plug
+      types listed later
+    </p>
+
+    <pre>
+  ...
+  <bandwidth>
+    <inbound average='1000' peak='5000' floor='200' burst='1024'/>
+    <outbound average='128' peak='256' burst='256'/>
+  </bandwidth>
+  <rxfilters trustGuest='yes'/>
+  <virtualport type='802.1Qbg'>
+    <parameters managerid='11' typeid='1193047' typeidversion='2'/>
+  </virtualport>
+  ...</pre>
+
+    <dl>
+      <dt><code>bandwidth</code></dt>
+      <dd>This part of the network port XML provides setting quality of service.
+        Incoming and outgoing traffic can be shaped independently.
+        The <code>bandwidth</code> element and its child elements are described
+        in the <a href="formatnetwork.html#elementQoS">QoS</a> section of
+        the Network XML. In addition the <code>classID</code> attribute may
+        exist provide the ID of the traffic shaping class that is active.
+      </dd>
+      <dt><code>rxfilters</code></dt>
+      <dd>The <code>rxfilters</code> element property
+        <code>trustGuestRxFilters</code> provides the
+        capability for the host to detect and trust reports from the
+        guest regarding changes to the interface mac address and receive
+        filters by setting the attribute to <code>yes</code>. The default
+        setting for the attribute is <code>no</code> for security
+        reasons and support depends on the guest network device model as
+        well as the type of connection on the host - currently it is
+        only supported for the virtio device model and for macvtap
+        connections on the host.
+      </dd>
+      <dt><code>virtualport</code></dt>
+      <dd>The <code>virtualport</code> element describes metadata that
+        needs to be provided to the underlying network subsystem. It
+        is described in the domain XML
+        <a href="formatdomain.html#elementsNICS">interface documentation</a>.
+      </dd>
+    </dl>
+
+
+    <h3><a id="elementsPlug">Plugs</a></h3>
+
+    <p>
+      The <code>plug</code> element has varying content depending
+      on the value of the <code>type</code> attribute.
+    </p>
+
+    <h4><a id="elementsPlugNetwork">Network</a></h4>
+
+    <p>
+      The <code>network</code> plug type refers to a managed virtual
+      network plug that is based on a traditional software bridge
+      device privately managed by libvirt.
+    </p>
+
+    <pre>
+  ...
+  <plug type='network' bridge='virbr0'>
+  ...</pre>
+
+    <p>
+      The <code>bridge</code> attribute provides the name of the
+      privately managed bridge device associated with the virtual
+      network.
+    </p>
+
+    <h4><a id="elementsPlugNetwork">Bridge</a></h4>
+
+    <p>
+      The <code>bridge</code> plug type refers to an externally
+      managed traditional software bridge.
+    </p>
+
+    <pre>
+  ...
+  <plug type='bridge' bridge='br2'>
+  ...</pre>
+
+    <p>
+      The <code>bridge</code> attribute provides the name of the
+      externally managed bridge device associated with the virtual
+      network.
+    </p>
+
+    <h4><a id="elementsPlugNetwork">Direct</a></h4>
+
+    <p>
+      The <code>direct</code> plug type refers to an connection
+      directly to a physical network interface.
+    </p>
+
+    <pre>
+  ...
+  <plug type='direct' dev='ens3' mode='vepa'/>
+  ...</pre>
+
+    <p>
+      The <code>dev</code> attribute provides the name of the
+      physical network interface to which the port will be
+      connected. The <code>mode</code> attribute describes
+      how the connection will be setup and takes the same
+      values described in the
+      <a href="formatdomain.html#elementsNICSDirect">domain XML</a>.
+    </p>
+
+    <h4><a id="elementsPlugNetwork">Host PCI</a></h4>
+
+    <p>
+      The <code>hostdev-pci</code> plug type refers to the
+      passthrough of a physical PCI device rather than emulation.
+    </p>
+
+    <pre>
+  ...
+  <plug type='hostdev-pci' managed='yes'>
+    <driver name='vfio'/>
+    <address domain='0x0001' bus='0x02' slot='0x03' function='0x4'/>
+  </plug>
+  ...</pre>
+
+    <p>
+      The <code>managed</code> attribute indicates who is responsible for
+      managing the PCI device in the host. When set to the value <code>yes</code>
+      libvirt is responsible for automatically detaching the device from host
+      drivers and resetting it if needed. If the value is <code>no</code>,
+      some other party must ensure the device is not attached to any
+      host drivers.
+    </p>
+
+  </body>
+</html>
diff --git a/docs/schemas/networkport.rng b/docs/schemas/networkport.rng
new file mode 100644
index 0000000000..8192f6efc4
--- /dev/null
+++ b/docs/schemas/networkport.rng
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<!-- A Relax NG schema for the libvirt network port XML format -->
+<grammar xmlns="http://relaxng.org/ns/structure/1.0"
+         datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">
+  <include href='basictypes.rng'/>
+  <include href='networkcommon.rng'/>
+
+  <start>
+    <ref name="networkport"/>
+  </start>
+
+  <define name="networkport">
+    <element name="networkport">
+      <interleave>
+        <element name="uuid">
+          <ref name="UUID"/>
+        </element>
+        <ref name="owner"/>
+        <ref name="mac"/>
+        <optional>
+          <ref name="group"/>
+        </optional>
+        <optional>
+          <ref name="class"/>
+        </optional>
+        <optional>
+          <ref name="rxfilters"/>
+        </optional>
+        <optional>
+          <ref name="virtualPortProfile"/>
+        </optional>
+        <optional>
+          <ref name="bandwidth"/>
+        </optional>
+        <optional>
+          <ref name="plug"/>
+        </optional>
+      </interleave>
+    </element>
+  </define>
+
+  <define name="owner">
+    <element name="owner">
+      <element name="name">
+        <text/>
+      </element>
+      <element name="uuid">
+        <ref name="UUID"/>
+      </element>
+    </element>
+  </define>
+
+  <define name="mac">
+    <element name="mac">
+      <attribute name="address">
+        <ref name="uniMacAddr"/>
+      </attribute>
+      <empty/>
+    </element>
+  </define>
+
+  <define name="group">
+    <element name="group">
+      <ref name="deviceName"/>
+    </element>
+  </define>
+
+  <define name="class">
+    <element name="class">
+      <attribute name="id">
+        <ref name="positiveInteger"/>
+      </attribute>
+    </element>
+  </define>
+
+  <define name="rxfilters">
+    <element name="rxfilters">
+      <attribute name="trustGuest">
+        <ref name="virYesNo"/>
+      </attribute>
+    </element>
+  </define>
+
+  <define name="plug">
+    <element name="plug">
+      <choice>
+        <ref name="plugnetwork"/>
+        <ref name="plugbridge"/>
+        <ref name="plugdirect"/>
+        <ref name="plughostdevpci"/>
+      </choice>
+    </element>
+  </define>
+
+  <define name="plugnetwork">
+    <attribute name="type">
+      <value>network</value>
+    </attribute>
+    <attribute name="bridge">
+      <ref name="deviceName"/>
+    </attribute>
+    <optional>
+      <attribute name="macTableManager">
+        <ref name="macTableManager"/>
+      </attribute>
+    </optional>
+  </define>
+
+  <define name="plugbridge">
+    <attribute name="type">
+      <value>bridge</value>
+    </attribute>
+    <attribute name="bridge">
+      <ref name="deviceName"/>
+    </attribute>
+    <optional>
+      <attribute name="macTableManager">
+        <ref name="macTableManager"/>
+      </attribute>
+    </optional>
+  </define>
+
+  <define name="plugdirect">
+    <attribute name="type">
+      <value>direct</value>
+    </attribute>
+    <attribute name="dev">
+      <ref name="deviceName"/>
+    </attribute>
+    <attribute name="mode">
+      <choice>
+        <value>bridge</value>
+        <value>passthrough</value>
+        <value>private</value>
+        <value>vepa</value>
+      </choice>
+    </attribute>
+  </define>
+
+  <define name="plughostdevpci">
+    <attribute name="type">
+      <value>hostdev-pci</value>
+    </attribute>
+    <optional>
+      <attribute name="managed">
+        <ref name="virYesNo"/>
+      </attribute>
+    </optional>
+    <optional>
+      <element name="driver">
+        <attribute name="name">
+          <choice>
+            <value>kvm</value>
+            <value>vfio</value>
+          </choice>
+        </attribute>
+        <empty/>
+      </element>
+    </optional>
+    <element name='address'>
+      <ref name="pciaddress"/>
+    </element>
+  </define>
+
+</grammar>
diff --git a/src/conf/Makefile.inc.am b/src/conf/Makefile.inc.am
index 3e9fdd1aea..6b52ba674b 100644
--- a/src/conf/Makefile.inc.am
+++ b/src/conf/Makefile.inc.am
@@ -7,6 +7,8 @@ NETDEV_CONF_SOURCES = \
 	conf/netdev_vport_profile_conf.c \
 	conf/netdev_vlan_conf.h \
 	conf/netdev_vlan_conf.c \
+	conf/virnetworkportdef.h \
+	conf/virnetworkportdef.c \
 	$(NULL)
 
 DOMAIN_CONF_SOURCES = \
diff --git a/src/conf/virnetworkportdef.c b/src/conf/virnetworkportdef.c
new file mode 100644
index 0000000000..6c896968ce
--- /dev/null
+++ b/src/conf/virnetworkportdef.c
@@ -0,0 +1,509 @@
+/*
+ * virnetworkportdef.c: network port XML processing
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "viralloc.h"
+#include "virerror.h"
+#include "virstring.h"
+#include "virfile.h"
+#include "virnetworkportdef.h"
+#include "network_conf.h"
+
+#define VIR_FROM_THIS VIR_FROM_NETWORK
+
+VIR_ENUM_IMPL(virNetworkPortPlug,
+              VIR_NETWORK_PORT_PLUG_TYPE_LAST,
+              "none", "network", "bridge", "direct", "hostdev-pci");
+
+void
+virNetworkPortDefFree(virNetworkPortDefPtr def)
+{
+    if (!def)
+        return;
+
+    VIR_FREE(def->ownername);
+    VIR_FREE(def->group);
+
+    virNetDevBandwidthFree(def->bandwidth);
+    virNetDevVlanClear(&def->vlan);
+    VIR_FREE(def->virtPortProfile);
+
+    switch ((virNetworkPortPlugType)def->plugtype) {
+    case VIR_NETWORK_PORT_PLUG_TYPE_NONE:
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_NETWORK:
+    case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE:
+        VIR_FREE(def->plug.bridge.brname);
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT:
+        VIR_FREE(def->plug.direct.linkdev);
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
+    default:
+        break;
+    }
+
+    VIR_FREE(def);
+}
+
+
+
+static virNetworkPortDefPtr
+virNetworkPortDefParseXML(xmlXPathContextPtr ctxt)
+{
+    virNetworkPortDefPtr def;
+    char *uuid = NULL;
+    xmlNodePtr virtPortNode;
+    xmlNodePtr vlanNode;
+    xmlNodePtr bandwidthNode;
+    xmlNodePtr addressNode;
+    char *trustGuestRxFilters = NULL;
+    char *mac = NULL;
+    char *macmgr = NULL;
+    char *mode = NULL;
+    char *plugtype = NULL;
+    char *managed = NULL;
+    char *driver = NULL;
+    char *class_id = NULL;
+
+    if (VIR_ALLOC(def) < 0)
+        return NULL;
+
+    uuid = virXPathString("string(./uuid)", ctxt);
+    if (!uuid) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("network port has no uuid"));
+        goto error;
+    }
+    if (virUUIDParse(uuid, def->uuid) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to parse UUID '%s'"), uuid);
+        goto error;
+    }
+
+    def->ownername = virXPathString("string(./owner/name)", ctxt);
+    if (!def->ownername) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("network port has no owner name"));
+        goto error;
+    }
+
+    VIR_FREE(uuid);
+    uuid = virXPathString("string(./owner/uuid)", ctxt);
+    if (!uuid) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("network port has no owner UUID"));
+        goto error;
+    }
+
+    if (virUUIDParse(uuid, def->owneruuid) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to parse UUID '%s'"), uuid);
+        goto error;
+    }
+
+    def->group = virXPathString("string(./group)", ctxt);
+
+    virtPortNode = virXPathNode("./virtualport", ctxt);
+    if (virtPortNode &&
+        (!(def->virtPortProfile = virNetDevVPortProfileParse(virtPortNode, 0)))) {
+        goto error;
+    }
+
+    mac = virXPathString("string(./mac/@address)", ctxt);
+    if (!mac) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       "%s", _("network port has no mac"));
+        goto error;
+    }
+    if (virMacAddrParse(mac, &def->mac) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unable to parse MAC '%s'"), mac);
+        goto error;
+    }
+
+    bandwidthNode = virXPathNode("./bandwidth", ctxt);
+    /*
+     * We don't know if the port will allow the "floor" param or
+     * not at this stage, so we must just tell virNetDevBandwidthParse
+     * to allow it regardless. Any bad config must be reported at
+     * time of use instead.
+     */
+    if (bandwidthNode &&
+        virNetDevBandwidthParse(&def->bandwidth, &def->class_id,
+                                bandwidthNode, true) < 0)
+        goto error;
+
+    vlanNode = virXPathNode("./vlan", ctxt);
+    if (vlanNode && virNetDevVlanParse(vlanNode, ctxt, &def->vlan) < 0)
+        goto error;
+
+
+    trustGuestRxFilters
+        = virXPathString("string(./rxfilters/@trustGuest)", ctxt);
+    if (trustGuestRxFilters) {
+        if ((def->trustGuestRxFilters
+             = virTristateBoolTypeFromString(trustGuestRxFilters)) <= 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("Invalid guest rx filters trust setting '%s' "),
+                           trustGuestRxFilters);
+            goto error;
+        }
+    }
+
+    plugtype = virXPathString("string(./plug/@type)", ctxt);
+
+    if (plugtype &&
+        (def->plugtype = virNetworkPortPlugTypeFromString(plugtype)) < 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid network prt plug type '%s'"), plugtype);
+    }
+
+    switch (def->plugtype) {
+    case VIR_NETWORK_PORT_PLUG_TYPE_NONE:
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_NETWORK:
+    case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE:
+        if (!(def->plug.bridge.brname = virXPathString("string(./plug/@bridge)", ctxt))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Missing network port bridge name"));
+            goto error;
+        }
+        macmgr = virXPathString("string(./plug/@macTableManager)", ctxt);
+        if (macmgr &&
+            (def->plug.bridge.macTableManager =
+             virNetworkBridgeMACTableManagerTypeFromString(macmgr)) <= 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("Invalid macTableManager setting '%s' "
+                             "in network port"), macmgr);
+            goto error;
+        }
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT:
+        if (!(def->plug.direct.linkdev = virXPathString("string(./plug/@dev)", ctxt))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Missing network port link device name"));
+            goto error;
+        }
+        mode = virXPathString("string(./plug/@mode)", ctxt);
+        if (mode &&
+            (def->plug.direct.mode =
+             virNetDevMacVLanModeTypeFromString(mode)) < 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("Invalid mode setting '%s' in network port"), mode);
+            goto error;
+        }
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
+        managed = virXPathString("string(./plug/@managed)", ctxt);
+        if (managed &&
+            (def->plug.hostdevpci.managed =
+             virTristateBoolTypeFromString(managed)) < 0) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("Invalid managed setting '%s' in network port"), mode);
+            goto error;
+        }
+        driver = virXPathString("string(./plug/driver/@name)", ctxt);
+        if (driver &&
+            (def->plug.hostdevpci.driver =
+             virNetworkForwardDriverNameTypeFromString(driver)) <= 0) {
+              virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Missing network port driver name"));
+            goto error;
+        }
+        if (!(addressNode = virXPathNode("./plug/address", ctxt))) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Missing network port PCI address"));
+            goto error;
+        }
+
+        if (virPCIDeviceAddressParseXML(addressNode, &def->plug.hostdevpci.addr) < 0)
+            goto error;
+        break;
+
+    case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
+    default:
+        virReportEnumRangeError(virNetworkPortPlugType, def->plugtype);
+        goto error;
+    }
+
+ cleanup:
+    VIR_FREE(class_id);
+    VIR_FREE(uuid);
+    VIR_FREE(plugtype);
+    VIR_FREE(mac);
+    VIR_FREE(mode);
+    VIR_FREE(macmgr);
+    VIR_FREE(driver);
+    VIR_FREE(managed);
+    return def;
+
+ error:
+    virNetworkPortDefFree(def);
+    def = NULL;
+    goto cleanup;
+}
+
+
+virNetworkPortDefPtr
+virNetworkPortDefParseNode(xmlDocPtr xml,
+                           xmlNodePtr root)
+{
+    xmlXPathContextPtr ctxt = NULL;
+    virNetworkPortDefPtr def = NULL;
+
+    if (STRNEQ((const char *)root->name, "networkport")) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       "%s",
+                       _("unknown root element for network port"));
+        goto cleanup;
+    }
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    ctxt->node = root;
+    def = virNetworkPortDefParseXML(ctxt);
+
+ cleanup:
+    xmlXPathFreeContext(ctxt);
+    return def;
+}
+
+
+static virNetworkPortDefPtr
+virNetworkPortDefParse(const char *xmlStr,
+                       const char *filename)
+{
+    virNetworkPortDefPtr def = NULL;
+    xmlDocPtr xml;
+
+    if ((xml = virXMLParse(filename, xmlStr, _("(networkport_definition)")))) {
+        def = virNetworkPortDefParseNode(xml, xmlDocGetRootElement(xml));
+        xmlFreeDoc(xml);
+    }
+
+    return def;
+}
+
+
+virNetworkPortDefPtr
+virNetworkPortDefParseString(const char *xmlStr)
+{
+    return virNetworkPortDefParse(xmlStr, NULL);
+}
+
+
+virNetworkPortDefPtr
+virNetworkPortDefParseFile(const char *filename)
+{
+    return virNetworkPortDefParse(NULL, filename);
+}
+
+
+char *
+virNetworkPortDefFormat(const virNetworkPortDef *def)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+
+    if (virNetworkPortDefFormatBuf(&buf, def) < 0) {
+        virBufferFreeAndReset(&buf);
+        return NULL;
+    }
+
+    if (virBufferCheckError(&buf) < 0)
+        return NULL;
+
+    return virBufferContentAndReset(&buf);
+}
+
+
+int
+virNetworkPortDefFormatBuf(virBufferPtr buf,
+                           const virNetworkPortDef *def)
+{
+    char uuid[VIR_UUID_STRING_BUFLEN];
+    char macaddr[VIR_MAC_STRING_BUFLEN];
+
+    virBufferAddLit(buf, "<networkport>\n");
+
+    virBufferAdjustIndent(buf, 2);
+
+    virUUIDFormat(def->uuid, uuid);
+    virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid);
+
+    virBufferAddLit(buf, "<owner>\n");
+    virBufferAdjustIndent(buf, 2);
+    virBufferEscapeString(buf, "<name>%s</name>\n", def->ownername);
+    virUUIDFormat(def->owneruuid, uuid);
+    virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuid);
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</owner>\n");
+
+    if (def->group)
+        virBufferEscapeString(buf, "<group>%s</group>\n", def->group);
+
+    virMacAddrFormat(&def->mac, macaddr);
+    virBufferAsprintf(buf, "<mac address='%s'/>\n", macaddr);
+
+    if (virNetDevVPortProfileFormat(def->virtPortProfile, buf) < 0)
+        return -1;
+    if (def->bandwidth)
+        virNetDevBandwidthFormat(def->bandwidth, def->class_id, buf);
+    if (virNetDevVlanFormat(&def->vlan, buf) < 0)
+        return -1;
+    if (def->trustGuestRxFilters)
+        virBufferAsprintf(buf, "<rxfilters trustGuest='%s'/>\n",
+                          virTristateBoolTypeToString(def->trustGuestRxFilters));
+
+    if (def->plugtype != VIR_NETWORK_PORT_PLUG_TYPE_NONE) {
+        virBufferAsprintf(buf, "<plug type='%s'",
+                          virNetworkPortPlugTypeToString(def->plugtype));
+
+        switch (def->plugtype) {
+        case VIR_NETWORK_PORT_PLUG_TYPE_NONE:
+            break;
+
+        case VIR_NETWORK_PORT_PLUG_TYPE_NETWORK:
+        case VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE:
+            virBufferEscapeString(buf, " bridge='%s'", def->plug.bridge.brname);
+            if (def->plug.bridge.macTableManager)
+                virBufferAsprintf(buf, " macTableManager='%s'",
+                                  virNetworkBridgeMACTableManagerTypeToString(
+                                      def->plug.bridge.macTableManager));
+            virBufferAddLit(buf, "/>\n");
+            break;
+
+        case VIR_NETWORK_PORT_PLUG_TYPE_DIRECT:
+            virBufferEscapeString(buf, " dev='%s'", def->plug.direct.linkdev);
+            virBufferAsprintf(buf, " mode='%s'",
+                              virNetDevMacVLanModeTypeToString(
+                                  def->plug.direct.mode));
+            virBufferAddLit(buf, "/>\n");
+            break;
+
+        case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
+            virBufferAsprintf(buf, " managed='%s'>\n",
+                              def->plug.hostdevpci.managed ? "yes" : "no");
+            virBufferAdjustIndent(buf, 2);
+            if (def->plug.hostdevpci.driver)
+                virBufferEscapeString(buf, "<driver name='%s'/>\n",
+                                      virNetworkForwardDriverNameTypeToString(
+                                          def->plug.hostdevpci.driver));
+
+            virPCIDeviceAddressFormat(buf, def->plug.hostdevpci.addr, false);
+            virBufferAdjustIndent(buf, -2);
+            virBufferAddLit(buf, "</plug>\n");
+            break;
+
+        case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
+        default:
+            virReportEnumRangeError(virNetworkPortPlugType, def->plugtype);
+            return -1;
+        }
+    }
+
+
+    virBufferAdjustIndent(buf, -2);
+    virBufferAddLit(buf, "</networkport>\n");
+
+    return 0;
+}
+
+
+static char *
+virNetworkPortDefConfigFile(const char *dir,
+                            const char *name)
+{
+    char *ret = NULL;
+
+    ignore_value(virAsprintf(&ret, "%s/%s.xml", dir, name));
+    return ret;
+}
+
+
+int
+virNetworkPortDefSaveStatus(virNetworkPortDef *def,
+                            const char *dir)
+{
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    char *path;
+    char *xml = NULL;
+    int ret = -1;
+
+    virUUIDFormat(def->uuid, uuidstr);
+
+    if (virFileMakePath(dir) < 0)
+        goto cleanup;
+
+    if (!(path = virNetworkPortDefConfigFile(dir, uuidstr)))
+        goto cleanup;
+
+    if (!(xml = virNetworkPortDefFormat(def)))
+        goto cleanup;
+
+    if (virXMLSaveFile(path, uuidstr, "net-port-create", xml) < 0)
+        goto cleanup;
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(xml);
+    VIR_FREE(path);
+    return ret;
+}
+
+
+int
+virNetworkPortDefDeleteStatus(virNetworkPortDef *def,
+                              const char *dir)
+{
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+    char *path;
+    int ret = -1;
+
+    virUUIDFormat(def->uuid, uuidstr);
+
+    if (!(path = virNetworkPortDefConfigFile(dir, uuidstr)))
+        goto cleanup;
+
+    if (unlink(path) < 0 && errno != ENOENT) {
+        virReportSystemError(errno,
+                             _("Unable to delete %s"), path);
+        goto cleanup;
+    }
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(path);
+    return ret;
+}
diff --git a/src/conf/virnetworkportdef.h b/src/conf/virnetworkportdef.h
new file mode 100644
index 0000000000..94b2fabf6b
--- /dev/null
+++ b/src/conf/virnetworkportdef.h
@@ -0,0 +1,113 @@
+/*
+ * virnetworkportdef.h: network port XML processing
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIBVIRT_VIRNETWORKPORTDEF_H
+# define LIBVIRT_VIRNETWORKPORTDEF_H
+
+# include "internal.h"
+# include "viruuid.h"
+# include "virnetdevvlan.h"
+# include "virnetdevvportprofile.h"
+# include "virnetdevbandwidth.h"
+# include "virpci.h"
+# include "virxml.h"
+# include "netdev_vport_profile_conf.h"
+# include "netdev_bandwidth_conf.h"
+# include "netdev_vlan_conf.h"
+
+typedef struct _virNetworkPortDef virNetworkPortDef;
+typedef virNetworkPortDef *virNetworkPortDefPtr;
+
+typedef enum {
+    VIR_NETWORK_PORT_PLUG_TYPE_NONE,
+    VIR_NETWORK_PORT_PLUG_TYPE_NETWORK,
+    VIR_NETWORK_PORT_PLUG_TYPE_BRIDGE,
+    VIR_NETWORK_PORT_PLUG_TYPE_DIRECT,
+    VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI,
+
+    VIR_NETWORK_PORT_PLUG_TYPE_LAST,
+} virNetworkPortPlugType;
+
+VIR_ENUM_DECL(virNetworkPortPlug);
+
+struct _virNetworkPortDef {
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    char *ownername;
+    unsigned char owneruuid[VIR_UUID_BUFLEN];
+
+    char *group;
+    virMacAddr mac;
+
+    virNetDevVPortProfilePtr virtPortProfile;
+    virNetDevBandwidthPtr bandwidth;
+    unsigned int class_id; /* class ID for bandwidth 'floor' */
+    virNetDevVlan vlan;
+    int trustGuestRxFilters; /* enum virTristateBool */
+
+    int plugtype; /* virNetworkPortPlugType */
+    union {
+        struct {
+            char *brname;
+            int macTableManager; /* enum virNetworkBridgeMACTableManagerType */
+        } bridge; /* For TYPE_NETWORK & TYPE_BRIDGE */
+        struct {
+            char *linkdev;
+            int mode; /* enum virNetDevMacVLanMode from util/virnetdevmacvlan.h */
+        } direct;
+        struct {
+            virPCIDeviceAddress addr; /* PCI Address of device */
+            int driver; /* virNetworkForwardDriverNameType */
+            int managed;
+        } hostdevpci;
+    } plug;
+};
+
+
+void
+virNetworkPortDefFree(virNetworkPortDefPtr port);
+
+virNetworkPortDefPtr
+virNetworkPortDefParseNode(xmlDocPtr xml,
+                           xmlNodePtr root);
+
+virNetworkPortDefPtr
+virNetworkPortDefParseString(const char *xml);
+
+virNetworkPortDefPtr
+virNetworkPortDefParseFile(const char *filename);
+
+char *
+virNetworkPortDefFormat(const virNetworkPortDef *def);
+
+int
+virNetworkPortDefFormatBuf(virBufferPtr buf,
+                           const virNetworkPortDef *def);
+
+int
+virNetworkPortDefSaveStatus(virNetworkPortDef *def,
+                            const char *dir);
+
+int
+virNetworkPortDefDeleteStatus(virNetworkPortDef *def,
+                              const char *dir);
+
+
+#endif /* LIBVIRT_VIRNETWORKPORTDEF_H */
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 909975750c..62a07c094e 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1075,6 +1075,16 @@ virNetworkObjUpdate;
 virNetworkObjUpdateAssignDef;
 
 
+# conf/virnetworkportdef.h
+virNetworkPortDefFormat;
+virNetworkPortDefFormatBuf;
+virNetworkPortDefFree;
+virNetworkPortDefParseFile;
+virNetworkPortDefParseNode;
+virNetworkPortDefParseString;
+virNetworkPortDefSaveStatus;
+
+
 # conf/virnodedeviceobj.h
 virNodeDeviceObjEndAPI;
 virNodeDeviceObjGetDef;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 46d94d2236..6865ee946e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -149,6 +149,7 @@ EXTRA_DIST = \
 	virmockstathelpers.c \
 	virnetdaemondata \
 	virnetdevtestdata \
+	virnetworkportxml2xmldata \
 	virnwfilterbindingxml2xmldata \
 	virpcitestdata \
 	virscsidata \
@@ -335,6 +336,7 @@ endif WITH_YAJL
 test_programs += \
 		networkxml2xmltest \
 		networkxml2xmlupdatetest \
+		virnetworkportxml2xmltest \
 		$(NULL)
 
 if WITH_NETWORK
@@ -832,6 +834,11 @@ networkxml2xmlupdatetest_SOURCES = \
 	testutils.c testutils.h
 networkxml2xmlupdatetest_LDADD = $(LDADDS)
 
+virnetworkportxml2xmltest_SOURCES = \
+	virnetworkportxml2xmltest.c \
+	testutils.c testutils.h
+virnetworkportxml2xmltest_LDADD = $(LDADDS)
+
 if WITH_NETWORK
 networkxml2conftest_SOURCES = \
 	networkxml2conftest.c \
diff --git a/tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml b/tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml
new file mode 100644
index 0000000000..8036bc2e1c
--- /dev/null
+++ b/tests/virnetworkportxml2xmldata/plug-bridge-mactbl.xml
@@ -0,0 +1,9 @@
+<networkport>
+  <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid>
+  <owner>
+    <name>memtest</name>
+    <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+  </owner>
+  <mac address='52:54:00:7b:35:93'/>
+  <plug type='bridge' bridge='virbr0' macTableManager='libvirt'/>
+</networkport>
diff --git a/tests/virnetworkportxml2xmldata/plug-bridge.xml b/tests/virnetworkportxml2xmldata/plug-bridge.xml
new file mode 100644
index 0000000000..e09fc45a9d
--- /dev/null
+++ b/tests/virnetworkportxml2xmldata/plug-bridge.xml
@@ -0,0 +1,15 @@
+<networkport>
+  <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid>
+  <owner>
+    <name>memtest</name>
+    <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+  </owner>
+  <group>web1</group>
+  <mac address='52:54:00:7b:35:93'/>
+  <bandwidth classID='1729'>
+    <inbound average='1000' peak='4000' floor='2000' burst='1024'/>
+    <outbound average='128' peak='256' burst='32768'/>
+  </bandwidth>
+  <rxfilters trustGuest='yes'/>
+  <plug type='bridge' bridge='virbr0'/>
+</networkport>
diff --git a/tests/virnetworkportxml2xmldata/plug-direct.xml b/tests/virnetworkportxml2xmldata/plug-direct.xml
new file mode 100644
index 0000000000..81554b4579
--- /dev/null
+++ b/tests/virnetworkportxml2xmldata/plug-direct.xml
@@ -0,0 +1,12 @@
+<networkport>
+  <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid>
+  <owner>
+    <name>memtest</name>
+    <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+  </owner>
+  <mac address='52:54:00:7b:35:93'/>
+  <virtualport type='802.1Qbg'>
+    <parameters managerid='11' typeid='1193047' typeidversion='2'/>
+  </virtualport>
+  <plug type='direct' dev='ens3' mode='vepa'/>
+</networkport>
diff --git a/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml b/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml
new file mode 100644
index 0000000000..cc4419f3fd
--- /dev/null
+++ b/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml
@@ -0,0 +1,12 @@
+<networkport>
+  <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid>
+  <owner>
+    <name>memtest</name>
+    <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+  </owner>
+  <mac address='52:54:00:7b:35:93'/>
+  <plug type='hostdev-pci' managed='yes'>
+    <driver name='vfio'/>
+    <address domain='0x0001' bus='0x02' slot='0x03' function='0x4'/>
+  </plug>
+</networkport>
diff --git a/tests/virnetworkportxml2xmldata/plug-network.xml b/tests/virnetworkportxml2xmldata/plug-network.xml
new file mode 100644
index 0000000000..7b08ca295a
--- /dev/null
+++ b/tests/virnetworkportxml2xmldata/plug-network.xml
@@ -0,0 +1,16 @@
+<networkport>
+  <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid>
+  <owner>
+    <name>memtest</name>
+    <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+  </owner>
+  <group>web1</group>
+  <mac address='52:54:00:7b:35:93'/>
+  <bandwidth>
+    <inbound average='1000' peak='4000' floor='2000' burst='1024'/>
+    <outbound average='128' peak='256' burst='32768'/>
+  </bandwidth>
+  <class id='1729'/>
+  <rxfilters trustGuest='yes'/>
+  <plug type='network' bridge='virbr0'/>
+</networkport>
diff --git a/tests/virnetworkportxml2xmldata/plug-none.xml b/tests/virnetworkportxml2xmldata/plug-none.xml
new file mode 100644
index 0000000000..ed7199ec8c
--- /dev/null
+++ b/tests/virnetworkportxml2xmldata/plug-none.xml
@@ -0,0 +1,8 @@
+<networkport>
+  <uuid>5d744f21-ba4a-4d6e-bdb2-30a35ff3207d</uuid>
+  <owner>
+    <name>memtest</name>
+    <uuid>d54df46f-1ab5-4a22-8618-4560ef5fac2c</uuid>
+  </owner>
+  <mac address='52:54:00:7b:35:93'/>
+</networkport>
diff --git a/tests/virnetworkportxml2xmltest.c b/tests/virnetworkportxml2xmltest.c
new file mode 100644
index 0000000000..bb0ae8a8d5
--- /dev/null
+++ b/tests/virnetworkportxml2xmltest.c
@@ -0,0 +1,104 @@
+/*
+ * virnetworkportxml2xmltest.c: network port XML processing test suite
+ *
+ * Copyright (C) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library.  If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "internal.h"
+#include "testutils.h"
+#include "virnetworkportdef.h"
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+
+static int
+testCompareXMLToXMLFiles(const char *expected)
+{
+    char *actual = NULL;
+    int ret = -1;
+    virNetworkPortDefPtr dev = NULL;
+
+    if (!(dev = virNetworkPortDefParseFile(expected)))
+        goto cleanup;
+
+    if (!(actual = virNetworkPortDefFormat(dev)))
+        goto cleanup;
+
+    if (virTestCompareToFile(actual, expected) < 0)
+        goto cleanup;
+
+    ret = 0;
+ cleanup:
+    VIR_FREE(actual);
+    virNetworkPortDefFree(dev);
+    return ret;
+}
+
+struct testInfo {
+    const char *name;
+};
+
+static int
+testCompareXMLToXMLHelper(const void *data)
+{
+    const struct testInfo *info = data;
+    int ret = -1;
+    char *xml = NULL;
+
+    if (virAsprintf(&xml, "%s/virnetworkportxml2xmldata/%s.xml",
+                    abs_srcdir, info->name) < 0)
+        goto cleanup;
+
+    ret = testCompareXMLToXMLFiles(xml);
+
+ cleanup:
+    VIR_FREE(xml);
+
+    return ret;
+}
+
+static int
+mymain(void)
+{
+    int ret = 0;
+
+#define DO_TEST(name) \
+    do { \
+        const struct testInfo info = {name}; \
+        if (virTestRun("virnetworkportdeftest " name, \
+                       testCompareXMLToXMLHelper, &info) < 0) \
+            ret = -1; \
+    } while (0)
+
+    DO_TEST("plug-none");
+    DO_TEST("plug-bridge");
+    DO_TEST("plug-bridge-mactbl");
+    DO_TEST("plug-direct");
+    DO_TEST("plug-hostdev-pci");
+
+    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+VIR_TEST_MAIN(mymain)
diff --git a/tests/virschematest.c b/tests/virschematest.c
index 56bdcb2f88..13c30acc30 100644
--- a/tests/virschematest.c
+++ b/tests/virschematest.c
@@ -227,6 +227,7 @@ mymain(void)
     DO_TEST_DIR("interface.rng", "interfaceschemadata");
     DO_TEST_DIR("network.rng", "../src/network", "networkxml2xmlin",
                 "networkxml2xmlout", "networkxml2confdata");
+    DO_TEST_DIR("networkport.rng", "virnetworkportxml2xmldata");
     DO_TEST_DIR("nodedev.rng", "nodedevschemadata");
     DO_TEST_DIR("nwfilter.rng", "nwfilterxml2xmlout", "../examples/xml/nwfilter");
     DO_TEST_DIR("nwfilterbinding.rng", "virnwfilterbindingxml2xmldata");
-- 
2.21.0




More information about the libvir-list mailing list