[libvirt] [PATCH] leasetime support for <dhcp>

aruiz@gnome.org posted 1 patch 6 years, 10 months ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/libvirt tags/patchew/20170623004447.9605-1-aruiz@gnome.org
docs/schemas/basictypes.rng                        |  16 +++
docs/schemas/network.rng                           |   8 ++
src/conf/network_conf.c                            |  93 +++++++++++++++-
src/conf/network_conf.h                            |   5 +-
src/libvirt_private.syms                           |   1 +
src/network/bridge_driver.c                        | 119 ++++++++++++++++-----
src/network/bridge_driver.h                        |   1 +
src/util/virdnsmasq.c                              | 106 +++++++++++-------
src/util/virdnsmasq.h                              |   2 +
.../dhcp6-nat-network.hostsfile                    |   7 ++
tests/networkxml2confdata/dhcp6-network.hostsfile  |   5 +
.../dhcp6host-routed-network.hostsfile             |   7 ++
tests/networkxml2confdata/leasetime-days.conf      |  18 ++++
tests/networkxml2confdata/leasetime-days.xml       |  18 ++++
tests/networkxml2confdata/leasetime-hours.conf     |  18 ++++
tests/networkxml2confdata/leasetime-hours.xml      |  18 ++++
tests/networkxml2confdata/leasetime-infinite.conf  |  18 ++++
tests/networkxml2confdata/leasetime-infinite.xml   |  18 ++++
tests/networkxml2confdata/leasetime-minutes.conf   |  18 ++++
tests/networkxml2confdata/leasetime-minutes.xml    |  18 ++++
tests/networkxml2confdata/leasetime-seconds.conf   |  18 ++++
tests/networkxml2confdata/leasetime-seconds.xml    |  18 ++++
tests/networkxml2confdata/leasetime.conf           |  18 ++++
tests/networkxml2confdata/leasetime.xml            |  18 ++++
.../nat-network-dns-srv-record-minimal.hostsfile   |   2 +
.../nat-network-dns-srv-record.hostsfile           |   2 +
.../nat-network-dns-txt-record.hostsfile           |   2 +
.../nat-network-name-with-quotes.hostsfile         |   2 +
tests/networkxml2confdata/nat-network.hostsfile    |   2 +
.../networkxml2confdata/ptr-domains-auto.hostsfile |   2 +
tests/networkxml2conftest.c                        |  45 ++++++--
31 files changed, 571 insertions(+), 72 deletions(-)
create mode 100644 tests/networkxml2confdata/dhcp6-nat-network.hostsfile
create mode 100644 tests/networkxml2confdata/dhcp6-network.hostsfile
create mode 100644 tests/networkxml2confdata/dhcp6host-routed-network.hostsfile
create mode 100644 tests/networkxml2confdata/leasetime-days.conf
create mode 100644 tests/networkxml2confdata/leasetime-days.xml
create mode 100644 tests/networkxml2confdata/leasetime-hours.conf
create mode 100644 tests/networkxml2confdata/leasetime-hours.xml
create mode 100644 tests/networkxml2confdata/leasetime-infinite.conf
create mode 100644 tests/networkxml2confdata/leasetime-infinite.xml
create mode 100644 tests/networkxml2confdata/leasetime-minutes.conf
create mode 100644 tests/networkxml2confdata/leasetime-minutes.xml
create mode 100644 tests/networkxml2confdata/leasetime-seconds.conf
create mode 100644 tests/networkxml2confdata/leasetime-seconds.xml
create mode 100644 tests/networkxml2confdata/leasetime.conf
create mode 100644 tests/networkxml2confdata/leasetime.xml
create mode 100644 tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile
create mode 100644 tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile
create mode 100644 tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile
create mode 100644 tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile
create mode 100644 tests/networkxml2confdata/nat-network.hostsfile
create mode 100644 tests/networkxml2confdata/ptr-domains-auto.hostsfile
[libvirt] [PATCH] leasetime support for <dhcp>
Posted by aruiz@gnome.org 6 years, 10 months ago
From: Alberto Ruiz <aruiz@gnome.org>

Fixes #913446

This patch addresses a few problems found by the initial reviews:

* leaseTimeUnit RNG type renamed to timeUnit
* virNetworkDHCPDefGetLeaseTime() renamed to virNetworkDHCPLeaseTimeParseXML()
* consistent use of braces in if-else-if
* use %lu instead of PRId64
* use 0 as infinite lease
* add a leasetime_defined field to struct _virNetworkIPDef to describe whether the value was set in the xml configuration or not
* use uint32_t for the leasetime instead of int64_t
* fail on all invalid leasetime values
* squash all patches into one

---
 docs/schemas/basictypes.rng                        |  16 +++
 docs/schemas/network.rng                           |   8 ++
 src/conf/network_conf.c                            |  93 +++++++++++++++-
 src/conf/network_conf.h                            |   5 +-
 src/libvirt_private.syms                           |   1 +
 src/network/bridge_driver.c                        | 119 ++++++++++++++++-----
 src/network/bridge_driver.h                        |   1 +
 src/util/virdnsmasq.c                              | 106 +++++++++++-------
 src/util/virdnsmasq.h                              |   2 +
 .../dhcp6-nat-network.hostsfile                    |   7 ++
 tests/networkxml2confdata/dhcp6-network.hostsfile  |   5 +
 .../dhcp6host-routed-network.hostsfile             |   7 ++
 tests/networkxml2confdata/leasetime-days.conf      |  18 ++++
 tests/networkxml2confdata/leasetime-days.xml       |  18 ++++
 tests/networkxml2confdata/leasetime-hours.conf     |  18 ++++
 tests/networkxml2confdata/leasetime-hours.xml      |  18 ++++
 tests/networkxml2confdata/leasetime-infinite.conf  |  18 ++++
 tests/networkxml2confdata/leasetime-infinite.xml   |  18 ++++
 tests/networkxml2confdata/leasetime-minutes.conf   |  18 ++++
 tests/networkxml2confdata/leasetime-minutes.xml    |  18 ++++
 tests/networkxml2confdata/leasetime-seconds.conf   |  18 ++++
 tests/networkxml2confdata/leasetime-seconds.xml    |  18 ++++
 tests/networkxml2confdata/leasetime.conf           |  18 ++++
 tests/networkxml2confdata/leasetime.xml            |  18 ++++
 .../nat-network-dns-srv-record-minimal.hostsfile   |   2 +
 .../nat-network-dns-srv-record.hostsfile           |   2 +
 .../nat-network-dns-txt-record.hostsfile           |   2 +
 .../nat-network-name-with-quotes.hostsfile         |   2 +
 tests/networkxml2confdata/nat-network.hostsfile    |   2 +
 .../networkxml2confdata/ptr-domains-auto.hostsfile |   2 +
 tests/networkxml2conftest.c                        |  45 ++++++--
 31 files changed, 571 insertions(+), 72 deletions(-)
 create mode 100644 tests/networkxml2confdata/dhcp6-nat-network.hostsfile
 create mode 100644 tests/networkxml2confdata/dhcp6-network.hostsfile
 create mode 100644 tests/networkxml2confdata/dhcp6host-routed-network.hostsfile
 create mode 100644 tests/networkxml2confdata/leasetime-days.conf
 create mode 100644 tests/networkxml2confdata/leasetime-days.xml
 create mode 100644 tests/networkxml2confdata/leasetime-hours.conf
 create mode 100644 tests/networkxml2confdata/leasetime-hours.xml
 create mode 100644 tests/networkxml2confdata/leasetime-infinite.conf
 create mode 100644 tests/networkxml2confdata/leasetime-infinite.xml
 create mode 100644 tests/networkxml2confdata/leasetime-minutes.conf
 create mode 100644 tests/networkxml2confdata/leasetime-minutes.xml
 create mode 100644 tests/networkxml2confdata/leasetime-seconds.conf
 create mode 100644 tests/networkxml2confdata/leasetime-seconds.xml
 create mode 100644 tests/networkxml2confdata/leasetime.conf
 create mode 100644 tests/networkxml2confdata/leasetime.xml
 create mode 100644 tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile
 create mode 100644 tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile
 create mode 100644 tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile
 create mode 100644 tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile
 create mode 100644 tests/networkxml2confdata/nat-network.hostsfile
 create mode 100644 tests/networkxml2confdata/ptr-domains-auto.hostsfile

diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng
index 1ea667cdf..9db19c7f0 100644
--- a/docs/schemas/basictypes.rng
+++ b/docs/schemas/basictypes.rng
@@ -564,4 +564,20 @@
     </element>
   </define>
 
+  <define name="timeUnit">
+    <choice>
+      <value>seconds</value>
+      <value>minutes</value>
+      <value>hours</value>
+      <value>days</value>
+    </choice>
+  </define>
+
+  <define name="leaseTime">
+    <data type="long">
+      <param name="minInclusive">-1</param>
+      <param name="maxInclusive">4294967295</param>
+    </data>
+  </define>
+
 </grammar>
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 1048dabf3..a0d878e4a 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -357,6 +357,14 @@
                 <!-- Define the range(s) of IP addresses that the DHCP
                      server should hand out -->
                 <element name="dhcp">
+                <optional>
+                  <element name="leasetime">
+                    <optional>
+                      <attribute name="unit"><ref name="timeUnit"/></attribute>
+                    </optional>
+                    <ref name="leaseTime"/>
+                  </element>
+                </optional>
                   <interleave>
                     <zeroOrMore>
                       <element name="range">
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 3ebf67ff5..8431ee806 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -29,6 +29,8 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
 
 #include "virerror.h"
 #include "datatypes.h"
