pvl.hosts: fixup and document host expansion
authorTero Marttila <tero.marttila@aalto.fi>
Tue, 24 Feb 2015 18:22:07 +0200
changeset 447 6f0357759e9b
parent 446 139b3a41b608
child 448 5ab0ec8200c3
pvl.hosts: fixup and document host expansion
README
etc/hosts/asdf
pvl/hosts/config.py
pvl/hosts/tests.py
--- 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()