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