@@ -514,8 +516,92 @@ virNetworkDHCPHostDefParseXML(const char *networkName,
 
 
 static int
+virNetworkDHCPLeaseTimeParseXML (xmlNodePtr node,
+                                 xmlXPathContextPtr ctxt,
+                                 uint32_t *lease,
+                                 bool     *defined)
+{
+    int ret = 0;
+    uint32_t multiplier;
+    char *leaseString, *leaseUnit;
+    xmlNodePtr save;
+
+    *defined = 0;
+
+    save = ctxt->node;
+    ctxt->node = node;
+
+    leaseString = virXPathString ("string(./leasetime/text())", ctxt);
+    leaseUnit   = virXPathString ("string(./leasetime/@unit)", ctxt);
+
+    /* If value is not present we set the value to -2 */
+    if (leaseString == NULL) {
+        goto cleanup;
+    }
+    if (leaseString[0] == '-') {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("<leasetime> value (%s) cannot be negative"),
+                       leaseString);
+    }
+
+    *defined = 1;
+
+    if (leaseUnit == NULL || strcmp (leaseUnit, "seconds") == 0) {
+        multiplier = 1;
+    }
+    else if (strcmp (leaseUnit, "minutes") == 0) {
+        multiplier = 60;
+    }
+    else if (strcmp (leaseUnit, "hours") == 0) {
+        multiplier = 60 * 60;
+    }
+    else if (strcmp (leaseUnit, "days") == 0) {
+        multiplier = 60 * 60 * 24;
+    }
+    else {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("invalid value for unit parameter in <leasetime> element"
+                         "found in <dhcp> network, only 'seconds', 'minutes', "
+                         "'hours' or 'days' are valid: %s"),
+                       leaseUnit);
+        ret = -1;
+        goto cleanup;
+    }
+
+    errno = 0;
+    *lease = (uint32_t) strtoul((const char*)leaseString, NULL, 10);
+
+    /* Report any errors parsing the string */
+    if (errno != 0) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("<leasetime> value could not be converted to a signed integer: %s"),
+                      leaseString);
+        ret = -1;
+        goto cleanup;
+    }
+
+    if (*lease > (UINT32_MAX / multiplier)) {
+      virReportError (VIR_ERR_XML_ERROR,
+                      _("<leasetime> value %lu %s exceeds the maximum of %lu seconds"),
+                      (unsigned long)*lease, leaseUnit, (unsigned long)UINT32_MAX);
+      ret = -1;
+      goto cleanup;
+    }
+
+    *lease = *lease * multiplier;
+
+cleanup:
+    VIR_FREE(leaseString);
+    VIR_FREE(leaseUnit);
+    ctxt->node = save;
+    return ret;
+}
+
+
+static int
 virNetworkDHCPDefParseXML(const char *networkName,
                           xmlNodePtr node,
+                          xmlXPathContextPtr ctxt,
                           virNetworkIPDefPtr def)
 {
     int ret = -1;
@@ -526,6 +612,11 @@ virNetworkDHCPDefParseXML(const char *networkName,
     memset(&range, 0, sizeof(range));
     memset(&host, 0, sizeof(host));
 
+    if (virNetworkDHCPLeaseTimeParseXML (node, ctxt,
+                                         &def->leasetime,
+                                         &def->leasetime_defined))
+        goto cleanup;
+
     cur = node->children;
     while (cur != NULL) {
         if (cur->type == XML_ELEMENT_NODE &&
@@ -1104,7 +1195,7 @@ virNetworkIPDefParseXML(const char *networkName,
     }
 
     if ((dhcp = virXPathNode("./dhcp[1]", ctxt)) &&
-        virNetworkDHCPDefParseXML(networkName, dhcp, def) < 0)
+        virNetworkDHCPDefParseXML(networkName, dhcp, ctxt, def) < 0)
         goto cleanup;
 
     if (virXPathNode("./tftp[1]", ctxt)) {
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index e9a5baf5b..466220e81 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -175,7 +175,10 @@ struct _virNetworkIPDef {
     char *tftproot;
     char *bootfile;
     virSocketAddr bootserver;
-   };
+
+    uint32_t leasetime;
+    bool leasetime_defined;
+};
 
 typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef;
 typedef virNetworkForwardIfDef *virNetworkForwardIfDefPtr;
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index c1e9471c5..5ef33dcfe 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1561,6 +1561,7 @@ dnsmasqCapsRefresh;
 dnsmasqContextFree;
 dnsmasqContextNew;
 dnsmasqDelete;
+dnsmasqDhcpHostsToString;
 dnsmasqReload;
 dnsmasqSave;
 
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 3ba70180b..cdb0230ab 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -41,6 +41,8 @@
 #include <sys/ioctl.h>
 #include <net/if.h>
 #include <dirent.h>
+#include <inttypes.h>
+#include <stdint.h>
 #if HAVE_SYS_SYSCTL_H
 # include <sys/sysctl.h>
 #endif
@@ -948,6 +950,62 @@ networkKillDaemon(pid_t pid, const char *daemonName, const char *networkName)
     return ret;
 }
 
+static int
+networkBuildDnsmasqHostsList(dnsmasqContext *dctx,
+                             virNetworkDNSDefPtr dnsdef)
+{
+    size_t i, j;
+
+    if (dnsdef) {
+        for (i = 0; i < dnsdef->nhosts; i++) {
+            virNetworkDNSHostDefPtr host = &(dnsdef->hosts[i]);
+            if (VIR_SOCKET_ADDR_VALID(&host->ip)) {
+                for (j = 0; j < host->nnames; j++)
+                    if (dnsmasqAddHost(dctx, &host->ip, host->names[j]) < 0)
+                        return -1;
+            }
+        }
+    }
+
+    return 0;
+}
+
+/* translates the leasetime value into a dnsmasq configuration string
+ * for dhcp-range/host */
+static char *
+networkDnsmasqConfLeaseValueToString (int64_t leasetime, bool defined)
+{
+    char *result = NULL;
+    virBuffer leasebuf = VIR_BUFFER_INITIALIZER;
+
+    /* Leasetime parameter set on the XML */
+
+    /* defined = FALSE means we fallback to dnsmasq defaults*/
+    if (defined == 0) {
+        virBufferAsprintf(&leasebuf, "%s", "");
+    }
+    /* 0 means no expiration */
+    else if (leasetime == 0) {
+        virBufferAsprintf(&leasebuf, ",infinite");
+    }
+    /* DHCP value for lease time is a unsigned four octect integer */
+    else if (leasetime <= UINT32_MAX) {
+        virBufferAsprintf(&leasebuf, ",%lu", (unsigned long)leasetime);
+    }
+    else {
+        if (leasetime < 120)
+          virReportError (VIR_ERR_CONFIG_UNSUPPORTED,
+                          _("lease time must be greater than 120 seconds"));
+        goto cleanup;
+    }
+
+    result = virBufferContentAndReset(&leasebuf);
+
+cleanup:
+    virBufferFreeAndReset (&leasebuf);
+    return result;
+}
+
 /* the following does not build a file, it builds a list
  * which is later saved into a file
  */
@@ -956,8 +1014,14 @@ static int
 networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
                                  virNetworkIPDefPtr ipdef)
 {
+    int ret = -1;
     size_t i;
     bool ipv6 = false;
+    char *leasetime = networkDnsmasqConfLeaseValueToString(ipdef->leasetime,
+                                                           ipdef->leasetime_defined);
+
+    if (!leasetime)
+        goto cleanup;
 
     if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
         ipv6 = true;
@@ -965,31 +1029,14 @@ networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
         virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
         if (VIR_SOCKET_ADDR_VALID(&host->ip))
             if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip,
-                                   host->name, host->id, ipv6) < 0)
-                return -1;
-    }
-
-    return 0;
-}
-
-static int
-networkBuildDnsmasqHostsList(dnsmasqContext *dctx,
-                             virNetworkDNSDefPtr dnsdef)
-{
-    size_t i, j;
-
-    if (dnsdef) {
-        for (i = 0; i < dnsdef->nhosts; i++) {
-            virNetworkDNSHostDefPtr host = &(dnsdef->hosts[i]);
-            if (VIR_SOCKET_ADDR_VALID(&host->ip)) {
-                for (j = 0; j < host->nnames; j++)
-                    if (dnsmasqAddHost(dctx, &host->ip, host->names[j]) < 0)
-                        return -1;
-            }
-        }
+                                   host->name, host->id, leasetime, ipv6) < 0)
+                goto cleanup;
     }
 
-    return 0;
+    ret = 0;
+cleanup:
+    VIR_FREE(leasetime);
+    return ret;
 }
 
 
@@ -1034,6 +1081,7 @@ int
 networkDnsmasqConfContents(virNetworkObjPtr network,
                            const char *pidfile,
                            char **configstr,
+                           char **hostsfilestr,
                            dnsmasqContext *dctx,
                            dnsmasqCapsPtr caps ATTRIBUTE_UNUSED)
 {
@@ -1362,6 +1410,7 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
         }
         for (r = 0; r < ipdef->nranges; r++) {
             int thisRange;
+            char *leasestr;
 
             if (!(saddr = virSocketAddrFormat(&ipdef->ranges[r].start)) ||
                 !(eaddr = virSocketAddrFormat(&ipdef->ranges[r].end)))
@@ -1369,12 +1418,23 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
 
             virBufferAsprintf(&configbuf, "dhcp-range=%s,%s",
                               saddr, eaddr);
-            if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
+
+            /* Add ipv6 prefix length parameter if needed */
+            if (ipdef == ipv6def)
                 virBufferAsprintf(&configbuf, ",%d", prefix);
+
+            leasestr = networkDnsmasqConfLeaseValueToString (ipdef->leasetime,
+                                                             ipdef->leasetime_defined);
+            if (!leasestr)
+                goto cleanup;
+            virBufferAsprintf(&configbuf, "%s", leasestr);
+
+            /* Add the newline */
             virBufferAddLit(&configbuf, "\n");
 
             VIR_FREE(saddr);
             VIR_FREE(eaddr);
+            VIR_FREE(leasestr);
             thisRange = virSocketAddrGetRange(&ipdef->ranges[r].start,
                                               &ipdef->ranges[r].end,
                                               &ipdef->address,
@@ -1405,6 +1465,15 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
         if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0)
             goto cleanup;
 
+        /* Return the contents of the hostsfile if requested */
+        if (hostsfilestr) {
+            *hostsfilestr = dnsmasqDhcpHostsToString (dctx->hostsfile->hosts,
+                                                      dctx->hostsfile->nhosts);
+
+            if (!hostsfilestr)
+                goto cleanup;
+        }
+
         /* Note: the following is IPv4 only */
         if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) {
             if (ipdef->nranges || ipdef->nhosts) {
@@ -1506,7 +1575,7 @@ networkBuildDhcpDaemonCommandLine(virNetworkDriverStatePtr driver,
 
     network->dnsmasqPid = -1;
 
-    if (networkDnsmasqConfContents(network, pidfile, &configstr,
+    if (networkDnsmasqConfContents(network, pidfile, &configstr, NULL,
                                    dctx, dnsmasq_caps) < 0)
         goto cleanup;
     if (!configstr)
diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h
index 7832b6031..56329eb59 100644
--- a/src/network/bridge_driver.h
+++ b/src/network/bridge_driver.h
@@ -53,6 +53,7 @@ int networkGetActualType(virDomainNetDefPtr iface)
 int networkDnsmasqConfContents(virNetworkObjPtr network,
                         const char *pidfile,
                         char **configstr,
+                        char **hostsfilestr,
                         dnsmasqContext *dctx,
                         dnsmasqCapsPtr caps);
 
diff --git a/src/util/virdnsmasq.c b/src/util/virdnsmasq.c
index 1b78c1fad..94c9a3bb1 100644
--- a/src/util/virdnsmasq.c
+++ b/src/util/virdnsmasq.c
@@ -308,52 +308,47 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile,
              virSocketAddr *ip,
              const char *name,
              const char *id,
+             const char *leasetime,
              bool ipv6)
 {
+    int ret = -1;
     char *ipstr = NULL;
+    virBuffer hostbuf = VIR_BUFFER_INITIALIZER;
+
     if (VIR_REALLOC_N(hostsfile->hosts, hostsfile->nhosts + 1) < 0)
         goto error;
 
     if (!(ipstr = virSocketAddrFormat(ip)))
-        return -1;
+        goto error;
 
     /* the first test determines if it is a dhcpv6 host */
     if (ipv6) {
-        if (name && id) {
-            if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host,
-                            "id:%s,%s,[%s]", id, name, ipstr) < 0)
-                goto error;
-        } else if (name && !id) {
-            if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host,
-                            "%s,[%s]", name, ipstr) < 0)
-                goto error;
-        } else if (!name && id) {
-            if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host,
-                            "id:%s,[%s]", id, ipstr) < 0)
-                goto error;
-        }
+        if (name && id)
+            virBufferAsprintf(&hostbuf, "id:%s,%s,[%s]", id, name, ipstr);
+        else if (name && !id)
+            virBufferAsprintf(&hostbuf, "%s,[%s]", name, ipstr);
+        else if (!name && id)
+            virBufferAsprintf(&hostbuf, "id:%s,[%s]", id, ipstr);
     } else if (name && mac) {
-        if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s",
-                        mac, ipstr, name) < 0)
-            goto error;
+        virBufferAsprintf(&hostbuf, "%s,%s,%s", mac, ipstr, name);
     } else if (name && !mac) {
-        if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s",
-                        name, ipstr) < 0)
-            goto error;
+        virBufferAsprintf(&hostbuf, "%s,%s", name, ipstr);
     } else {
-        if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s",
-                        mac, ipstr) < 0)
-            goto error;
+        virBufferAsprintf(&hostbuf, "%s,%s", mac, ipstr);
     }
