--- a/README Tue Feb 24 18:06:32 2015 +0200
+++ b/README Tue Feb 24 18:22:07 2015 +0200
@@ -25,3 +25,26 @@
1.0.0 PTR foo.test.
2.0.0 PTR bar.test.
+=== Generated hosts ===
+The hosts file format supports something similar to bind9's $GENERATE directive for hosts:
+
+ [asdf{1-3}]
+ ip = 10.100.100.$
+
+ $ bin/pvl.hosts-dns --forward-zone=asdf etc/hosts/asdf
+ asdf1@asdf A 10.100.100.1
+ asdf2@asdf A 10.100.100.2
+ asdf3@asdf A 10.100.100.3
+
+Note that the generate directives are interpreted and compiled directly by pvl.hosts.
+
+Most of the $GENERATE options should be supported, with a little clever hackery:
+
+ [asdf{1-5/2}{0,2}]
+ ip = 10.100.100.$${10}
+
+ $ bin/pvl.hosts-dns --forward-zone=asdf2 etc/hosts/asdf2
+ asdf01@asdf2 A 10.100.100.11
+ asdf03@asdf2 A 10.100.100.13
+ asdf05@asdf2 A 10.100.100.15
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/etc/hosts/asdf Tue Feb 24 18:22:07 2015 +0200
@@ -0,0 +1,2 @@
+[asdf{1-3}]
+ ip = 10.100.100.$
--- a/pvl/hosts/config.py Tue Feb 24 18:06:32 2015 +0200
+++ b/pvl/hosts/config.py Tue Feb 24 18:22:07 2015 +0200
@@ -50,7 +50,7 @@
def __str__ (self):
return "{self.config}:{self.error.line_number}: {self.error.message}".format(self=self)
-def apply_host_expand (options, range, name, domain, **params):
+def apply_host_expand (options, path, range, name, domain, **params):
"""
Expand a templated host item.
"""
@@ -61,11 +61,13 @@
params = {param: pvl.dns.zone.parse_generate_field(value) for param, value in params.iteritems()}
for i in range:
- log.info("%s: %s[%d]@%s", path, name, i, domain)
-
- yield Host.build(host(i), domain,
+ host = Host.build(name(i), domain,
**{param: value(i) for param, value in params.iteritems()}
)
+
+ log.info("%s: [%d] %s", path, i, host)
+
+ yield host
def parse_expand(name):
"""
@@ -77,11 +79,14 @@
"""
if '{' in name:
+ # consume the first {...} token as the range
pre, name = name.split('{', 1)
- range, post = name.rsplit('}', 1)
+ range, post = name.split('}', 1)
+
+ # if there's a second {...} token, it will be re-composed into ${...}
+ name = pre + "$" + post
range = pvl.dns.zone.parse_generate_range(range)
- name = pre + "$" + post
else:
range = None
@@ -121,12 +126,14 @@
name, range = parse_expand(name)
if range:
- for host in apply_host_expand(options, range, name, domain, **params):
+ for host in apply_host_expand(options, path, range, name, domain, **params):
yield host
else:
- log.info("%s: %s@%s", path, name, domain)
+ host = Host.build(name, domain, **params)
+
+ log.info("%s: %s", path, host)
- yield Host.build(name, domain, **params)
+ yield host
def apply_host_includes (options, config_path, include):
"""
--- a/pvl/hosts/tests.py Tue Feb 24 18:06:32 2015 +0200
+++ b/pvl/hosts/tests.py Tue Feb 24 18:22:07 2015 +0200
@@ -9,7 +9,7 @@
hosts_domain = None
hosts_include = None
-class NamedStringIO(StringIO):
+class ConfFile(StringIO):
def __init__(self, name, buffer):
StringIO.__init__(self, buffer)
self.name = name
@@ -18,42 +18,46 @@
def setUp(self):
self.options = Options()
- def testApply(self):
- expected = [
- ('foo', 'test', ipaddr.IPAddress('127.0.0.1')),
- ('bar', 'test', ipaddr.IPAddress('127.0.0.2')),
- ]
+ def assertHostsEqual(self, hosts, expected):
+ for host, expect in zip(hosts, expected):
+ host_str, attrs = expect
- for expect, host in zip(expected, config.apply(self.options, ['etc/hosts/test'])):
- hostname, domain, ip = expect
+ self.assertEquals(str(host), host_str)
- self.assertEquals(str(host), hostname)
- self.assertEquals(host.domain, domain)
- self.assertEquals(host.ip, ip)
+ for attr, value in attrs.iteritems():
+ self.assertEquals(getattr(host, attr), value)
+ def testApply(self):
+ self.assertHostsEqual(config.apply(self.options, ['etc/hosts/test']), [
+ ('foo@test', dict(ip=ipaddr.IPAddress('127.0.0.1'))),
+ ('bar@test', dict(ip=ipaddr.IPAddress('127.0.0.2'))),
+ ])
+
def testApplyHostsError(self):
with self.assertRaises(config.HostConfigError):
list(config.apply_hosts(self.options, ['nonexistant']))
def testApplyHosts(self):
- expected = [
- ('foo', 'test', ipaddr.IPAddress('127.0.0.1')),
- ('bar', 'test', ipaddr.IPAddress('127.0.0.2')),
- ]
-
- for expect, host in zip(expected, config.apply_hosts_file(self.options, NamedStringIO('test', """
+ conf_file = ConfFile('test', """
[foo]
ip = 127.0.0.1
[bar]
ip = 127.0.0.2
-"""
- ))):
- hostname, domain, ip = expect
+ """)
+ expected = [
+ ('foo@test', dict(ip=ipaddr.IPAddress('127.0.0.1'))),
+ ('bar@test', dict(ip=ipaddr.IPAddress('127.0.0.2'))),
+ ]
- self.assertEquals(str(host), hostname)
- self.assertEquals(host.domain, domain)
- self.assertEquals(host.ip, ip)
-
+ self.assertHostsEqual(config.apply_hosts_file(self.options, conf_file), expected)
+
+ def testApplyHostsExpand(self):
+ self.assertHostsEqual(config.apply_host_config(self.options, 'etc/hosts/asdf', 'asdf', 'asdf{1-3}', ip='10.100.100.$'), [
+ ('asdf1@asdf', dict(ip=ipaddr.IPAddress('10.100.100.1'))),
+ ('asdf2@asdf', dict(ip=ipaddr.IPAddress('10.100.100.2'))),
+ ('asdf3@asdf', dict(ip=ipaddr.IPAddress('10.100.100.3'))),
+ ])
+
if __name__ == '__main__':
unittest.main()