On 3/19/19 8:46 AM, Daniel P. Berrangé wrote:
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>
---
src/conf/Makefile.inc.am | 2 +
src/conf/virnetworkportdef.c | 514 ++++++++++++++++++
src/conf/virnetworkportdef.h | 112 ++++
src/libvirt_private.syms | 10 +
tests/Makefile.am | 7 +
.../plug-bridge-mactbl.xml | 9 +
.../virnetworkportxml2xmldata/plug-bridge.xml | 12 +
.../virnetworkportxml2xmldata/plug-direct.xml | 12 +
.../plug-hostdev-pci.xml | 12 +
tests/virnetworkportxml2xmldata/plug-none.xml | 8 +
tests/virnetworkportxml2xmltest.c | 104 ++++
11 files changed, 802 insertions(+)
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-none.xml
create mode 100644 tests/virnetworkportxml2xmltest.c
diff --git a/src/conf/Makefile.inc.am b/src/conf/Makefile.inc.am
index 9b4d80485b..388917c5dd 100644
--- a/src/conf/Makefile.inc.am
+++ b/src/conf/Makefile.inc.am
@@ -5,6 +5,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..7023d9607e
--- /dev/null
+++ b/src/conf/virnetworkportdef.c
@@ -0,0 +1,514 @@
+/*
+ * 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", "bridge", "direct",
"hostdev-pci");
Are we going to need to differentiate between veth and macvtap later?
Having the choice implied based on which hypervisor it's coming from
seems potentially troublesome...
+
+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_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;
Maybe you could make all of those VIR_AUTOFREE() to further the world
conquest of that stuff. (I still haven't decided if I'm really a fan of
it, but it does seem to be taking over the source).
There's one or two comments below this, but only for comic relief,
nothing of substance.
Reviewed-by: Laine Stump <laine(a)laine.org>
(I'm assuming the XML documentation is in a followup patch somewhere?)
+
+ 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;
+ }
+
+ class_id = virXPathString("string(./class/@id)", ctxt);
Good, so the class_id issue we've talked about in other patches no
longer exists once we're using NetworkPortDef instead of ActualNetDef.
+ if (class_id &&
+ virStrToLong_ui(class_id, NULL, 10, &def->class_id) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("Unable to parse class id '%s'"),
+ class_id);
+ 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, 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_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;
+ virNetDevBandwidthFormat(def->bandwidth, buf);
+ if (def->class_id)
+ virBufferAsprintf(buf, "<class id='%u'/>\n",
def->class_id);
+ 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_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..3897013a86
--- /dev/null
+++ b/src/conf/virnetworkportdef.h
@@ -0,0 +1,112 @@
+/*
+ * 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_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;
+ struct {
+ char *linkdev;
+ int mode; /* enum virMacvtapMode from util/macvtap.h */
+ } direct;
+ struct {
+ virPCIDeviceAddress addr; /* PCI Address of device */
+ int driver; /* virNetworkForwardDriverNameType */
(Aside: someday we should get rid of all the stuff that's there only to
allow legacy qemu PCI device assignment, since we no longer support any
distros that actually have it built into the kernel. I'm pretty sure
that's the only reason for the "driver" attribute...)
+ 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 6f5a734fdb..0694db7089 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1061,6 +1061,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 29f1fe2d2a..e39e3658a3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -148,6 +148,7 @@ EXTRA_DIST = \
virmock.h \
virnetdaemondata \
virnetdevtestdata \
+ virnetworkportxml2xmldata \
virnwfilterbindingxml2xmldata \
virpcitestdata \
virscsidata \
@@ -334,6 +335,7 @@ endif WITH_YAJL
test_programs += \
networkxml2xmltest \
networkxml2xmlupdatetest \
+ virnetworkportxml2xmltest \
$(NULL)
if WITH_NETWORK
@@ -830,6 +832,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..6dd576e8a1
--- /dev/null
+++ b/tests/virnetworkportxml2xmldata/plug-bridge.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>
+ <group>web1</group>
+ <mac address='52:54:00:7b:35:93'/>
+ <class id='1729'/>
+ <rxfilters trustGuest='yes'/>
Where were you back in 1897 (or whenever - it was a long time ago) when
I posted a patch with "trustGuestRxFilters" as a strawman hoping for
counter proposals, and nobody offered an alternative? :-P
+ <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-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)