[libvirt] PATCH: Generic internal API for network XML parser/formatter

Daniel P. Berrange berrange at redhat.com
Tue Jun 24 15:17:18 UTC 2008


We currently have two drivers which handle the networking XML containing
duplicated parsers and formatters for the XML, and very similar structs.
This patch introduces a new general purpose internal API for parsing and
formatting network XML, and representing it as a series of structs.

This code is derived from the current equivalent code in the QEMU driver
for networks.

The naming conventions I'm adopting in this patch follow those  in the
storage driver:

 - virNetworkPtr - the public opaque object in libvirt.h
 - virNetworkObjPtr - the primary internal object for network state
 - virNetworkDefPtr - the configuration data for a network

A virNetworkObjPtr contains a reference to one or two virNetworkDefPtr
objects - the current live config, and potentially a secondary inactive
config which will become live at the next restart.

The structs are defined in network_conf.h, along with a bunch of APIs for
dealing with them. These APIs are the same as similarly named ones from
the current qemu driver, but I'll go over them again for a reminder:


  virNetworkObjPtr virNetworkFindByUUID(const virNetworkObjPtr nets,
                                        const unsigned char *uuid);
  virNetworkObjPtr virNetworkFindByName(const virNetworkObjPtr nets,
                                        const char *name);

Allow lookup of a virNetworkObjPtr object based on its name or UUID, as
typically obtained from the public virNetworkPtr object.
The 'nets' parameter to both of thse is a linked list of networks which
are currently known to the driver using this API.

  void virNetworkDefFree(virNetworkDefPtr def);
  void virNetworkObjFree(virNetworkObjPtr net);

Convenience APIs to totally free the memory associated with these
objects.

  virNetworkObjPtr virNetworkAssignDef(virConnectPtr conn,
                                       virNetworkObjPtr *nets,
                                       const virNetworkDefPtr def);

Given a virNetworkDefPtr object, it'll search for a pre-existing
virNetworkObjPtr object with matching config. If one is found, its
config will be updated, otherwise a new object will be allocated.

  void virNetworkRemoveInactive(virNetworkObjPtr *nets,
                                const virNetworkObjPtr net);

Convenience for removing and free'ing a virNetworkObjPtr object in
the current list of active networks.

  virNetworkDefPtr virNetworkDefParse(virConnectPtr conn,
                                      const char *xmlStr,
                                      const char *displayName);

Given an XML document describing a network, parses the doc and generates
a virNetworkDefPtr to represent it in memory.

  char *virNetworkDefFormat(virConnectPtr conn,
                            const virNetworkDefPtr def);

Given a virNetworkDefPtr object, generate a XML document describing the
network.

As a mentioned earlier, the impl of these APIs is just copied from the QEMU
driver, but instead of using pre-declared char[PATH_MAX] fields, we allocate
memory for strings as required.

Regards,
Daniel

diff -r a6e5acdd23df src/Makefile.am
--- a/src/Makefile.am	Tue Jun 24 15:07:21 2008 +0100
+++ b/src/Makefile.am	Tue Jun 24 15:07:23 2008 +0100
@@ -52,6 +52,7 @@
 		driver.h					\
 		proxy_internal.c proxy_internal.h		\
 		conf.c conf.h                                   \
+                network_conf.c network_conf.h                   \
 		xm_internal.c xm_internal.h                     \
 		remote_internal.c remote_internal.h		\
 		bridge.c bridge.h				\