-    VIR_FREE(ipstr);
 
-    hostsfile->nhosts++;
+    /* The leasetime string already includes comma if there's any value at all */
+    virBufferAsprintf(&hostbuf, "%s", leasetime);
 
-    return 0;
+    if (!(hostsfile->hosts[hostsfile->nhosts].host = virBufferContentAndReset (&hostbuf)))
+      goto error;
 
+    hostsfile->nhosts++;
+    ret = 0;
  error:
+    virBufferFreeAndReset(&hostbuf);
     VIR_FREE(ipstr);
-    return -1;
+    return ret;
 }
 
 static dnsmasqHostsfile *
@@ -391,10 +386,9 @@ hostsfileWrite(const char *path,
                dnsmasqDhcpHost *hosts,
                unsigned int nhosts)
 {
-    char *tmp;
+    char *tmp, *content = NULL;
     FILE *f;
     bool istmp = true;
-    size_t i;
     int rc = 0;
 
     /* even if there are 0 hosts, create a 0 length file, to allow
@@ -412,17 +406,21 @@ hostsfileWrite(const char *path,
         }
     }
 
-    for (i = 0; i < nhosts; i++) {
-        if (fputs(hosts[i].host, f) == EOF || fputc('\n', f) == EOF) {
-            rc = -errno;
-            VIR_FORCE_FCLOSE(f);
+    if (!(content = dnsmasqDhcpHostsToString(hosts, nhosts))) {
+        rc = -ENOMEM;
+        goto cleanup;
+    }
 
-            if (istmp)
-                unlink(tmp);
+    if (fputs(content, f) == EOF) {
+        rc = -errno;
+        VIR_FORCE_FCLOSE(f);
+
+        if (istmp)
+            unlink(tmp);
+
+        goto cleanup;
+     }
 
-            goto cleanup;
-        }
-    }
 
     if (VIR_FCLOSE(f) == EOF) {
         rc = -errno;
@@ -436,6 +434,7 @@ hostsfileWrite(const char *path,
     }
 
  cleanup:
+    VIR_FREE(content);
     VIR_FREE(tmp);
 
     return rc;
@@ -524,9 +523,10 @@ dnsmasqAddDhcpHost(dnsmasqContext *ctx,
                    virSocketAddr *ip,
                    const char *name,
                    const char *id,
+                   const char *leasetime,
                    bool ipv6)
 {
-    return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, ipv6);
+    return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, leasetime, ipv6);
 }
 
 /*
@@ -892,3 +892,31 @@ dnsmasqCapsGet(dnsmasqCapsPtr caps, dnsmasqCapsFlags flag)
 
     return caps && virBitmapIsBitSet(caps->flags, flag);
 }
+
+/** dnsmasqDhcpHostsToString:
+ *
+ *   Turns a vector of dnsmasqDhcpHost into the string that is ought to be
+ *   stored in the hostsfile, this functionality is split to make hostsfiles
+ *   testable. Returs NULL if nhosts is 0.
+ */
+char *
+dnsmasqDhcpHostsToString (dnsmasqDhcpHost *hosts,
+                          unsigned int nhosts)
+{
+    int i;
+    char *result = NULL;
+    virBuffer hostsfilebuf = VIR_BUFFER_INITIALIZER;
+
+    if (nhosts == 0)
+       goto cleanup;
+
+    for (i = 0; i < nhosts; i++) {
+        virBufferAsprintf(&hostsfilebuf, "%s\n", hosts[i].host);
+    }
+
+    result = virBufferContentAndReset(&hostsfilebuf);
+
+cleanup:
+    virBufferFreeAndReset(&hostsfilebuf);
+    return result;
+}
diff --git a/src/util/virdnsmasq.h b/src/util/virdnsmasq.h
index f47bea3ab..1795bc83b 100644
--- a/src/util/virdnsmasq.h
+++ b/src/util/virdnsmasq.h
@@ -88,6 +88,7 @@ int              dnsmasqAddDhcpHost(dnsmasqContext *ctx,
                                     virSocketAddr *ip,
                                     const char *name,
                                     const char *id,
+                                    const char *leastime,
                                     bool ipv6);
 int              dnsmasqAddHost(dnsmasqContext *ctx,
                                 virSocketAddr *ip,
@@ -105,6 +106,7 @@ int dnsmasqCapsRefresh(dnsmasqCapsPtr *caps, const char *binaryPath);
 bool dnsmasqCapsGet(dnsmasqCapsPtr caps, dnsmasqCapsFlags flag);
 const char *dnsmasqCapsGetBinaryPath(dnsmasqCapsPtr caps);
 unsigned long dnsmasqCapsGetVersion(dnsmasqCapsPtr caps);
+char *dnsmasqDhcpHostsToString(dnsmasqDhcpHost *hosts, unsigned int nhosts);
 
 # define DNSMASQ_DHCPv6_MAJOR_REQD 2
 # define DNSMASQ_DHCPv6_MINOR_REQD 64
diff --git a/tests/networkxml2confdata/dhcp6-nat-network.hostsfile b/tests/networkxml2confdata/dhcp6-nat-network.hostsfile
new file mode 100644
index 000000000..de659b98c
--- /dev/null
+++ b/tests/networkxml2confdata/dhcp6-nat-network.hostsfile
@@ -0,0 +1,7 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
+id:0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63,[2001:db8:ac10:fd01::1:20]
+paul,[2001:db8:ac10:fd01::1:21]
+id:0:3:0:1:0:16:3e:11:22:33,peter.xyz,[2001:db8:ac10:fd01::1:22]
+id:0:3:0:1:0:16:3e:44:55:33,[2001:db8:ac10:fd01::1:23]
+id:0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66,badbob,[2001:db8:ac10:fd01::1:24]
diff --git a/tests/networkxml2confdata/dhcp6-network.hostsfile b/tests/networkxml2confdata/dhcp6-network.hostsfile
new file mode 100644
index 000000000..9dfb172ce
--- /dev/null
+++ b/tests/networkxml2confdata/dhcp6-network.hostsfile
@@ -0,0 +1,5 @@
+id:0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63,[2001:db8:ac10:fd01::1:20]
+paul,[2001:db8:ac10:fd01::1:21]
+id:0:3:0:1:0:16:3e:11:22:33,peter.xyz,[2001:db8:ac10:fd01::1:22]
+id:0:3:0:1:0:16:3e:44:55:33,[2001:db8:ac10:fd01::1:23]
+id:0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66,badbob,[2001:db8:ac10:fd01::1:24]
diff --git a/tests/networkxml2confdata/dhcp6host-routed-network.hostsfile b/tests/networkxml2confdata/dhcp6host-routed-network.hostsfile
new file mode 100644
index 000000000..de659b98c
--- /dev/null
+++ b/tests/networkxml2confdata/dhcp6host-routed-network.hostsfile
@@ -0,0 +1,7 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
+id:0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63,[2001:db8:ac10:fd01::1:20]
+paul,[2001:db8:ac10:fd01::1:21]
+id:0:3:0:1:0:16:3e:11:22:33,peter.xyz,[2001:db8:ac10:fd01::1:22]
+id:0:3:0:1:0:16:3e:44:55:33,[2001:db8:ac10:fd01::1:23]
+id:0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66,badbob,[2001:db8:ac10:fd01::1:24]
diff --git a/tests/networkxml2confdata/leasetime-days.conf b/tests/networkxml2confdata/leasetime-days.conf
new file mode 100644
index 000000000..d227be67a
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-days.conf
@@ -0,0 +1,18 @@
+##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
+##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
+##    virsh net-edit default
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+except-interface=lo
+bind-dynamic
+interface=virbr0
+dhcp-range=192.168.122.2,192.168.122.254,86400
+dhcp-no-override
+dhcp-authoritative
+dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,86400
+dhcp-lease-max=493
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
+enable-ra
diff --git a/tests/networkxml2confdata/leasetime-days.xml b/tests/networkxml2confdata/leasetime-days.xml
new file mode 100644
index 000000000..b990b4d68
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-days.xml
@@ -0,0 +1,18 @@
+<network>
+  <name>default</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward dev='eth1' mode='nat'/>
+  <bridge name='virbr0' stp='on' delay='0'/>
+  <ip address='192.168.122.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime unit="days">1</leasetime>
+      <range start='192.168.122.2' end='192.168.122.254'/>
+    </dhcp>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+    <dhcp>
+      <leasetime unit="days">1</leasetime>
+      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2confdata/leasetime-hours.conf b/tests/networkxml2confdata/leasetime-hours.conf
new file mode 100644
index 000000000..e5e8c19cd
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-hours.conf
@@ -0,0 +1,18 @@
+##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
+##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
+##    virsh net-edit default
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+except-interface=lo
+bind-dynamic
+interface=virbr0
+dhcp-range=192.168.122.2,192.168.122.254,3600
+dhcp-no-override
+dhcp-authoritative
+dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,3600
+dhcp-lease-max=493
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
+enable-ra
diff --git a/tests/networkxml2confdata/leasetime-hours.xml b/tests/networkxml2confdata/leasetime-hours.xml
new file mode 100644
index 000000000..3b9609601
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-hours.xml
@@ -0,0 +1,18 @@
+<network>
+  <name>default</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward dev='eth1' mode='nat'/>
+  <bridge name='virbr0' stp='on' delay='0'/>
+  <ip address='192.168.122.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime unit="hours">1</leasetime>
+      <range start='192.168.122.2' end='192.168.122.254'/>
+    </dhcp>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+    <dhcp>
+      <leasetime unit="hours">1</leasetime>
+      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2confdata/leasetime-infinite.conf b/tests/networkxml2confdata/leasetime-infinite.conf
new file mode 100644
index 000000000..52f4798e5
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-infinite.conf
@@ -0,0 +1,18 @@
+##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
+##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
+##    virsh net-edit default
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+except-interface=lo
+bind-dynamic
+interface=virbr0
+dhcp-range=192.168.122.2,192.168.122.254,infinite
+dhcp-no-override
+dhcp-authoritative
+dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,infinite
+dhcp-lease-max=493
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
+enable-ra
diff --git a/tests/networkxml2confdata/leasetime-infinite.xml b/tests/networkxml2confdata/leasetime-infinite.xml
new file mode 100644
index 000000000..d855978a1
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-infinite.xml
@@ -0,0 +1,18 @@
+<network>
+  <name>default</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward dev='eth1' mode='nat'/>
+  <bridge name='virbr0' stp='on' delay='0'/>
+  <ip address='192.168.122.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime>0</leasetime>
+      <range start='192.168.122.2' end='192.168.122.254'/>
+    </dhcp>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+    <dhcp>
+      <leasetime>0</leasetime>
+      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2confdata/leasetime-minutes.conf b/tests/networkxml2confdata/leasetime-minutes.conf
new file mode 100644
index 000000000..37da5702f
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-minutes.conf
@@ -0,0 +1,18 @@
+##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
+##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
+##    virsh net-edit default
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+except-interface=lo
+bind-dynamic
+interface=virbr0
+dhcp-range=192.168.122.2,192.168.122.254,300
+dhcp-no-override
+dhcp-authoritative
+dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,300
+dhcp-lease-max=493
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
+enable-ra
diff --git a/tests/networkxml2confdata/leasetime-minutes.xml b/tests/networkxml2confdata/leasetime-minutes.xml
new file mode 100644
index 000000000..e7a27afe6
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-minutes.xml
@@ -0,0 +1,18 @@
+<network>
+  <name>default</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward dev='eth1' mode='nat'/>
+  <bridge name='virbr0' stp='on' delay='0'/>
+  <ip address='192.168.122.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime unit="minutes">5</leasetime>
+      <range start='192.168.122.2' end='192.168.122.254'/>
+    </dhcp>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+    <dhcp>
+      <leasetime unit="minutes">5</leasetime>
+      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2confdata/leasetime-seconds.conf b/tests/networkxml2confdata/leasetime-seconds.conf
new file mode 100644
index 000000000..ea0845e21
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-seconds.conf
@@ -0,0 +1,18 @@
+##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
+##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
+##    virsh net-edit default
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+except-interface=lo
+bind-dynamic
+interface=virbr0
+dhcp-range=192.168.122.2,192.168.122.254,125
+dhcp-no-override
+dhcp-authoritative
+dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,125
+dhcp-lease-max=493
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
+enable-ra
diff --git a/tests/networkxml2confdata/leasetime-seconds.xml b/tests/networkxml2confdata/leasetime-seconds.xml
new file mode 100644
index 000000000..56b07f8ae
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime-seconds.xml
@@ -0,0 +1,18 @@
+<network>
+  <name>default</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward dev='eth1' mode='nat'/>
+  <bridge name='virbr0' stp='on' delay='0'/>
+  <ip address='192.168.122.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime unit="seconds">125</leasetime>
+      <range start='192.168.122.2' end='192.168.122.254'/>
+    </dhcp>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+    <dhcp>
+      <leasetime unit="seconds">125</leasetime>
+      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2confdata/leasetime.conf b/tests/networkxml2confdata/leasetime.conf
new file mode 100644
index 000000000..b754c3ffb
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime.conf
@@ -0,0 +1,18 @@
+##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
+##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
+##    virsh net-edit default
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+except-interface=lo
+bind-dynamic
+interface=virbr0
+dhcp-range=192.168.122.2,192.168.122.254,122
+dhcp-no-override
+dhcp-authoritative
+dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,121
+dhcp-lease-max=493
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
+enable-ra
diff --git a/tests/networkxml2confdata/leasetime.xml b/tests/networkxml2confdata/leasetime.xml
new file mode 100644
index 000000000..fdbb15fc0
--- /dev/null
+++ b/tests/networkxml2confdata/leasetime.xml
@@ -0,0 +1,18 @@
+<network>
+  <name>default</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward dev='eth1' mode='nat'/>
+  <bridge name='virbr0' stp='on' delay='0'/>
+  <ip address='192.168.122.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime>122</leasetime>
+      <range start='192.168.122.2' end='192.168.122.254'/>
+    </dhcp>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
+    <dhcp>
+      <leasetime>121</leasetime>
+      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile b/tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile
new file mode 100644
index 000000000..deb3f00ac
--- /dev/null
+++ b/tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile
@@ -0,0 +1,2 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
diff --git a/tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile b/tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile
new file mode 100644
index 000000000..deb3f00ac
--- /dev/null
+++ b/tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile
@@ -0,0 +1,2 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
diff --git a/tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile b/tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile
new file mode 100644
index 000000000..deb3f00ac
--- /dev/null
+++ b/tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile
@@ -0,0 +1,2 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
diff --git a/tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile b/tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile
new file mode 100644
index 000000000..deb3f00ac
--- /dev/null
+++ b/tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile
@@ -0,0 +1,2 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
diff --git a/tests/networkxml2confdata/nat-network.hostsfile b/tests/networkxml2confdata/nat-network.hostsfile
new file mode 100644
index 000000000..deb3f00ac
--- /dev/null
+++ b/tests/networkxml2confdata/nat-network.hostsfile
@@ -0,0 +1,2 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
diff --git a/tests/networkxml2confdata/ptr-domains-auto.hostsfile b/tests/networkxml2confdata/ptr-domains-auto.hostsfile
new file mode 100644
index 000000000..deb3f00ac
--- /dev/null
+++ b/tests/networkxml2confdata/ptr-domains-auto.hostsfile
@@ -0,0 +1,2 @@
+00:16:3e:77:e2:ed,192.168.122.10,a.example.com
+00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
diff --git a/tests/networkxml2conftest.c b/tests/networkxml2conftest.c
index ab3c13aa0..15f783fb1 100644
--- a/tests/networkxml2conftest.c
+++ b/tests/networkxml2conftest.c
@@ -19,9 +19,13 @@
 #define VIR_FROM_THIS VIR_FROM_NONE
 
 static int
-testCompareXMLToConfFiles(const char *inxml, const char *outconf, dnsmasqCapsPtr caps)
+testCompareXMLToConfFiles(const char *inxml,
+                          const char *outconf,
+                          const char *outhostsfile,
+                          dnsmasqCapsPtr caps)
 {
-    char *actual = NULL;
+    char *actualconf = NULL;
+    char *actualhosts = NULL;
     int ret = -1;
     virNetworkDefPtr dev = NULL;
     virNetworkObjPtr obj = NULL;
@@ -41,7 +45,11 @@ testCompareXMLToConfFiles(const char *inxml, const char *outconf, dnsmasqCapsPtr
     if (dctx == NULL)
         goto fail;
 
-    if (networkDnsmasqConfContents(obj, pidfile, &actual, dctx, caps) < 0)
+    if (networkDnsmasqConfContents(obj, pidfile, &actualconf, &actualhosts,
+                        dctx, caps) < 0)
+        goto fail;
+
+    if (virTestCompareToFile(actualconf, outconf) < 0)
         goto fail;
 
     /* Any changes to this function ^^ should be reflected here too. */
@@ -57,13 +65,27 @@ testCompareXMLToConfFiles(const char *inxml, const char *outconf, dnsmasqCapsPtr
     tmp = NULL;
 #endif
 
-    if (virTestCompareToFile(actual, outconf) < 0)
+    if (virFileExists(outhostsfile)) {
+        if (!actualhosts) {
+            fprintf(stderr,
+                    "%s: hostsfile exists but the configuration did not specify any host",
+                    outhostsfile);
+            goto fail;
+        } else if (virTestCompareToFile(actualhosts, outhostsfile) < 0) {
+            goto fail;
+        }
+    } else if (actualhosts) {
+        fprintf(stderr,
+                "%s: file does not exist but actual data was expected",
+                outhostsfile);
         goto fail;
+    }
 
     ret = 0;
 
  fail:
-    VIR_FREE(actual);
+    VIR_FREE(actualconf);
+    VIR_FREE(actualhosts);
     VIR_FREE(pidfile);
     virCommandFree(cmd);
     virObjectUnref(obj);
@@ -83,19 +105,23 @@ testCompareXMLToConfHelper(const void *data)
     const testInfo *info = data;
     char *inxml = NULL;
     char *outconf = NULL;
+    char *outhostsfile = NULL;
 
     if (virAsprintf(&inxml, "%s/networkxml2confdata/%s.xml",
                     abs_srcdir, info->name) < 0 ||
         virAsprintf(&outconf, "%s/networkxml2confdata/%s.conf",
+                    abs_srcdir, info->name) < 0 ||
+        virAsprintf(&outhostsfile, "%s/networkxml2confdata/%s.hostsfile",
                     abs_srcdir, info->name) < 0) {
         goto cleanup;
     }
 
-    result = testCompareXMLToConfFiles(inxml, outconf, info->caps);
+    result = testCompareXMLToConfFiles(inxml, outconf, outhostsfile, info->caps);
 
  cleanup:
     VIR_FREE(inxml);
     VIR_FREE(outconf);
+    VIR_FREE(outhostsfile);
 
     return result;
 }
@@ -143,6 +169,13 @@ mymain(void)
     DO_TEST("dhcp6-nat-network", dhcpv6);
     DO_TEST("dhcp6host-routed-network", dhcpv6);
     DO_TEST("ptr-domains-auto", dhcpv6);
+    DO_TEST("leasetime", dhcpv6);
+    DO_TEST("leasetime-seconds", dhcpv6);
+    DO_TEST("leasetime-hours", dhcpv6);
+    DO_TEST("leasetime-minutes", dhcpv6);
+    DO_TEST("leasetime-hours", dhcpv6);
+    DO_TEST("leasetime-days", dhcpv6);
+    DO_TEST("leasetime-infinite", dhcpv6);
 
     virObjectUnref(dhcpv6);
     virObjectUnref(full);
-- 
2.13.0

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] leasetime support for <dhcp>
Posted by Alberto Ruiz 6 years, 10 months ago
Hey Laine,

Have some time to review this?

2017-06-23 1:44 GMT+01:00  <aruiz@gnome.org>:
> From: Alberto Ruiz <aruiz@gnome.org>
>
> Fixes #913446
>
> This patch addresses a few problems found by the initial reviews:
>
> * leaseTimeUnit RNG type renamed to timeUnit
> * virNetworkDHCPDefGetLeaseTime() renamed to virNetworkDHCPLeaseTimeParseXML()
> * consistent use of braces in if-else-if
> * use %lu instead of PRId64
> * use 0 as infinite lease
> * add a leasetime_defined field to struct _virNetworkIPDef to describe whether the value was set in the xml configuration or not
> * use uint32_t for the leasetime instead of int64_t
> * fail on all invalid leasetime values
> * squash all patches into one
>
> ---
>  docs/schemas/basictypes.rng                        |  16 +++
>  docs/schemas/network.rng                           |   8 ++
>  src/conf/network_conf.c                            |  93 +++++++++++++++-
>  src/conf/network_conf.h                            |   5 +-
>  src/libvirt_private.syms                           |   1 +
>  src/network/bridge_driver.c                        | 119 ++++++++++++++++-----
>  src/network/bridge_driver.h                        |   1 +
>  src/util/virdnsmasq.c                              | 106 +++++++++++-------
>  src/util/virdnsmasq.h                              |   2 +
>  .../dhcp6-nat-network.hostsfile                    |   7 ++
>  tests/networkxml2confdata/dhcp6-network.hostsfile  |   5 +
>  .../dhcp6host-routed-network.hostsfile             |   7 ++
>  tests/networkxml2confdata/leasetime-days.conf      |  18 ++++
>  tests/networkxml2confdata/leasetime-days.xml       |  18 ++++
>  tests/networkxml2confdata/leasetime-hours.conf     |  18 ++++
>  tests/networkxml2confdata/leasetime-hours.xml      |  18 ++++
>  tests/networkxml2confdata/leasetime-infinite.conf  |  18 ++++
>  tests/networkxml2confdata/leasetime-infinite.xml   |  18 ++++
>  tests/networkxml2confdata/leasetime-minutes.conf   |  18 ++++
>  tests/networkxml2confdata/leasetime-minutes.xml    |  18 ++++
>  tests/networkxml2confdata/leasetime-seconds.conf   |  18 ++++
>  tests/networkxml2confdata/leasetime-seconds.xml    |  18 ++++
>  tests/networkxml2confdata/leasetime.conf           |  18 ++++
>  tests/networkxml2confdata/leasetime.xml            |  18 ++++
>  .../nat-network-dns-srv-record-minimal.hostsfile   |   2 +
>  .../nat-network-dns-srv-record.hostsfile           |   2 +
>  .../nat-network-dns-txt-record.hostsfile           |   2 +
>  .../nat-network-name-with-quotes.hostsfile         |   2 +
>  tests/networkxml2confdata/nat-network.hostsfile    |   2 +
>  .../networkxml2confdata/ptr-domains-auto.hostsfile |   2 +
>  tests/networkxml2conftest.c                        |  45 ++++++--
>  31 files changed, 571 insertions(+), 72 deletions(-)
>  create mode 100644 tests/networkxml2confdata/dhcp6-nat-network.hostsfile
>  create mode 100644 tests/networkxml2confdata/dhcp6-network.hostsfile
>  create mode 100644 tests/networkxml2confdata/dhcp6host-routed-network.hostsfile
>  create mode 100644 tests/networkxml2confdata/leasetime-days.conf
>  create mode 100644 tests/networkxml2confdata/leasetime-days.xml
>  create mode 100644 tests/networkxml2confdata/leasetime-hours.conf
>  create mode 100644 tests/networkxml2confdata/leasetime-hours.xml
>  create mode 100644 tests/networkxml2confdata/leasetime-infinite.conf
>  create mode 100644 tests/networkxml2confdata/leasetime-infinite.xml
>  create mode 100644 tests/networkxml2confdata/leasetime-minutes.conf
>  create mode 100644 tests/networkxml2confdata/leasetime-minutes.xml
>  create mode 100644 tests/networkxml2confdata/leasetime-seconds.conf
>  create mode 100644 tests/networkxml2confdata/leasetime-seconds.xml
>  create mode 100644 tests/networkxml2confdata/leasetime.conf
>  create mode 100644 tests/networkxml2confdata/leasetime.xml
>  create mode 100644 tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile
>  create mode 100644 tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile
>  create mode 100644 tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile
>  create mode 100644 tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile
>  create mode 100644 tests/networkxml2confdata/nat-network.hostsfile
>  create mode 100644 tests/networkxml2confdata/ptr-domains-auto.hostsfile
>
> diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng
> index 1ea667cdf..9db19c7f0 100644
> --- a/docs/schemas/basictypes.rng
> +++ b/docs/schemas/basictypes.rng
> @@ -564,4 +564,20 @@
>      </element>
>    </define>
>
> +  <define name="timeUnit">
> +    <choice>
> +      <value>seconds</value>
> +      <value>minutes</value>
> +      <value>hours</value>
> +      <value>days</value>
> +    </choice>
> +  </define>
> +
> +  <define name="leaseTime">
> +    <data type="long">
> +      <param name="minInclusive">-1</param>
> +      <param name="maxInclusive">4294967295</param>
> +    </data>
> +  </define>
> +
>  </grammar>
> diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
> index 1048dabf3..a0d878e4a 100644
> --- a/docs/schemas/network.rng
> +++ b/docs/schemas/network.rng
> @@ -357,6 +357,14 @@
>                  <!-- Define the range(s) of IP addresses that the DHCP
>                       server should hand out -->
>                  <element name="dhcp">
> +                <optional>
> +                  <element name="leasetime">
> +                    <optional>
> +                      <attribute name="unit"><ref name="timeUnit"/></attribute>
> +                    </optional>
> +                    <ref name="leaseTime"/>
> +                  </element>
> +                </optional>
>                    <interleave>
>                      <zeroOrMore>
>                        <element name="range">
> diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
> index 3ebf67ff5..8431ee806 100644
> --- a/src/conf/network_conf.c
> +++ b/src/conf/network_conf.c
> @@ -29,6 +29,8 @@
>  #include <sys/stat.h>
>  #include <fcntl.h>
>  #include <string.h>
> +#include <stdlib.h>
> +#include <inttypes.h>
>
>  #include "virerror.h"
>  #include "datatypes.h"
> @@ -514,8 +516,92 @@ virNetworkDHCPHostDefParseXML(const char *networkName,
>
>
>  static int
> +virNetworkDHCPLeaseTimeParseXML (xmlNodePtr node,
> +                                 xmlXPathContextPtr ctxt,
> +                                 uint32_t *lease,
> +                                 bool     *defined)
> +{
> +    int ret = 0;
> +    uint32_t multiplier;
> +    char *leaseString, *leaseUnit;
> +    xmlNodePtr save;
> +
> +    *defined = 0;
> +
> +    save = ctxt->node;
> +    ctxt->node = node;
> +
> +    leaseString = virXPathString ("string(./leasetime/text())", ctxt);
> +    leaseUnit   = virXPathString ("string(./leasetime/@unit)", ctxt);
> +
> +    /* If value is not present we set the value to -2 */
> +    if (leaseString == NULL) {
> +        goto cleanup;
> +    }
> +    if (leaseString[0] == '-') {
> +        virReportError(VIR_ERR_XML_ERROR,
> +                       _("<leasetime> value (%s) cannot be negative"),
> +                       leaseString);
> +    }
> +
> +    *defined = 1;
> +
> +    if (leaseUnit == NULL || strcmp (leaseUnit, "seconds") == 0) {
> +        multiplier = 1;
> +    }
> +    else if (strcmp (leaseUnit, "minutes") == 0) {
> +        multiplier = 60;
> +    }
> +    else if (strcmp (leaseUnit, "hours") == 0) {
> +        multiplier = 60 * 60;
> +    }
> +    else if (strcmp (leaseUnit, "days") == 0) {
> +        multiplier = 60 * 60 * 24;
> +    }
> +    else {
> +        virReportError(VIR_ERR_XML_ERROR,
> +                       _("invalid value for unit parameter in <leasetime> element"
> +                         "found in <dhcp> network, only 'seconds', 'minutes', "
> +                         "'hours' or 'days' are valid: %s"),
> +                       leaseUnit);
> +        ret = -1;
> +        goto cleanup;
> +    }
> +
> +    errno = 0;
> +    *lease = (uint32_t) strtoul((const char*)leaseString, NULL, 10);
> +
> +    /* Report any errors parsing the string */
> +    if (errno != 0) {
> +        virReportError(VIR_ERR_XML_ERROR,
> +                       _("<leasetime> value could not be converted to a signed integer: %s"),
> +                      leaseString);
> +        ret = -1;
> +        goto cleanup;
> +    }
> +
> +    if (*lease > (UINT32_MAX / multiplier)) {
> +      virReportError (VIR_ERR_XML_ERROR,
> +                      _("<leasetime> value %lu %s exceeds the maximum of %lu seconds"),
> +                      (unsigned long)*lease, leaseUnit, (unsigned long)UINT32_MAX);
> +      ret = -1;
> +      goto cleanup;
> +    }
> +
> +    *lease = *lease * multiplier;
> +
> +cleanup:
> +    VIR_FREE(leaseString);
> +    VIR_FREE(leaseUnit);
> +    ctxt->node = save;
> +    return ret;
> +}
> +
> +
> +static int
>  virNetworkDHCPDefParseXML(const char *networkName,
>                            xmlNodePtr node,
> +                          xmlXPathContextPtr ctxt,
>                            virNetworkIPDefPtr def)
>  {
>      int ret = -1;
> @@ -526,6 +612,11 @@ virNetworkDHCPDefParseXML(const char *networkName,
>      memset(&range, 0, sizeof(range));
>      memset(&host, 0, sizeof(host));
>
> +    if (virNetworkDHCPLeaseTimeParseXML (node, ctxt,
> +                                         &def->leasetime,
> +                                         &def->leasetime_defined))
> +        goto cleanup;
> +
>      cur = node->children;
>      while (cur != NULL) {
>          if (cur->type == XML_ELEMENT_NODE &&
> @@ -1104,7 +1195,7 @@ virNetworkIPDefParseXML(const char *networkName,
>      }
>
>      if ((dhcp = virXPathNode("./dhcp[1]", ctxt)) &&
> -        virNetworkDHCPDefParseXML(networkName, dhcp, def) < 0)
> +        virNetworkDHCPDefParseXML(networkName, dhcp, ctxt, def) < 0)
>          goto cleanup;
>
>      if (virXPathNode("./tftp[1]", ctxt)) {
> diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
> index e9a5baf5b..466220e81 100644
> --- a/src/conf/network_conf.h
> +++ b/src/conf/network_conf.h
> @@ -175,7 +175,10 @@ struct _virNetworkIPDef {
>      char *tftproot;
>      char *bootfile;
>      virSocketAddr bootserver;
> -   };
> +
> +    uint32_t leasetime;
> +    bool leasetime_defined;
> +};
>
>  typedef struct _virNetworkForwardIfDef virNetworkForwardIfDef;
>  typedef virNetworkForwardIfDef *virNetworkForwardIfDefPtr;
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index c1e9471c5..5ef33dcfe 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -1561,6 +1561,7 @@ dnsmasqCapsRefresh;
>  dnsmasqContextFree;
>  dnsmasqContextNew;
>  dnsmasqDelete;
> +dnsmasqDhcpHostsToString;
>  dnsmasqReload;
>  dnsmasqSave;
>
> diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
> index 3ba70180b..cdb0230ab 100644
> --- a/src/network/bridge_driver.c
> +++ b/src/network/bridge_driver.c
> @@ -41,6 +41,8 @@
>  #include <sys/ioctl.h>
>  #include <net/if.h>
>  #include <dirent.h>
> +#include <inttypes.h>
> +#include <stdint.h>
>  #if HAVE_SYS_SYSCTL_H
>  # include <sys/sysctl.h>
>  #endif
> @@ -948,6 +950,62 @@ networkKillDaemon(pid_t pid, const char *daemonName, const char *networkName)
>      return ret;
>  }
>
> +static int
> +networkBuildDnsmasqHostsList(dnsmasqContext *dctx,
> +                             virNetworkDNSDefPtr dnsdef)
> +{
> +    size_t i, j;
> +
> +    if (dnsdef) {
> +        for (i = 0; i < dnsdef->nhosts; i++) {
> +            virNetworkDNSHostDefPtr host = &(dnsdef->hosts[i]);
> +            if (VIR_SOCKET_ADDR_VALID(&host->ip)) {
> +                for (j = 0; j < host->nnames; j++)
> +                    if (dnsmasqAddHost(dctx, &host->ip, host->names[j]) < 0)
> +                        return -1;
> +            }
> +        }
> +    }
> +
> +    return 0;
> +}
> +
> +/* translates the leasetime value into a dnsmasq configuration string
> + * for dhcp-range/host */
> +static char *
> +networkDnsmasqConfLeaseValueToString (int64_t leasetime, bool defined)
> +{
> +    char *result = NULL;
> +    virBuffer leasebuf = VIR_BUFFER_INITIALIZER;
> +
> +    /* Leasetime parameter set on the XML */
> +
> +    /* defined = FALSE means we fallback to dnsmasq defaults*/
> +    if (defined == 0) {
> +        virBufferAsprintf(&leasebuf, "%s", "");
> +    }
> +    /* 0 means no expiration */
> +    else if (leasetime == 0) {
> +        virBufferAsprintf(&leasebuf, ",infinite");
> +    }
> +    /* DHCP value for lease time is a unsigned four octect integer */
> +    else if (leasetime <= UINT32_MAX) {
> +        virBufferAsprintf(&leasebuf, ",%lu", (unsigned long)leasetime);
> +    }
> +    else {
> +        if (leasetime < 120)
> +          virReportError (VIR_ERR_CONFIG_UNSUPPORTED,
> +                          _("lease time must be greater than 120 seconds"));
> +        goto cleanup;
> +    }
> +
> +    result = virBufferContentAndReset(&leasebuf);
> +
> +cleanup:
> +    virBufferFreeAndReset (&leasebuf);
> +    return result;
> +}
> +
>  /* the following does not build a file, it builds a list
>   * which is later saved into a file
>   */
> @@ -956,8 +1014,14 @@ static int
>  networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
>                                   virNetworkIPDefPtr ipdef)
>  {
> +    int ret = -1;
>      size_t i;
>      bool ipv6 = false;
> +    char *leasetime = networkDnsmasqConfLeaseValueToString(ipdef->leasetime,
> +                                                           ipdef->leasetime_defined);
> +
> +    if (!leasetime)
> +        goto cleanup;
>
>      if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
>          ipv6 = true;
> @@ -965,31 +1029,14 @@ networkBuildDnsmasqDhcpHostsList(dnsmasqContext *dctx,
>          virNetworkDHCPHostDefPtr host = &(ipdef->hosts[i]);
>          if (VIR_SOCKET_ADDR_VALID(&host->ip))
>              if (dnsmasqAddDhcpHost(dctx, host->mac, &host->ip,
> -                                   host->name, host->id, ipv6) < 0)
> -                return -1;
> -    }
> -
> -    return 0;
> -}
> -
> -static int
> -networkBuildDnsmasqHostsList(dnsmasqContext *dctx,
> -                             virNetworkDNSDefPtr dnsdef)
> -{
> -    size_t i, j;
> -
> -    if (dnsdef) {
> -        for (i = 0; i < dnsdef->nhosts; i++) {
> -            virNetworkDNSHostDefPtr host = &(dnsdef->hosts[i]);
> -            if (VIR_SOCKET_ADDR_VALID(&host->ip)) {
> -                for (j = 0; j < host->nnames; j++)
> -                    if (dnsmasqAddHost(dctx, &host->ip, host->names[j]) < 0)
> -                        return -1;
> -            }
> -        }
> +                                   host->name, host->id, leasetime, ipv6) < 0)
> +                goto cleanup;
>      }
>
> -    return 0;
> +    ret = 0;
> +cleanup:
> +    VIR_FREE(leasetime);
> +    return ret;
>  }
>
>
> @@ -1034,6 +1081,7 @@ int
>  networkDnsmasqConfContents(virNetworkObjPtr network,
>                             const char *pidfile,
>                             char **configstr,
> +                           char **hostsfilestr,
>                             dnsmasqContext *dctx,
>                             dnsmasqCapsPtr caps ATTRIBUTE_UNUSED)
>  {
> @@ -1362,6 +1410,7 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
>          }
>          for (r = 0; r < ipdef->nranges; r++) {
>              int thisRange;
> +            char *leasestr;
>
>              if (!(saddr = virSocketAddrFormat(&ipdef->ranges[r].start)) ||
>                  !(eaddr = virSocketAddrFormat(&ipdef->ranges[r].end)))
> @@ -1369,12 +1418,23 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
>
>              virBufferAsprintf(&configbuf, "dhcp-range=%s,%s",
>                                saddr, eaddr);
> -            if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
> +
> +            /* Add ipv6 prefix length parameter if needed */
> +            if (ipdef == ipv6def)
>                  virBufferAsprintf(&configbuf, ",%d", prefix);
> +
> +            leasestr = networkDnsmasqConfLeaseValueToString (ipdef->leasetime,
> +                                                             ipdef->leasetime_defined);
> +            if (!leasestr)
> +                goto cleanup;
> +            virBufferAsprintf(&configbuf, "%s", leasestr);
> +
> +            /* Add the newline */
>              virBufferAddLit(&configbuf, "\n");
>
>              VIR_FREE(saddr);
>              VIR_FREE(eaddr);
> +            VIR_FREE(leasestr);
>              thisRange = virSocketAddrGetRange(&ipdef->ranges[r].start,
>                                                &ipdef->ranges[r].end,
>                                                &ipdef->address,
> @@ -1405,6 +1465,15 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
>          if (networkBuildDnsmasqDhcpHostsList(dctx, ipdef) < 0)
>              goto cleanup;
>
> +        /* Return the contents of the hostsfile if requested */
> +        if (hostsfilestr) {
> +            *hostsfilestr = dnsmasqDhcpHostsToString (dctx->hostsfile->hosts,
> +                                                      dctx->hostsfile->nhosts);
> +
> +            if (!hostsfilestr)
> +                goto cleanup;
> +        }
> +
>          /* Note: the following is IPv4 only */
>          if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET)) {
>              if (ipdef->nranges || ipdef->nhosts) {
> @@ -1506,7 +1575,7 @@ networkBuildDhcpDaemonCommandLine(virNetworkDriverStatePtr driver,
>
>      network->dnsmasqPid = -1;
>
> -    if (networkDnsmasqConfContents(network, pidfile, &configstr,
> +    if (networkDnsmasqConfContents(network, pidfile, &configstr, NULL,
>                                     dctx, dnsmasq_caps) < 0)
>          goto cleanup;
>      if (!configstr)
> diff --git a/src/network/bridge_driver.h b/src/network/bridge_driver.h
> index 7832b6031..56329eb59 100644
> --- a/src/network/bridge_driver.h
> +++ b/src/network/bridge_driver.h
> @@ -53,6 +53,7 @@ int networkGetActualType(virDomainNetDefPtr iface)
>  int networkDnsmasqConfContents(virNetworkObjPtr network,
>                          const char *pidfile,
>                          char **configstr,
> +                        char **hostsfilestr,
>                          dnsmasqContext *dctx,
>                          dnsmasqCapsPtr caps);
>
> diff --git a/src/util/virdnsmasq.c b/src/util/virdnsmasq.c
> index 1b78c1fad..94c9a3bb1 100644
> --- a/src/util/virdnsmasq.c
> +++ b/src/util/virdnsmasq.c
> @@ -308,52 +308,47 @@ hostsfileAdd(dnsmasqHostsfile *hostsfile,
>               virSocketAddr *ip,
>               const char *name,
>               const char *id,
> +             const char *leasetime,
>               bool ipv6)
>  {
> +    int ret = -1;
>      char *ipstr = NULL;
> +    virBuffer hostbuf = VIR_BUFFER_INITIALIZER;
> +
>      if (VIR_REALLOC_N(hostsfile->hosts, hostsfile->nhosts + 1) < 0)
>          goto error;
>
>      if (!(ipstr = virSocketAddrFormat(ip)))
> -        return -1;
> +        goto error;
>
>      /* the first test determines if it is a dhcpv6 host */
>      if (ipv6) {
> -        if (name && id) {
> -            if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host,
> -                            "id:%s,%s,[%s]", id, name, ipstr) < 0)
> -                goto error;
> -        } else if (name && !id) {
> -            if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host,
> -                            "%s,[%s]", name, ipstr) < 0)
> -                goto error;
> -        } else if (!name && id) {
> -            if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host,
> -                            "id:%s,[%s]", id, ipstr) < 0)
> -                goto error;
> -        }
> +        if (name && id)
> +            virBufferAsprintf(&hostbuf, "id:%s,%s,[%s]", id, name, ipstr);
> +        else if (name && !id)
> +            virBufferAsprintf(&hostbuf, "%s,[%s]", name, ipstr);
> +        else if (!name && id)
> +            virBufferAsprintf(&hostbuf, "id:%s,[%s]", id, ipstr);
>      } else if (name && mac) {
> -        if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s,%s",
> -                        mac, ipstr, name) < 0)
> -            goto error;
> +        virBufferAsprintf(&hostbuf, "%s,%s,%s", mac, ipstr, name);
>      } else if (name && !mac) {
> -        if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s",
> -                        name, ipstr) < 0)
> -            goto error;
> +        virBufferAsprintf(&hostbuf, "%s,%s", name, ipstr);
>      } else {
> -        if (virAsprintf(&hostsfile->hosts[hostsfile->nhosts].host, "%s,%s",
> -                        mac, ipstr) < 0)
> -            goto error;
> +        virBufferAsprintf(&hostbuf, "%s,%s", mac, ipstr);
>      }
> -    VIR_FREE(ipstr);
>
> -    hostsfile->nhosts++;
> +    /* The leasetime string already includes comma if there's any value at all */
> +    virBufferAsprintf(&hostbuf, "%s", leasetime);
>
> -    return 0;
> +    if (!(hostsfile->hosts[hostsfile->nhosts].host = virBufferContentAndReset (&hostbuf)))
> +      goto error;
>
> +    hostsfile->nhosts++;
> +    ret = 0;
>   error:
> +    virBufferFreeAndReset(&hostbuf);
>      VIR_FREE(ipstr);
> -    return -1;
> +    return ret;
>  }
>
>  static dnsmasqHostsfile *
> @@ -391,10 +386,9 @@ hostsfileWrite(const char *path,
>                 dnsmasqDhcpHost *hosts,
>                 unsigned int nhosts)
>  {
> -    char *tmp;
> +    char *tmp, *content = NULL;
>      FILE *f;
>      bool istmp = true;
> -    size_t i;
>      int rc = 0;
>
>      /* even if there are 0 hosts, create a 0 length file, to allow
> @@ -412,17 +406,21 @@ hostsfileWrite(const char *path,
>          }
>      }
>
> -    for (i = 0; i < nhosts; i++) {
> -        if (fputs(hosts[i].host, f) == EOF || fputc('\n', f) == EOF) {
> -            rc = -errno;
> -            VIR_FORCE_FCLOSE(f);
> +    if (!(content = dnsmasqDhcpHostsToString(hosts, nhosts))) {
> +        rc = -ENOMEM;
> +        goto cleanup;
> +    }
>
> -            if (istmp)
> -                unlink(tmp);
> +    if (fputs(content, f) == EOF) {
> +        rc = -errno;
> +        VIR_FORCE_FCLOSE(f);
> +
> +        if (istmp)
> +            unlink(tmp);
> +
> +        goto cleanup;
> +     }
>
> -            goto cleanup;
> -        }
> -    }
>
>      if (VIR_FCLOSE(f) == EOF) {
>          rc = -errno;
> @@ -436,6 +434,7 @@ hostsfileWrite(const char *path,
>      }
>
>   cleanup:
> +    VIR_FREE(content);
>      VIR_FREE(tmp);
>
>      return rc;
> @@ -524,9 +523,10 @@ dnsmasqAddDhcpHost(dnsmasqContext *ctx,
>                     virSocketAddr *ip,
>                     const char *name,
>                     const char *id,
> +                   const char *leasetime,
>                     bool ipv6)
>  {
> -    return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, ipv6);
> +    return hostsfileAdd(ctx->hostsfile, mac, ip, name, id, leasetime, ipv6);
>  }
>
>  /*
> @@ -892,3 +892,31 @@ dnsmasqCapsGet(dnsmasqCapsPtr caps, dnsmasqCapsFlags flag)
>
>      return caps && virBitmapIsBitSet(caps->flags, flag);
>  }
> +
> +/** dnsmasqDhcpHostsToString:
> + *
> + *   Turns a vector of dnsmasqDhcpHost into the string that is ought to be
> + *   stored in the hostsfile, this functionality is split to make hostsfiles
> + *   testable. Returs NULL if nhosts is 0.
> + */
> +char *
> +dnsmasqDhcpHostsToString (dnsmasqDhcpHost *hosts,
> +                          unsigned int nhosts)
> +{
> +    int i;
> +    char *result = NULL;
> +    virBuffer hostsfilebuf = VIR_BUFFER_INITIALIZER;
> +
> +    if (nhosts == 0)
> +       goto cleanup;
> +
> +    for (i = 0; i < nhosts; i++) {
> +        virBufferAsprintf(&hostsfilebuf, "%s\n", hosts[i].host);
> +    }
> +
> +    result = virBufferContentAndReset(&hostsfilebuf);
> +
> +cleanup:
> +    virBufferFreeAndReset(&hostsfilebuf);
> +    return result;
> +}
> diff --git a/src/util/virdnsmasq.h b/src/util/virdnsmasq.h
> index f47bea3ab..1795bc83b 100644
> --- a/src/util/virdnsmasq.h
> +++ b/src/util/virdnsmasq.h
> @@ -88,6 +88,7 @@ int              dnsmasqAddDhcpHost(dnsmasqContext *ctx,
>                                      virSocketAddr *ip,
>                                      const char *name,
>                                      const char *id,
> +                                    const char *leastime,
>                                      bool ipv6);
>  int              dnsmasqAddHost(dnsmasqContext *ctx,
>                                  virSocketAddr *ip,
> @@ -105,6 +106,7 @@ int dnsmasqCapsRefresh(dnsmasqCapsPtr *caps, const char *binaryPath);
>  bool dnsmasqCapsGet(dnsmasqCapsPtr caps, dnsmasqCapsFlags flag);
>  const char *dnsmasqCapsGetBinaryPath(dnsmasqCapsPtr caps);
>  unsigned long dnsmasqCapsGetVersion(dnsmasqCapsPtr caps);
> +char *dnsmasqDhcpHostsToString(dnsmasqDhcpHost *hosts, unsigned int nhosts);
>
>  # define DNSMASQ_DHCPv6_MAJOR_REQD 2
>  # define DNSMASQ_DHCPv6_MINOR_REQD 64
> diff --git a/tests/networkxml2confdata/dhcp6-nat-network.hostsfile b/tests/networkxml2confdata/dhcp6-nat-network.hostsfile
> new file mode 100644
> index 000000000..de659b98c
> --- /dev/null
> +++ b/tests/networkxml2confdata/dhcp6-nat-network.hostsfile
> @@ -0,0 +1,7 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> +id:0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63,[2001:db8:ac10:fd01::1:20]
> +paul,[2001:db8:ac10:fd01::1:21]
> +id:0:3:0:1:0:16:3e:11:22:33,peter.xyz,[2001:db8:ac10:fd01::1:22]
> +id:0:3:0:1:0:16:3e:44:55:33,[2001:db8:ac10:fd01::1:23]
> +id:0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66,badbob,[2001:db8:ac10:fd01::1:24]
> diff --git a/tests/networkxml2confdata/dhcp6-network.hostsfile b/tests/networkxml2confdata/dhcp6-network.hostsfile
> new file mode 100644
> index 000000000..9dfb172ce
> --- /dev/null
> +++ b/tests/networkxml2confdata/dhcp6-network.hostsfile
> @@ -0,0 +1,5 @@
> +id:0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63,[2001:db8:ac10:fd01::1:20]
> +paul,[2001:db8:ac10:fd01::1:21]
> +id:0:3:0:1:0:16:3e:11:22:33,peter.xyz,[2001:db8:ac10:fd01::1:22]
> +id:0:3:0:1:0:16:3e:44:55:33,[2001:db8:ac10:fd01::1:23]
> +id:0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66,badbob,[2001:db8:ac10:fd01::1:24]
> diff --git a/tests/networkxml2confdata/dhcp6host-routed-network.hostsfile b/tests/networkxml2confdata/dhcp6host-routed-network.hostsfile
> new file mode 100644
> index 000000000..de659b98c
> --- /dev/null
> +++ b/tests/networkxml2confdata/dhcp6host-routed-network.hostsfile
> @@ -0,0 +1,7 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> +id:0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63,[2001:db8:ac10:fd01::1:20]
> +paul,[2001:db8:ac10:fd01::1:21]
> +id:0:3:0:1:0:16:3e:11:22:33,peter.xyz,[2001:db8:ac10:fd01::1:22]
> +id:0:3:0:1:0:16:3e:44:55:33,[2001:db8:ac10:fd01::1:23]
> +id:0:1:0:1:18:aa:62:fe:0:16:3e:44:55:66,badbob,[2001:db8:ac10:fd01::1:24]
> diff --git a/tests/networkxml2confdata/leasetime-days.conf b/tests/networkxml2confdata/leasetime-days.conf
> new file mode 100644
> index 000000000..d227be67a
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-days.conf
> @@ -0,0 +1,18 @@
> +##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
> +##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
> +##    virsh net-edit default
> +## or other application using the libvirt API.
> +##
> +## dnsmasq conf file created by libvirt
> +strict-order
> +except-interface=lo
> +bind-dynamic
> +interface=virbr0
> +dhcp-range=192.168.122.2,192.168.122.254,86400
> +dhcp-no-override
> +dhcp-authoritative
> +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,86400
> +dhcp-lease-max=493
> +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
> +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
> +enable-ra
> diff --git a/tests/networkxml2confdata/leasetime-days.xml b/tests/networkxml2confdata/leasetime-days.xml
> new file mode 100644
> index 000000000..b990b4d68
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-days.xml
> @@ -0,0 +1,18 @@
> +<network>
> +  <name>default</name>
> +  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
> +  <forward dev='eth1' mode='nat'/>
> +  <bridge name='virbr0' stp='on' delay='0'/>
> +  <ip address='192.168.122.1' netmask='255.255.255.0'>
> +    <dhcp>
> +      <leasetime unit="days">1</leasetime>
> +      <range start='192.168.122.2' end='192.168.122.254'/>
> +    </dhcp>
> +  </ip>
> +  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
> +    <dhcp>
> +      <leasetime unit="days">1</leasetime>
> +      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
> +    </dhcp>
> +  </ip>
> +</network>
> diff --git a/tests/networkxml2confdata/leasetime-hours.conf b/tests/networkxml2confdata/leasetime-hours.conf
> new file mode 100644
> index 000000000..e5e8c19cd
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-hours.conf
> @@ -0,0 +1,18 @@
> +##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
> +##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
> +##    virsh net-edit default
> +## or other application using the libvirt API.
> +##
> +## dnsmasq conf file created by libvirt
> +strict-order
> +except-interface=lo
> +bind-dynamic
> +interface=virbr0
> +dhcp-range=192.168.122.2,192.168.122.254,3600
> +dhcp-no-override
> +dhcp-authoritative
> +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,3600
> +dhcp-lease-max=493
> +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
> +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
> +enable-ra
> diff --git a/tests/networkxml2confdata/leasetime-hours.xml b/tests/networkxml2confdata/leasetime-hours.xml
> new file mode 100644
> index 000000000..3b9609601
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-hours.xml
> @@ -0,0 +1,18 @@
> +<network>
> +  <name>default</name>
> +  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
> +  <forward dev='eth1' mode='nat'/>
> +  <bridge name='virbr0' stp='on' delay='0'/>
> +  <ip address='192.168.122.1' netmask='255.255.255.0'>
> +    <dhcp>
> +      <leasetime unit="hours">1</leasetime>
> +      <range start='192.168.122.2' end='192.168.122.254'/>
> +    </dhcp>
> +  </ip>
> +  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
> +    <dhcp>
> +      <leasetime unit="hours">1</leasetime>
> +      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
> +    </dhcp>
> +  </ip>
> +</network>
> diff --git a/tests/networkxml2confdata/leasetime-infinite.conf b/tests/networkxml2confdata/leasetime-infinite.conf
> new file mode 100644
> index 000000000..52f4798e5
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-infinite.conf
> @@ -0,0 +1,18 @@
> +##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
> +##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
> +##    virsh net-edit default
> +## or other application using the libvirt API.
> +##
> +## dnsmasq conf file created by libvirt
> +strict-order
> +except-interface=lo
> +bind-dynamic
> +interface=virbr0
> +dhcp-range=192.168.122.2,192.168.122.254,infinite
> +dhcp-no-override
> +dhcp-authoritative
> +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,infinite
> +dhcp-lease-max=493
> +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
> +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
> +enable-ra
> diff --git a/tests/networkxml2confdata/leasetime-infinite.xml b/tests/networkxml2confdata/leasetime-infinite.xml
> new file mode 100644
> index 000000000..d855978a1
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-infinite.xml
> @@ -0,0 +1,18 @@
> +<network>
> +  <name>default</name>
> +  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
> +  <forward dev='eth1' mode='nat'/>
> +  <bridge name='virbr0' stp='on' delay='0'/>
> +  <ip address='192.168.122.1' netmask='255.255.255.0'>
> +    <dhcp>
> +      <leasetime>0</leasetime>
> +      <range start='192.168.122.2' end='192.168.122.254'/>
> +    </dhcp>
> +  </ip>
> +  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
> +    <dhcp>
> +      <leasetime>0</leasetime>
> +      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
> +    </dhcp>
> +  </ip>
> +</network>
> diff --git a/tests/networkxml2confdata/leasetime-minutes.conf b/tests/networkxml2confdata/leasetime-minutes.conf
> new file mode 100644
> index 000000000..37da5702f
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-minutes.conf
> @@ -0,0 +1,18 @@
> +##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
> +##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
> +##    virsh net-edit default
> +## or other application using the libvirt API.
> +##
> +## dnsmasq conf file created by libvirt
> +strict-order
> +except-interface=lo
> +bind-dynamic
> +interface=virbr0
> +dhcp-range=192.168.122.2,192.168.122.254,300
> +dhcp-no-override
> +dhcp-authoritative
> +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,300
> +dhcp-lease-max=493
> +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
> +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
> +enable-ra
> diff --git a/tests/networkxml2confdata/leasetime-minutes.xml b/tests/networkxml2confdata/leasetime-minutes.xml
> new file mode 100644
> index 000000000..e7a27afe6
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-minutes.xml
> @@ -0,0 +1,18 @@
> +<network>
> +  <name>default</name>
> +  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
> +  <forward dev='eth1' mode='nat'/>
> +  <bridge name='virbr0' stp='on' delay='0'/>
> +  <ip address='192.168.122.1' netmask='255.255.255.0'>
> +    <dhcp>
> +      <leasetime unit="minutes">5</leasetime>
> +      <range start='192.168.122.2' end='192.168.122.254'/>
> +    </dhcp>
> +  </ip>
> +  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
> +    <dhcp>
> +      <leasetime unit="minutes">5</leasetime>
> +      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
> +    </dhcp>
> +  </ip>
> +</network>
> diff --git a/tests/networkxml2confdata/leasetime-seconds.conf b/tests/networkxml2confdata/leasetime-seconds.conf
> new file mode 100644
> index 000000000..ea0845e21
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-seconds.conf
> @@ -0,0 +1,18 @@
> +##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
> +##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
> +##    virsh net-edit default
> +## or other application using the libvirt API.
> +##
> +## dnsmasq conf file created by libvirt
> +strict-order
> +except-interface=lo
> +bind-dynamic
> +interface=virbr0
> +dhcp-range=192.168.122.2,192.168.122.254,125
> +dhcp-no-override
> +dhcp-authoritative
> +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,125
> +dhcp-lease-max=493
> +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
> +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
> +enable-ra
> diff --git a/tests/networkxml2confdata/leasetime-seconds.xml b/tests/networkxml2confdata/leasetime-seconds.xml
> new file mode 100644
> index 000000000..56b07f8ae
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime-seconds.xml
> @@ -0,0 +1,18 @@
> +<network>
> +  <name>default</name>
> +  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
> +  <forward dev='eth1' mode='nat'/>
> +  <bridge name='virbr0' stp='on' delay='0'/>
> +  <ip address='192.168.122.1' netmask='255.255.255.0'>
> +    <dhcp>
> +      <leasetime unit="seconds">125</leasetime>
> +      <range start='192.168.122.2' end='192.168.122.254'/>
> +    </dhcp>
> +  </ip>
> +  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
> +    <dhcp>
> +      <leasetime unit="seconds">125</leasetime>
> +      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
> +    </dhcp>
> +  </ip>
> +</network>
> diff --git a/tests/networkxml2confdata/leasetime.conf b/tests/networkxml2confdata/leasetime.conf
> new file mode 100644
> index 000000000..b754c3ffb
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime.conf
> @@ -0,0 +1,18 @@
> +##WARNING:  THIS IS AN AUTO-GENERATED FILE. CHANGES TO IT ARE LIKELY TO BE
> +##OVERWRITTEN AND LOST.  Changes to this configuration should be made using:
> +##    virsh net-edit default
> +## or other application using the libvirt API.
> +##
> +## dnsmasq conf file created by libvirt
> +strict-order
> +except-interface=lo
> +bind-dynamic
> +interface=virbr0
> +dhcp-range=192.168.122.2,192.168.122.254,122
> +dhcp-no-override
> +dhcp-authoritative
> +dhcp-range=2001:db8:ac10:fd01::1:10,2001:db8:ac10:fd01::1:ff,64,121
> +dhcp-lease-max=493
> +dhcp-hostsfile=/var/lib/libvirt/dnsmasq/default.hostsfile
> +addn-hosts=/var/lib/libvirt/dnsmasq/default.addnhosts
> +enable-ra
> diff --git a/tests/networkxml2confdata/leasetime.xml b/tests/networkxml2confdata/leasetime.xml
> new file mode 100644
> index 000000000..fdbb15fc0
> --- /dev/null
> +++ b/tests/networkxml2confdata/leasetime.xml
> @@ -0,0 +1,18 @@
> +<network>
> +  <name>default</name>
> +  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
> +  <forward dev='eth1' mode='nat'/>
> +  <bridge name='virbr0' stp='on' delay='0'/>
> +  <ip address='192.168.122.1' netmask='255.255.255.0'>
> +    <dhcp>
> +      <leasetime>122</leasetime>
> +      <range start='192.168.122.2' end='192.168.122.254'/>
> +    </dhcp>
> +  </ip>
> +  <ip family='ipv6' address='2001:db8:ac10:fd01::1' prefix='64'>
> +    <dhcp>
> +      <leasetime>121</leasetime>
> +      <range start='2001:db8:ac10:fd01::1:10' end='2001:db8:ac10:fd01::1:ff'/>
> +    </dhcp>
> +  </ip>
> +</network>
> diff --git a/tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile b/tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile
> new file mode 100644
> index 000000000..deb3f00ac
> --- /dev/null
> +++ b/tests/networkxml2confdata/nat-network-dns-srv-record-minimal.hostsfile
> @@ -0,0 +1,2 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> diff --git a/tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile b/tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile
> new file mode 100644
> index 000000000..deb3f00ac
> --- /dev/null
> +++ b/tests/networkxml2confdata/nat-network-dns-srv-record.hostsfile
> @@ -0,0 +1,2 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> diff --git a/tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile b/tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile
> new file mode 100644
> index 000000000..deb3f00ac
> --- /dev/null
> +++ b/tests/networkxml2confdata/nat-network-dns-txt-record.hostsfile
> @@ -0,0 +1,2 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> diff --git a/tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile b/tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile
> new file mode 100644
> index 000000000..deb3f00ac
> --- /dev/null
> +++ b/tests/networkxml2confdata/nat-network-name-with-quotes.hostsfile
> @@ -0,0 +1,2 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> diff --git a/tests/networkxml2confdata/nat-network.hostsfile b/tests/networkxml2confdata/nat-network.hostsfile
> new file mode 100644
> index 000000000..deb3f00ac
> --- /dev/null
> +++ b/tests/networkxml2confdata/nat-network.hostsfile
> @@ -0,0 +1,2 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> diff --git a/tests/networkxml2confdata/ptr-domains-auto.hostsfile b/tests/networkxml2confdata/ptr-domains-auto.hostsfile
> new file mode 100644
> index 000000000..deb3f00ac
> --- /dev/null
> +++ b/tests/networkxml2confdata/ptr-domains-auto.hostsfile
> @@ -0,0 +1,2 @@
> +00:16:3e:77:e2:ed,192.168.122.10,a.example.com
> +00:16:3e:3e:a9:1a,192.168.122.11,b.example.com
> diff --git a/tests/networkxml2conftest.c b/tests/networkxml2conftest.c
> index ab3c13aa0..15f783fb1 100644
> --- a/tests/networkxml2conftest.c
> +++ b/tests/networkxml2conftest.c
> @@ -19,9 +19,13 @@
>  #define VIR_FROM_THIS VIR_FROM_NONE
>
>  static int
> -testCompareXMLToConfFiles(const char *inxml, const char *outconf, dnsmasqCapsPtr caps)
> +testCompareXMLToConfFiles(const char *inxml,
> +                          const char *outconf,
> +                          const char *outhostsfile,
> +                          dnsmasqCapsPtr caps)
>  {
> -    char *actual = NULL;
> +    char *actualconf = NULL;
> +    char *actualhosts = NULL;
>      int ret = -1;
>      virNetworkDefPtr dev = NULL;
>      virNetworkObjPtr obj = NULL;
> @@ -41,7 +45,11 @@ testCompareXMLToConfFiles(const char *inxml, const char *outconf, dnsmasqCapsPtr
>      if (dctx == NULL)
>          goto fail;
>
> -    if (networkDnsmasqConfContents(obj, pidfile, &actual, dctx, caps) < 0)
> +    if (networkDnsmasqConfContents(obj, pidfile, &actualconf, &actualhosts,
> +                        dctx, caps) < 0)
> +        goto fail;
> +
> +    if (virTestCompareToFile(actualconf, outconf) < 0)
>          goto fail;
>
>      /* Any changes to this function ^^ should be reflected here too. */
> @@ -57,13 +65,27 @@ testCompareXMLToConfFiles(const char *inxml, const char *outconf, dnsmasqCapsPtr
>      tmp = NULL;
>  #endif
>
> -    if (virTestCompareToFile(actual, outconf) < 0)
> +    if (virFileExists(outhostsfile)) {
> +        if (!actualhosts) {
> +            fprintf(stderr,
> +                    "%s: hostsfile exists but the configuration did not specify any host",
> +                    outhostsfile);
> +            goto fail;
> +        } else if (virTestCompareToFile(actualhosts, outhostsfile) < 0) {
> +            goto fail;
> +        }
> +    } else if (actualhosts) {
> +        fprintf(stderr,
> +                "%s: file does not exist but actual data was expected",
> +                outhostsfile);
>          goto fail;
> +    }
>
>      ret = 0;
>
>   fail:
> -    VIR_FREE(actual);
> +    VIR_FREE(actualconf);
> +    VIR_FREE(actualhosts);
>      VIR_FREE(pidfile);
>      virCommandFree(cmd);
>      virObjectUnref(obj);
> @@ -83,19 +105,23 @@ testCompareXMLToConfHelper(const void *data)
>      const testInfo *info = data;
>      char *inxml = NULL;
>      char *outconf = NULL;
> +    char *outhostsfile = NULL;
>
>      if (virAsprintf(&inxml, "%s/networkxml2confdata/%s.xml",
>                      abs_srcdir, info->name) < 0 ||
>          virAsprintf(&outconf, "%s/networkxml2confdata/%s.conf",
> +                    abs_srcdir, info->name) < 0 ||
> +        virAsprintf(&outhostsfile, "%s/networkxml2confdata/%s.hostsfile",
>                      abs_srcdir, info->name) < 0) {
>          goto cleanup;
>      }
>
> -    result = testCompareXMLToConfFiles(inxml, outconf, info->caps);
> +    result = testCompareXMLToConfFiles(inxml, outconf, outhostsfile, info->caps);
>
>   cleanup:
>      VIR_FREE(inxml);
>      VIR_FREE(outconf);
> +    VIR_FREE(outhostsfile);
>
>      return result;
>  }
> @@ -143,6 +169,13 @@ mymain(void)
>      DO_TEST("dhcp6-nat-network", dhcpv6);
>      DO_TEST("dhcp6host-routed-network", dhcpv6);
>      DO_TEST("ptr-domains-auto", dhcpv6);
> +    DO_TEST("leasetime", dhcpv6);
> +    DO_TEST("leasetime-seconds", dhcpv6);
> +    DO_TEST("leasetime-hours", dhcpv6);
> +    DO_TEST("leasetime-minutes", dhcpv6);
> +    DO_TEST("leasetime-hours", dhcpv6);
> +    DO_TEST("leasetime-days", dhcpv6);
> +    DO_TEST("leasetime-infinite", dhcpv6);
>
>      virObjectUnref(dhcpv6);
>      virObjectUnref(full);
> --
> 2.13.0
>



-- 
Cheers,
Alberto Ruiz

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list
Re: [libvirt] [PATCH] leasetime support for <dhcp>
Posted by Cole Robinson 6 years, 9 months ago
Sorry this keeps falling off the radar...

When posting a v2/v3/vX, please use [PATCH v2] prefix, and make each new
posting a top level patch

On 06/22/2017 08:44 PM, aruiz@gnome.org wrote:
> From: Alberto Ruiz <aruiz@gnome.org>
> 
> Fixes #913446
> 

Full bug link makes things slightly easier if the reviewer wants to look

In the commit message [lease at least give an example of the added XML values,
possibly what dnsmasq value it maps to. Makes grepping commit logs easier

> This patch addresses a few problems found by the initial reviews:
> 
> * leaseTimeUnit RNG type renamed to timeUnit
> * virNetworkDHCPDefGetLeaseTime() renamed to virNetworkDHCPLeaseTimeParseXML()
> * consistent use of braces in if-else-if
> * use %lu instead of PRId64
> * use 0 as infinite lease
> * add a leasetime_defined field to struct _virNetworkIPDef to describe whether the value was set in the xml configuration or not
> * use uint32_t for the leasetime instead of int64_t
> * fail on all invalid leasetime values
> * squash all patches into one


These types of bits should go after the --- break below: they don't need to be
in the commit message but they are useful for reviewers

There's lots of style issues: please run 'make syntax-check' and fix the
warnings. Peter pointed out some of them already against your 6/21 posting but
they weren't addressed in this version:

https://www.redhat.com/archives/libvir-list/2017-June/msg00838.html

Drop the strcmp usage, we have a STREQ macro we use ('make syntax-check' might
warn but I'm not positive)

CC me on the v3 and I'll do a full review

Thanks,
Cole

--
libvir-list mailing list
libvir-list@redhat.com
https://www.redhat.com/mailman/listinfo/libvir-list