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 219ff350d7..eec861591f 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");
+
+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;
+
+ 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);
+ 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 */
+ 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 07bc7d94d2..c63e204c61 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1036,6 +1036,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 c3f633cee0..cccd85e125 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -156,6 +156,7 @@ EXTRA_DIST = \
virmock.h \
virnetdaemondata \
virnetdevtestdata \
+ viretworkportxml2xmldata \
virnwfilterbindingxml2xmldata \
virpcitestdata \
virscsidata \
@@ -342,6 +343,7 @@ endif WITH_YAJL
test_programs += \
networkxml2xmltest \
networkxml2xmlupdatetest \
+ virnetworkportxml2xmltest \
$(NULL)
if WITH_NETWORK
@@ -836,6 +838,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'/>
+ <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)
--
2.20.1