pvl.hosts.dhcp: split out of script, refactor using pvl.dhcp.config, and test
authorTero Marttila <tero.marttila@aalto.fi>
Thu, 26 Feb 2015 14:40:37 +0200
changeset 479 1e68e3a30b51
parent 478 517c359a683b
child 480 7e44854e85d4
pvl.hosts.dhcp: split out of script, refactor using pvl.dhcp.config, and test
bin/pvl.hosts-dhcp
pvl/hosts/dhcp.py
pvl/hosts/tests.py
--- a/bin/pvl.hosts-dhcp	Thu Feb 26 14:40:14 2015 +0200
+++ b/bin/pvl.hosts-dhcp	Thu Feb 26 14:40:37 2015 +0200
@@ -1,93 +1,28 @@
 #!/usr/bin/env python
 
-import pvl.args
-import pvl.hosts
-
-import configobj
 import logging; log = logging.getLogger('pvl.hosts-dhcp')
 import optparse
-
-def build_host (host_name, *items) :
-    yield "host {host} {{".format(host=host_name)
-    for item in items :
-        if isinstance(item, dict) :
-            for setting, value in item.iteritems() :
-                if value :
-                    yield "\t{setting:30} {value};".format(setting=setting, value=value)
-        else :
-            raise ValueError("Unknown item: %r", item)
-    yield "}"
-
-def dhcp_quote (value) :
-    if value is None :
-        return None
-    else :
-        return '"{value}"'.format(value=value)
-
-def process_host (options, host) :
-    if host.boot :
-        if ':' in host.boot :
-            next_server, filename = host.boot.split(':', 1)
-        elif host.boot.startswith('/') :
-            next_server = None
-            filename = host.boot
-        elif host.boot.endswith(':') :
-            next_server = host.boot
-            filename = None
-        else :
-            log.error("%s: invalid boot: %s", host, host.boot)
-    else :
-        next_server = filename = None
+import pvl.args
+import pvl.hosts
+import pvl.hosts.dhcp
 
-    if set(host.ethernet) == set([None]) :
-        host_fmt = "{host.name}"
-    elif host.ethernet :
-        host_fmt = "{host.name}-{index}"
-    else :
-        # nothing there
-        return
-
-    if host.owner :
-        yield u"# Owner: {host.owner}".format(host=host)
-
-    for index, ethernet in host.ethernet.iteritems() :
-        for line in build_host(host_fmt.format(host=host, index=index),
-                { 'option host-name':   dhcp_quote(host.name) },
-                { 'hardware ethernet':  ethernet },
-                { 'fixed-address':      host.ip },
-                { 'next-server':        next_server },
-                { 'filename':           dhcp_quote(filename) },
-        ) :
-            yield line
-
-    yield ""
-    
-def process_hosts (options, hosts) :
-    for host in hosts :
-        for line in process_host(options, host) :
-            yield line
-
-def apply_conf (options, lines) :
-    for line in lines :
-        print line
-
-def main (argv) :
+def main (argv):
     """
         Generate DHCP host configs from host definitions.
     """
 
     parser = optparse.OptionParser(main.__doc__)
     parser.add_option_group(pvl.args.parser(parser))
-    parser.add_option_group(pvl.hosts.optparser(parser))
-
-    options, args = parser.parse_args(argv[1:])
-    pvl.args.apply(options)
+    parser.add_option_group(pvl.hosts.config.optparser(parser))
 
     # input
+    options, args = pvl.args.parse(parser, argv)
+    
     hosts = pvl.hosts.apply(options, args)
 
     # process
-    apply_conf(options, process_hosts(options, hosts))
+    for line in pvl.hosts.dhcp.apply_hosts_dhcp(options, hosts):
+        print line
 
 if __name__ == '__main__':
     pvl.args.main(main)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pvl/hosts/dhcp.py	Thu Feb 26 14:40:37 2015 +0200
@@ -0,0 +1,80 @@
+import pvl.dhcp.config
+import pvl.hosts.host
+
+class HostDHCPError(pvl.hosts.host.HostError):
+    pass
+
+def dhcp_host_options (host, ethernet):
+    """
+        Yield specific dhcp.conf host { ... } parameters for build_block()
+    """
+
+    yield 'option', 'host-name', host.name
+    yield 'hardware', 'ethernet', ethernet
+
+    if host.ip:
+        yield 'fixed-address', str(host.ip)
+      
+    if not host.boot:
+        next_server = filename = None
+    elif ':' in host.boot :
+        next_server, filename = host.boot.split(':', 1)
+    elif host.boot.startswith('/') :
+        next_server = None
+        filename = host.boot
+    elif host.boot.endswith(':') :
+        next_server = host.boot
+        filename = None
+    else :
+        raise HostError(host, "invalid boot={host.boot}".format(host=host))
+
+    if next_server:
+        yield 'next-server', next_server
+    
+    if filename:
+        yield 'filename', filename
+
+def dhcp_host (host):
+    """
+        Yield (block, items, **opts) tuples for pvl.dhcp.config.build_block().
+    """
+
+    if set(host.ethernet) == set([None]) :
+        host_fmt = "{host.name}"
+    elif host.ethernet :
+        host_fmt = "{host.name}-{index}"
+    else :
+        # nothing to be seen here
+        return
+ 
+    if host.owner :
+        comment = u"Owner: {host.owner}".format(host=host)
+    else:
+        comment = None
+
+    for index, ethernet in host.ethernet.iteritems() :
+        name = host_fmt.format(host=host, index=index)
+
+        yield ('host', name), list(dhcp_host_options(host, ethernet)), dict(comment=comment)
+    
+def apply_hosts_dhcp (options, hosts):
+    """
+        Generate dhcp.conf output lines for the set of hosts.
+
+        Verifies that there are no dupliate hosts.
+    """
+
+    blocks = { }
+
+    for host in hosts:
+        for block, items, opts in dhcp_host(host):
+            if block in blocks:
+                raise HostDHCPError(host, "dupliate host block {block}: {other}".format(block=block, other=blocks[block]))
+
+            blocks[block] = block
+
+            for line in pvl.dhcp.config.build_block(block, items, **opts):
+                yield line
+            
+            yield ''
+
--- a/pvl/hosts/tests.py	Thu Feb 26 14:40:14 2015 +0200
+++ b/pvl/hosts/tests.py	Thu Feb 26 14:40:37 2015 +0200
@@ -436,5 +436,37 @@
             ('6', 'PTR'): ['ufc.domain.'],
         })
 
+class TestDhcp(unittest.TestCase):
+    def setUp(self):
+        self.options = pvl.args.options(
+                hosts_charset   = 'utf-8',
+                hosts_domain    = None,
+                hosts_include   = None,
+        )
+
+    def assertBlocksEqual(self, blockdefs, expected):
+        for (_block, _items, _opts), (block, items, opts) in zip(blockdefs, expected):
+            self.assertEqual(_block, block)
+            self.assertItemsEqual(_items, items)
+
+            if opts is not None:
+                self.assertEqual(_opts, opts)
+        
+        self.assertEqual(len(blockdefs), len(expected))
+    
+    def testHost(self):
+        host = Host.build('foo', 'test',
+                ip          = '192.0.2.1',
+                ethernet    = '00:11:22:33:44:55',
+        )
+
+        self.assertBlocksEqual(list(dhcp.dhcp_host(host)), [
+            (('host', 'foo'), [
+                ('option', 'host-name', "foo"),
+                ('fixed-address', '192.0.2.1'),
+                ('hardware', 'ethernet', '00:11:22:33:44:55'),
+            ], None)
+        ])
+
 if __name__ == '__main__':
     unittest.main()