diff -r a6e5acdd23df src/network_conf.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/network_conf.c	Tue Jun 24 15:07:23 2008 +0100
@@ -0,0 +1,467 @@
+/*
+ * network_conf.h: network XML handling
+ *
+ * Copyright (C) 2006-2008 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+
+
+#include <config.h>
+
+#include <arpa/inet.h>
+
+#include "internal.h"
+
+#include "network_conf.h"
+#include "memory.h"
+#include "xml.h"
+#include "uuid.h"
+#include "util.h"
+#include "buf.h"
+
+VIR_ENUM_DECL(virNetworkForward)
+
+VIR_ENUM_IMPL(virNetworkForward,
+              VIR_NETWORK_FORWARD_LAST,
+              "none", "nat", "route" )
+
+static void virNetworkReportError(virConnectPtr conn,
+                                  int code, const char *fmt, ...)
+{
+    va_list args;
+    char errorMessage[1024];
+    const char *virerr;
+
+    if (fmt) {
+        va_start(args, fmt);
+        vsnprintf(errorMessage, sizeof(errorMessage)-1, fmt, args);
+        va_end(args);
+    } else {
+        errorMessage[0] = '\0';
+    }
+
+    virerr = __virErrorMsg(code, (errorMessage[0] ? errorMessage : NULL));
+    __virRaiseError(conn, NULL, NULL, VIR_FROM_QEMU, code, VIR_ERR_ERROR,
+                    virerr, errorMessage, NULL, -1, -1, virerr, errorMessage);
+}
+
+
+virNetworkObjPtr virNetworkFindByUUID(const virNetworkObjPtr nets,
+                                      const unsigned char *uuid)
+{
+    virNetworkObjPtr net = nets;
+    while (net) {
+        if (!memcmp(net->def->uuid, uuid, VIR_UUID_BUFLEN))
+            return net;
+        net = net->next;
+    }
+
+    return NULL;
+}
+
+virNetworkObjPtr virNetworkFindByName(const virNetworkObjPtr nets,
+                                      const char *name)
+{
+    virNetworkObjPtr net = nets;
+    while (net) {
+        if (STREQ(net->def->name, name))
+            return net;
+        net = net->next;
+    }
+
+    return NULL;
+}
+
+
+void virNetworkDefFree(virNetworkDefPtr def)
+{
+    int i;
+
+    if (!def)
+        return;
+
+    VIR_FREE(def->name);
+    VIR_FREE(def->bridge);
+    VIR_FREE(def->forwardDev);
+    VIR_FREE(def->ipAddress);
+    VIR_FREE(def->network);
+    VIR_FREE(def->netmask);
+
+    for (i = 0 ; i < def->nranges && def->ranges ; i++) {
+        VIR_FREE(def->ranges[i].start);
+        VIR_FREE(def->ranges[i].end);
+    }
+    VIR_FREE(def->ranges);
+
+    VIR_FREE(def);
+}
+
+void virNetworkObjFree(virNetworkObjPtr net)
+{
+    if (!net)
+        return;
+
+    virNetworkDefFree(net->def);
+    virNetworkDefFree(net->newDef);
+
+    VIR_FREE(net->configFile);
+    VIR_FREE(net->autostartLink);
+
+    VIR_FREE(net);
+}
+
+virNetworkObjPtr virNetworkAssignDef(virConnectPtr conn,
+                                     virNetworkObjPtr *nets,
+                                     const virNetworkDefPtr def)
+{
+    virNetworkObjPtr network;
+
+    if ((network = virNetworkFindByName(*nets, def->name))) {
+        if (!virNetworkIsActive(network)) {
+            virNetworkDefFree(network->def);
+            network->def = def;
+        } else {
+            if (network->newDef)
+                virNetworkDefFree(network->newDef);
+            network->newDef = def;
+        }
+
+        return network;
+    }
+
+    if (VIR_ALLOC(network) < 0) {
+        virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    network->def = def;
+    network->next = *nets;
+
+    *nets = network;
+
+    return network;
+
+}
+
+void virNetworkRemoveInactive(virNetworkObjPtr *nets,
+                              const virNetworkObjPtr net)
+{
+    virNetworkObjPtr prev = NULL;
+    virNetworkObjPtr curr = *nets;
+
+    while (curr &&
+           curr != net) {
+        prev = curr;
+        curr = curr->next;
+    }
+
+    if (curr) {
+        if (prev)
+            prev->next = curr->next;
+        else
+            *nets = curr->next;
+    }
+
+    virNetworkObjFree(net);
+}
+
+
+static int
+virNetworkDHCPRangeDefParseXML(virConnectPtr conn,
+                               virNetworkDefPtr def,
+                               xmlNodePtr node) {
+
+    xmlNodePtr cur;
+
+    cur = node->children;
+    while (cur != NULL) {
+        xmlChar *start, *end;
+
+        if (cur->type != XML_ELEMENT_NODE ||
+            !xmlStrEqual(cur->name, BAD_CAST "range")) {
+            cur = cur->next;
+            continue;
+        }
+
+        if (!(start = xmlGetProp(cur, BAD_CAST "start"))) {
+            cur = cur->next;
+            continue;
+        }
+        if (!(end = xmlGetProp(cur, BAD_CAST "end"))) {
+            cur = cur->next;
+            xmlFree(start);
+            continue;
+        }
+
+        if (VIR_REALLOC_N(def->ranges, def->nranges + 1) < 0) {
+            xmlFree(start);
+            xmlFree(end);
+            virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+            return -1;
+        }
+        def->ranges[def->nranges].start = (char *)start;
+        def->ranges[def->nranges].end = (char *)end;
+        def->nranges++;
+
+        cur = cur->next;
+    }
+
+    return 0;
+}
+
+static virNetworkDefPtr
+virNetworkDefParseXML(virConnectPtr conn,
+                      xmlDocPtr xml)
+{
+    xmlNodePtr root = NULL;
+    xmlXPathContextPtr ctxt = NULL;
+    virNetworkDefPtr def;
+    char *tmp;
+
+    if (VIR_ALLOC(def) < 0) {
+        virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+        return NULL;
+    }
+
+    /* Prepare parser / xpath context */
+    root = xmlDocGetRootElement(xml);
+    if ((root == NULL) || (!xmlStrEqual(root->name, BAD_CAST "network"))) {
+        virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s", _("incorrect root element"));
+        goto error;
+    }
+
+    ctxt = xmlXPathNewContext(xml);
+    if (ctxt == NULL) {
+        virNetworkReportError(conn, VIR_ERR_NO_MEMORY,
+                              "%s", _("failed to allocate space for xmlXPathContext string"));
+        goto error;
+    }
+
+
+    /* Extract network name */
+    def->name = virXPathString("string(/network/name[1])", ctxt);
+    if (!def->name) {
+        virNetworkReportError(conn, VIR_ERR_NO_NAME, NULL);
+        goto error;
+    }
+
+    /* Extract network uuid */
+    tmp = virXPathString("string(/network/uuid[1])", ctxt);
+    if (!tmp) {
+        int err;
+        if ((err = virUUIDGenerate(def->uuid))) {
+            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             _("Failed to generate UUID: %s"), strerror(err));
+            goto error;
+        }
+    } else {
+        if (virUUIDParse(tmp, def->uuid) < 0) {
+            VIR_FREE(tmp);
+            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  "%s", _("malformed uuid element"));
+            goto error;
+        }
+        VIR_FREE(tmp);
+    }
+
+    /* Parse bridge information */
+    def->bridge = virXPathString("string(/network/bridge[1]/@name)", ctxt);
+    tmp = virXPathString("string(/network/bridge[1]/@stp)", ctxt);
+    if (tmp && STREQ(tmp, "off"))
+        def->stp = 0;
+    else
+        def->stp = 1;
+    VIR_FREE(tmp);
+
+    if (virXPathLong("string(/network/bridge[1]/@delay)", ctxt, &def->delay) < 0)
+        def->delay = 0;
+
+    def->ipAddress = virXPathString("string(/network/ip[1]/@address)", ctxt);
+    def->netmask = virXPathString("string(/network/ip[1]/@netmask)", ctxt);
+    if (def->ipAddress &&
+        def->netmask) {
+        /* XXX someday we want IPv6 too, so inet_aton won't work there */
+        struct in_addr inaddress, innetmask;
+        char *netaddr;
+        int netlen;
+        xmlNodePtr dhcp;
+
+        if (!inet_aton(def->ipAddress, &inaddress)) {
+            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("cannot parse IP address '%s'"),
+                                  def->ipAddress);
+            goto error;
+        }
+        if (!inet_aton(def->netmask, &innetmask)) {
+            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  _("cannot parse netmask '%s'"),
+                                  def->netmask);
+            goto error;
+        }
+
+        inaddress.s_addr &= innetmask.s_addr;
+        netaddr = inet_ntoa(inaddress);
+
+        netlen = strlen(netaddr) + 1 + strlen(def->netmask) + 1;
+        if (VIR_ALLOC_N(def->network, netlen) < 0) {
+            virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+            goto error;
+        }
+        strcpy(def->network, netaddr);
+        strcat(def->network, "/");
+        strcat(def->network, def->netmask);
+
+
+        if ((dhcp = virXPathNode("/network/ip[1]/dhcp[1]", ctxt)) &&
+            virNetworkDHCPRangeDefParseXML(conn, def, dhcp) < 0)
+            goto error;
+    }
+
+
+    /* IPv4 forwarding setup */
+    if (virXPathBoolean("count(/network/forward) > 0", ctxt)) {
+        if (!def->ipAddress ||
+            !def->netmask) {
+            virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                  "%s", _("Forwarding requested, but no IPv4 address/netmask provided"));
+            goto error;
+        }
+
+        tmp = virXPathString("string(/network/forward[1]/@mode)", ctxt);
+        if (tmp) {
+            if ((def->forwardType = virNetworkForwardTypeFromString(tmp)) < 0) {
+                virNetworkReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                                      _("unknown forwarding type '%s'"), tmp);
+                VIR_FREE(tmp);
+            }
+            VIR_FREE(tmp);
+        } else {
+            def->forwardType = VIR_NETWORK_FORWARD_NAT;
+        }
+
+
+        def->forwardDev = virXPathString("string(/network/forward[1]/@dev)", ctxt);
+    } else {
+        def->forwardType = VIR_NETWORK_FORWARD_NONE;
+    }
+
+    xmlXPathFreeContext(ctxt);
+
+    return def;
+
+ error:
+    xmlXPathFreeContext(ctxt);
+    virNetworkDefFree(def);
+    return NULL;
+}
+
+virNetworkDefPtr virNetworkDefParse(virConnectPtr conn,
+                                    const char *xmlStr,
+                                    const char *displayName)
+{
+    xmlDocPtr xml;
+    virNetworkDefPtr def;
+
+    if (!(xml = xmlReadDoc(BAD_CAST xmlStr, displayName ? displayName : "network.xml", NULL,
+                           XML_PARSE_NOENT | XML_PARSE_NONET |
+                           XML_PARSE_NOERROR | XML_PARSE_NOWARNING))) {
+        virNetworkReportError(conn, VIR_ERR_XML_ERROR, NULL);
+        return NULL;
+    }
+
+    def = virNetworkDefParseXML(conn, xml);
+
+    xmlFreeDoc(xml);
+
+    return def;
+}
+
+char *virNetworkDefFormat(virConnectPtr conn,
+                          const virNetworkDefPtr def)
+{
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    unsigned char *uuid;
+    char *tmp;
+    char uuidstr[VIR_UUID_STRING_BUFLEN];
+
+    virBufferAddLit(&buf, "<network>\n");
+    virBufferVSprintf(&buf, "  <name>%s</name>\n", def->name);
+
+    uuid = def->uuid;
+    virUUIDFormat(uuid, uuidstr);
+    virBufferVSprintf(&buf, "  <uuid>%s</uuid>\n", uuidstr);
+
+    if (def->forwardType != VIR_NETWORK_FORWARD_NONE) {
+        const char *mode = virNetworkForwardTypeToString(def->forwardType);
+        if (mode) {
+            if (def->forwardDev) {
+                virBufferVSprintf(&buf, "  <forward dev='%s' mode='%s'/>\n",
+                                  def->forwardDev, mode);
+            } else {
+                virBufferVSprintf(&buf, "  <forward mode='%s'/>\n", mode);
+            }
+        }
+    }
+
+    virBufferAddLit(&buf, "  <bridge");
+    if (def->bridge)
+        virBufferVSprintf(&buf, " name='%s'", def->bridge);
+    virBufferVSprintf(&buf, " stp='%s' forwardDelay='%ld' />\n",
+                      def->stp ? "on" : "off",
+                      def->delay);
+
+    if (def->ipAddress || def->netmask) {
+        virBufferAddLit(&buf, "  <ip");
+
+        if (def->ipAddress)
+            virBufferVSprintf(&buf, " address='%s'", def->ipAddress);
+
+        if (def->netmask)
+            virBufferVSprintf(&buf, " netmask='%s'", def->netmask);
+
+        virBufferAddLit(&buf, ">\n");
+
+        if (def->nranges) {
+            int i;
+            virBufferAddLit(&buf, "    <dhcp>\n");
+            for (i = 0 ; i < def->nranges ; i++)
+                virBufferVSprintf(&buf, "      <range start='%s' end='%s' />\n",
+                                  def->ranges[i].start, def->ranges[i].end);
+            virBufferAddLit(&buf, "    </dhcp>\n");
+        }
+
+        virBufferAddLit(&buf, "  </ip>\n");
+    }
+
+    virBufferAddLit(&buf, "</network>\n");
+
+    if (virBufferError(&buf))
+        goto no_memory;
+
+    return virBufferContentAndReset(&buf);
+
+ no_memory:
+    virNetworkReportError(conn, VIR_ERR_NO_MEMORY, NULL);
+    tmp = virBufferContentAndReset(&buf);
+    VIR_FREE(tmp);
+    return NULL;
+}
+
diff -r a6e5acdd23df src/network_conf.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/network_conf.h	Tue Jun 24 15:07:23 2008 +0100
@@ -0,0 +1,114 @@
+/*
+ * network_conf.h: network XML handling
+ *
+ * Copyright (C) 2006-2008 Red Hat, Inc.
+ * Copyright (C) 2006-2008 Daniel P. Berrange
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#ifndef __NETWORK_CONF_H__
+#define __NETWORK_CONF_H__
+
+#include "internal.h"
+
+/* 2 possible types of forwarding */
+enum virNetworkForwardType {
+    VIR_NETWORK_FORWARD_NONE   = 0,
+    VIR_NETWORK_FORWARD_NAT,
+    VIR_NETWORK_FORWARD_ROUTE,
+
+    VIR_NETWORK_FORWARD_LAST,
+};
+
+typedef struct _virNetworkDHCPRangeDef virNetworkDHCPRangeDef;
+typedef virNetworkDHCPRangeDef *virNetworkDHCPRangeDefPtr;
+struct _virNetworkDHCPRangeDef {
+    char *start;
+    char *end;
+};
+
+typedef struct _virNetworkDef virNetworkDef;
+typedef virNetworkDef *virNetworkDefPtr;
+struct _virNetworkDef {
+    unsigned char uuid[VIR_UUID_BUFLEN];
+    char *name;
+
+    char *bridge;       /* Name of bridge device */
+    int stp : 1; /* Spanning tree protocol */
+    long delay;   /* Bridge forward delay (ms) */
+
+    int forwardType;    /* One of virNetworkForwardType constants */
+    char *forwardDev;   /* Destination device for forwarding */
+
+    char *ipAddress;    /* Bridge IP address */
+    char *netmask;
+    char *network;
+
+    int nranges;        /* Zero or more dhcp ranges */
+    virNetworkDHCPRangeDefPtr ranges;
+};
+
+typedef struct _virNetworkObj virNetworkObj;
+typedef virNetworkObj *virNetworkObjPtr;
+struct _virNetworkObj {
+    int dnsmasqPid;
+    unsigned int active : 1;
+    unsigned int autostart : 1;
+    unsigned int persistent : 1;
+
+    char *configFile;    /* Persistent config file path */
+    char *autostartLink; /* Symlink path for autostart */
+
+    virNetworkDefPtr def; /* The current definition */
+    virNetworkDefPtr newDef; /* New definition to activate at shutdown */
+
+    virNetworkObjPtr next;
+};
+
+static inline int
+virNetworkIsActive(const virNetworkObjPtr net)
+{
+    return net->active;
+}
+
+
+virNetworkObjPtr virNetworkFindByUUID(const virNetworkObjPtr nets,
+                                      const unsigned char *uuid);
+virNetworkObjPtr virNetworkFindByName(const virNetworkObjPtr nets,
+                                      const char *name);
+
+
+void virNetworkDefFree(virNetworkDefPtr def);
+void virNetworkObjFree(virNetworkObjPtr net);
+
+virNetworkObjPtr virNetworkAssignDef(virConnectPtr conn,
+                                     virNetworkObjPtr *nets,
+                                     const virNetworkDefPtr def);
+void virNetworkRemoveInactive(virNetworkObjPtr *nets,
+                              const virNetworkObjPtr net);
+
+virNetworkDefPtr virNetworkDefParse(virConnectPtr conn,
+                                    const char *xmlStr,
+                                    const char *displayName);
+
+char *virNetworkDefFormat(virConnectPtr conn,
+                          const virNetworkDefPtr def);
+
+
+#endif /* __NETWORK_CONF_H__ */
+

-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list