[libvirt] [PATCH] network: Add support for configuring dhcp lease time

Nehal J Wani nehaljw.kkd1 at gmail.com
Fri Sep 23 06:49:49 UTC 2016


The default dhcp lease time set by dnsmasq is only one hour, which can be
pretty small for developers relying on ip address(es) to be consistent
across reboots.

This patch adds support for setting a lease time in the network definition.
For now, all IP ranges under one <dhcp> will share a common leasetime.

An example:
<dhcp>
  <leasetime units='hours'>12</leasetime>
</dhcp>

The value for attribute 'units' can be seconds/hours/days/weeks/s/h/d/w.
If the leasetime specified is -1, it is considered to be infinite.

docs/schema/{basictypes,network}.rng
  * Add datatype long
  * Introduce timeUnits, scaledTime
  * Add schema for tag: leasetime

src/util/virutil.{c,h}
  * Introduce virScaleTime()

src/conf/network_conf.h
  * Extend virNetworkIPDef to accomodate leastime
  * Define VIR_NETWORK_DHCP_LEASE_TIME_MAX

src/conf/network_conf.c
  * Introduce virNetworkDHCPLeaseTimeParseXML
  * Modify virNetworkDHCPDefParseXML to have XPath context pointer

tests/*
  * Add test cases for xml parsing and conversion

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=913446
---
 docs/schemas/basictypes.rng                        | 20 +++++++
 docs/schemas/network.rng                           | 10 +++-
 src/conf/network_conf.c                            | 67 ++++++++++++++++++++-
 src/conf/network_conf.h                            | 11 ++++
 src/network/bridge_driver.c                        |  8 +++
 src/util/virutil.c                                 | 70 ++++++++++++++++++++++
 src/util/virutil.h                                 |  3 +
 .../isolated-network-with-lease-time.conf          | 17 ++++++
 .../isolated-network-with-lease-time.xml           | 24 ++++++++
 tests/networkxml2conftest.c                        |  1 +
 .../isolated-network-with-lease-time.xml           | 24 ++++++++
 .../isolated-network-with-lease-time.xml           | 24 ++++++++
 tests/networkxml2xmltest.c                         |  1 +
 13 files changed, 276 insertions(+), 4 deletions(-)
 create mode 100644 tests/networkxml2confdata/isolated-network-with-lease-time.conf
 create mode 100644 tests/networkxml2confdata/isolated-network-with-lease-time.xml
 create mode 100644 tests/networkxml2xmlin/isolated-network-with-lease-time.xml
 create mode 100644 tests/networkxml2xmlout/isolated-network-with-lease-time.xml

diff --git a/docs/schemas/basictypes.rng b/docs/schemas/basictypes.rng
index 1b4f980..023edfb 100644
--- a/docs/schemas/basictypes.rng
+++ b/docs/schemas/basictypes.rng
@@ -13,6 +13,12 @@
       <param name='pattern'>[0-9]+</param>
     </data>
   </define>
+  <!-- Our long doesn"t allow a leading "+" in its lexical form -->
+  <define name='long'>
+    <data type='long'>
+      <param name='pattern'>-?[0-9]+</param>
+    </data>
+  </define>
 
   <define name='hexuint'>
     <data type='string'>
@@ -283,6 +289,20 @@
     <ref name='unsignedLong'/>
   </define>
 
+  <define name='timeUnit'>
+    <data type='string'>
+      <param name='pattern'>(seconds|minutes|hours|days|weeks|s|m|h|d|w)</param>
+    </data>
+  </define>
+  <define name='scaledTime'>
+    <optional>
+      <attribute name='timeUnit'>
+        <ref name='timeUnit'/>
+      </attribute>
+    </optional>
+    <ref name='long'/>
+  </define>
+
   <define name="pciDomain">
     <ref name="uint16"/>
   </define>
diff --git a/docs/schemas/network.rng b/docs/schemas/network.rng
index 1a18e64..66b65bc 100644
--- a/docs/schemas/network.rng
+++ b/docs/schemas/network.rng
@@ -337,9 +337,15 @@
               </element>
             </optional>
             <optional>
-              <!-- Define the range(s) of IP addresses that the DHCP
-                   server should hand out -->
               <element name="dhcp">
+                <optional>
+                  <element name="leasetime">
+                    <ref name="scaledTime"/>
+                    <attribute name="unit"><ref name="timeUnit"/></attribute>
+                  </element>
+                </optional>
+                <!-- Define the range(s) of IP addresses that the DHCP
+                     server should hand out -->
                 <zeroOrMore>
                   <element name="range">
                     <attribute name="start"><ref name="ipAddr"/></attribute>
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index aa39776..d2372f2 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -1032,21 +1032,76 @@ virNetworkDHCPHostDefParseXML(const char *networkName,
 }
 
 static int
+virNetworkDHCPLeaseTimeParseXML(const char *networkName,
+                                virNetworkIPDefPtr def,
+                                xmlXPathContextPtr ctxt,
+                                xmlNodePtr node)
+{
+    int ret = -1;
+    int scale = 1;
+    xmlNodePtr save;
+    char *unit = NULL;
+    int leasetimeRV = 0;
+    long long leasetime;
+
+    save = ctxt->node;
+    ctxt->node = node;
+
+    leasetimeRV = virXPathLongLong("string(./text())", ctxt, &leasetime);
+    if (leasetimeRV == -2) {
+        virReportError(VIR_ERR_XML_ERROR,
+                       _("Invalid long long value specified for leasetime "
+                         "in definition of network '%s'"),
+                       networkName);
+        goto cleanup;
+    } else {
+        if (leasetime < 0) {
+            leasetime = -1;       /* infinite */
+        } else {
+            unit = virXPathString("string(./@unit)", ctxt);
+            if (virScaleTime(&leasetime, unit, scale,
+                             VIR_NETWORK_DHCP_LEASE_TIME_MAX) < 0) {
+                // let virScaleTime() report the appropriate error
+                goto cleanup;
+            }
+        }
+    }
+
+    def->leasetime = leasetime;
+    ret = 0;
+
+ cleanup:
+    VIR_FREE(unit);
+    ctxt->node = save;
+    return ret;
+}
+
+static int
 virNetworkDHCPDefParseXML(const char *networkName,
                           xmlNodePtr node,
+                          xmlXPathContextPtr ctxt,
                           virNetworkIPDefPtr def)
 {
     int ret = -1;
-    xmlNodePtr cur;
+    xmlNodePtr cur, save;
     virSocketAddrRange range;
     virNetworkDHCPHostDef host;
 
     memset(&range, 0, sizeof(range));
     memset(&host, 0, sizeof(host));
 
+    save = ctxt->node;
+    ctxt->node = node;
+
     cur = node->children;
     while (cur != NULL) {
         if (cur->type == XML_ELEMENT_NODE &&
+            xmlStrEqual(cur->name, BAD_CAST "leasetime")) {
+
+            if (virNetworkDHCPLeaseTimeParseXML(networkName, def, ctxt, cur) < 0)
+                goto cleanup;
+
+        } else if (cur->type == XML_ELEMENT_NODE &&
             xmlStrEqual(cur->name, BAD_CAST "range")) {
 
             if (virSocketAddrRangeParseXML(networkName, def, cur, &range) < 0)
@@ -1095,6 +1150,7 @@ virNetworkDHCPDefParseXML(const char *networkName,
     ret = 0;
  cleanup:
     virNetworkDHCPHostDefClear(&host);
+    ctxt->node = save;
     return ret;
 }
 
@@ -1607,7 +1663,7 @@ virNetworkIPDefParseXML(const char *networkName,
     while (cur != NULL) {
         if (cur->type == XML_ELEMENT_NODE &&
             xmlStrEqual(cur->name, BAD_CAST "dhcp")) {
-            if (virNetworkDHCPDefParseXML(networkName, cur, def) < 0)
+            if (virNetworkDHCPDefParseXML(networkName, cur, ctxt, def) < 0)
                 goto cleanup;
         } else if (cur->type == XML_ELEMENT_NODE &&
                    xmlStrEqual(cur->name, BAD_CAST "tftp")) {
@@ -2675,6 +2731,13 @@ virNetworkIPDefFormat(virBufferPtr buf,
         virBufferAddLit(buf, "<dhcp>\n");
         virBufferAdjustIndent(buf, 2);
 
+        if (def->leasetime) {
+            virBufferAddLit(buf, "<leasetime");
+            virBufferAsprintf(buf, " unit='seconds'>%lld",
+                              def->leasetime);
+            virBufferAddLit(buf, "</leasetime>\n");
+        }
+
         for (i = 0; i < def->nranges; i++) {
             char *saddr = virSocketAddrFormat(&def->ranges[i].start);
             if (!saddr)
diff --git a/src/conf/network_conf.h b/src/conf/network_conf.h
index 3b227db..965fd3b 100644
--- a/src/conf/network_conf.h
+++ b/src/conf/network_conf.h
@@ -26,6 +26,15 @@
 
 # define DNS_RECORD_LENGTH_SRV  (512 - 30)  /* Limit minus overhead as mentioned in RFC-2782 */
 
+/**
+ * VIR_NETWORK_DHCP_LEASE_TIME_MAX:
+ *
+ * Macro providing the upper limit on DHCP Lease Time (in seconds).
+ * Libvirt supports only dnsmasq as of now, and it stores the lease
+ * time in an unsigned int.
+ */
+# define VIR_NETWORK_DHCP_LEASE_TIME_MAX UINT_MAX
+
 # include <libxml/parser.h>
 # include <libxml/tree.h>
 # include <libxml/xpath.h>
@@ -167,6 +176,8 @@ struct _virNetworkIPDef {
     size_t nhosts;              /* Zero or more dhcp hosts */
     virNetworkDHCPHostDefPtr hosts;
 
+    long long leasetime;        /* DHCP lease time for all ranges */
+
     char *tftproot;
     char *bootfile;
     virSocketAddr bootserver;
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index 7b99aca..fa628d3 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -1222,6 +1222,14 @@ networkDnsmasqConfContents(virNetworkObjPtr network,
                               saddr, eaddr);
             if (VIR_SOCKET_ADDR_IS_FAMILY(&ipdef->address, AF_INET6))
                 virBufferAsprintf(&configbuf, ",%d", prefix);
+
+            if (ipdef->leasetime) {
+                if (ipdef->leasetime == -1)
+                    virBufferAddLit(&configbuf, ",infinite");
+                else
+                    virBufferAsprintf(&configbuf, ",%lld", ipdef->leasetime);
+            }
+
             virBufferAddLit(&configbuf, "\n");
 
             VIR_FREE(saddr);
diff --git a/src/util/virutil.c b/src/util/virutil.c
index b57a195..81a60e6 100644
--- a/src/util/virutil.c
+++ b/src/util/virutil.c
@@ -275,6 +275,76 @@ virHexToBin(unsigned char c)
     }
 }
 
+/**
+ * virScaleTime:
+ * @value: pointer to the integer which is supposed to hold value
+ * @unit: pointer to the string holding the unit
+ * @scale: integer holding the value of scale
+ * @limit: upper limit on scaled value
+ *
+ * Scale an integer @value in-place by an optional case-insensitive @unit,
+ * defaulting to @scale if @unit is NULL or empty. Recognized units include
+ * (w)eeks, (d)ays, (h)ours, (m)inutes and (s)econds. Ensure that the result
+ * does not exceed @limit.  Return 0 on success, -1 with error message raised
+ * on failure.
+ */
+int
+virScaleTime(long long *value, const char *unit,
+             long long scale, long long limit)
+{
+    size_t i;
+    static char const* const allowed_units[] = {"s", "m", "h", "d", "w",
+        "seconds", "minutes", "hours", "days", "weeks"};
+
+    size_t n_allowed_units = ARRAY_CARDINALITY(allowed_units);
+
+    if (!unit || !*unit) {
+        if (!scale) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Invalid scale %lld"), scale);
+            return -1;
+        }
+        unit = "";
+    } else {
+        for (i = 0; i < n_allowed_units; i++) {
+            if (STREQ(unit, allowed_units[i])) {
+                switch (*unit) {
+                case 'w':
+                    scale *= 7;
+                    /* fall through */
+                case 'd':
+                    scale *= 24;
+                    /* fall through */
+                case 'h':
+                    scale *= 60;
+                    /* fall through */
+                case 'm':
+                    scale *= 60;
+                    /* fall through */
+                case 's':
+                    break;
+                }
+                break;
+            }
+        }
+        if (i == n_allowed_units) {
+            virReportError(VIR_ERR_INVALID_ARG,
+                           _("Unknown time unit '%s'"), unit);
+            return -1;
+        }
+    }
+
+    if (*value && *value > (limit / scale)) {
+        virReportError(VIR_ERR_OVERFLOW, _("Value too large: %lld %s"),
+                       *value, unit);
+        return -1;
+    }
+
+    *value *= scale;
+    return 0;
+}
+
+
 /* Scale an integer VALUE in-place by an optional case-insensitive
  * SUFFIX, defaulting to SCALE if suffix is NULL or empty (scale is
  * typically 1 or 1024).  Recognized suffixes include 'b' or 'bytes',
diff --git a/src/util/virutil.h b/src/util/virutil.h
index 703ec53..5c11f77 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -51,6 +51,9 @@ int virSetUIDGIDWithCaps(uid_t uid, gid_t gid, gid_t *groups, int ngroups,
                          unsigned long long capBits,
                          bool clearExistingCaps);
 
+int virScaleTime(long long *value, const char *unit,
+                 long long scale, long long limit);
+
 int virScaleInteger(unsigned long long *value, const char *suffix,
                     unsigned long long scale, unsigned long long limit)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
diff --git a/tests/networkxml2confdata/isolated-network-with-lease-time.conf b/tests/networkxml2confdata/isolated-network-with-lease-time.conf
new file mode 100644
index 0000000..cd353f5
--- /dev/null
+++ b/tests/networkxml2confdata/isolated-network-with-lease-time.conf
@@ -0,0 +1,17 @@
+##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 private
+## or other application using the libvirt API.
+##
+## dnsmasq conf file created by libvirt
+strict-order
+except-interface=lo
+bind-dynamic
+interface=virbr1
+dhcp-range=192.168.123.2,192.168.123.254,infinite
+dhcp-no-override
+dhcp-range=2001:db8:ca2:2:1::10,2001:db8:ca2:2:1::ff,64,108000
+dhcp-lease-max=493
+dhcp-hostsfile=/var/lib/libvirt/dnsmasq/private.hostsfile
+addn-hosts=/var/lib/libvirt/dnsmasq/private.addnhosts
+enable-ra
diff --git a/tests/networkxml2confdata/isolated-network-with-lease-time.xml b/tests/networkxml2confdata/isolated-network-with-lease-time.xml
new file mode 100644
index 0000000..be8ea56
--- /dev/null
+++ b/tests/networkxml2confdata/isolated-network-with-lease-time.xml
@@ -0,0 +1,24 @@
+<network>
+  <name>private</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward mode='nat'>
+   <nat>
+     <port start='1024' end='65535'/>
+   </nat>
+  </forward>
+  <bridge name='virbr1' stp='on' delay='0'/>
+  <mac address='52:54:00:10:2f:05'/>
+  <ip address='192.168.123.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime unit="minutes">-1</leasetime>
+      <range start='192.168.123.2' end='192.168.123.254'/>
+    </dhcp>
+  </ip>
+  <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" >
+    <dhcp>
+      <leasetime unit="h">30</leasetime>
+      <range start="2001:db8:ca2:2:1::10" end="2001:db8:ca2:2:1::ff" />
+      <host id="0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63" name="nwani" ip="2001:db8:ca2:2:3::4" />
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2conftest.c b/tests/networkxml2conftest.c
index 65a0e32..501954d 100644
--- a/tests/networkxml2conftest.c
+++ b/tests/networkxml2conftest.c
@@ -112,6 +112,7 @@ mymain(void)
     } while (0)
 
     DO_TEST("isolated-network", restricted);
+    DO_TEST("isolated-network-with-lease-time", dhcpv6);
     DO_TEST("netboot-network", restricted);
     DO_TEST("netboot-proxy-network", restricted);
     DO_TEST("nat-network-dns-srv-record-minimal", restricted);
diff --git a/tests/networkxml2xmlin/isolated-network-with-lease-time.xml b/tests/networkxml2xmlin/isolated-network-with-lease-time.xml
new file mode 100644
index 0000000..75c9f22
--- /dev/null
+++ b/tests/networkxml2xmlin/isolated-network-with-lease-time.xml
@@ -0,0 +1,24 @@
+<network>
+  <name>private</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward mode='nat'>
+   <nat>
+     <port start='1024' end='65535'/>
+   </nat>
+  </forward>
+  <bridge name='virbr1' stp='on' delay='0'/>
+  <mac address='52:54:00:10:2f:05'/>
+  <ip address='192.168.123.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime unit="minutes">-1</leasetime>
+      <range start='192.168.123.2' end='192.168.123.254'/>
+    </dhcp>
+  </ip>
+  <ip family="ipv6" address="2001:db8:ca2:2::1" prefix="64" >
+    <dhcp>
+      <leasetime unit="d">30</leasetime>
+      <range start="2001:db8:ca2:2:1::10" end="2001:db8:ca2:2:1::ff" />
+      <host id="0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63" name="nwani" ip="2001:db8:ca2:2:3::4" />
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2xmlout/isolated-network-with-lease-time.xml b/tests/networkxml2xmlout/isolated-network-with-lease-time.xml
new file mode 100644
index 0000000..d9d6ff4
--- /dev/null
+++ b/tests/networkxml2xmlout/isolated-network-with-lease-time.xml
@@ -0,0 +1,24 @@
+<network>
+  <name>private</name>
+  <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+  <forward mode='nat'>
+    <nat>
+      <port start='1024' end='65535'/>
+    </nat>
+  </forward>
+  <bridge name='virbr1' stp='on' delay='0'/>
+  <mac address='52:54:00:10:2f:05'/>
+  <ip address='192.168.123.1' netmask='255.255.255.0'>
+    <dhcp>
+      <leasetime unit='seconds'>-1</leasetime>
+      <range start='192.168.123.2' end='192.168.123.254'/>
+    </dhcp>
+  </ip>
+  <ip family='ipv6' address='2001:db8:ca2:2::1' prefix='64'>
+    <dhcp>
+      <leasetime unit='seconds'>2592000</leasetime>
+      <range start='2001:db8:ca2:2:1::10' end='2001:db8:ca2:2:1::ff'/>
+      <host id='0:4:7e:7d:f0:7d:a8:bc:c5:d2:13:32:11:ed:16:ea:84:63' name='nwani' ip='2001:db8:ca2:2:3::4'/>
+    </dhcp>
+  </ip>
+</network>
diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c
index 01cd6f7..665f695 100644
--- a/tests/networkxml2xmltest.c
+++ b/tests/networkxml2xmltest.c
@@ -126,6 +126,7 @@ mymain(void)
     DO_TEST("dhcp6host-routed-network");
     DO_TEST("empty-allow-ipv6");
     DO_TEST("isolated-network");
+    DO_TEST("isolated-network-with-lease-time");
     DO_TEST("routed-network");
     DO_TEST("routed-network-no-dns");
     DO_TEST_PARSE_ERROR("routed-network-no-dns-extra-elements");
-- 
2.7.4




More information about the libvir-list mailing list