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