# HG changeset patch # User Tero Marttila # Date 1424954437 -7200 # Node ID 1e68e3a30b5118d8c0d2cc7b98371ef0ec449c1a # Parent 517c359a683b2db1d9aa109ad17cfb84204f7bf6 pvl.hosts.dhcp: split out of script, refactor using pvl.dhcp.config, and test diff -r 517c359a683b -r 1e68e3a30b51 bin/pvl.hosts-dhcp --- 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) diff -r 517c359a683b -r 1e68e3a30b51 pvl/hosts/dhcp.py --- /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 '' + diff -r 517c359a683b -r 1e68e3a30b51 pvl/hosts/tests.py --- 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()