--- a/pvl/hosts/dhcp.py Mon Mar 09 23:31:13 2015 +0200
+++ b/pvl/hosts/dhcp.py Tue Mar 10 00:11:43 2015 +0200
@@ -1,6 +1,93 @@
import pvl.dhcp.config
import pvl.hosts.host
+def parse_dhcp_boot(boot):
+ """
+ Parse the dhcp boot=... option
+
+ >>> print parse_dhcp_boot(None)
+ {}
+ >>> print parse_dhcp_boot({'filename': '/foo'})
+ {'filename': '/foo'}
+ >>> print parse_dhcp_boot({'filename': '/foo', 'next-server': 'bar'})
+ {'next-server': 'bar', 'filename': '/foo'}
+ >>> print parse_dhcp_boot('/foo')
+ {'filename': '/foo'}
+ >>> print parse_dhcp_boot('bar:/foo')
+ {'next-server': 'bar', 'filename': '/foo'}
+ >>> print parse_dhcp_boot('bar:')
+ {'next-server': 'bar'}
+ >>> print parse_dhcp_boot('foo')
+ Traceback (most recent call last):
+ ...
+ ValueError: invalid boot=foo
+ """
+
+ # unpack dict, or str
+ if not boot:
+ filename = next_server = None
+ boot_str = None
+
+ elif isinstance(boot, dict):
+ filename = boot.pop('filename', None)
+ next_server = boot.pop('next-server', None)
+ boot_str = boot.pop(None, None)
+
+ else:
+ filename = next_server = None
+ boot_str = boot
+ boot = None
+
+ if boot:
+ raise ValueError("Invalid boot.*: {instances}".format(instances=' '.join(boot)))
+
+ # any boot= given overrides boot.* fields
+ if not boot_str:
+ pass
+
+ elif boot_str.startswith('/'):
+ filename = boot_str
+
+ elif boot_str.endswith(':'):
+ next_server = boot_str[:-1]
+
+ elif ':' in boot_str:
+ next_server, filename = boot_str.split(':', 1)
+
+ else :
+ raise ValueError("invalid boot={boot}".format(boot=boot_str))
+
+ return next_server, filename
+
+@pvl.hosts.host.register_extension
+class HostDHCP(pvl.hosts.host.HostExtension):
+ EXTENSION = 'dhcp'
+ EXTENSION_FIELDS = (
+ 'boot',
+ )
+
+ @classmethod
+ def build (cls,
+ boot = None,
+ subclass = None,
+ ):
+ next_server, filename = parse_dhcp_boot(boot)
+
+ return cls(
+ filename = filename,
+ subclass = subclass,
+ next_server = next_server,
+ )
+
+ def __init__(self,
+ filename = None,
+ next_server = None,
+ subclass = None,
+ ):
+ self.filename = filename
+ self.next_server = next_server
+ self.subclass = subclass
+
def dhcp_host_subclass (host, subclass, ethernet):
"""
Build a DHCP Item for declaring a subclass for a host.
@@ -16,7 +103,7 @@
class HostDHCPError(pvl.hosts.host.HostError):
pass
-def dhcp_host_options (host, ethernet, subclass=None):
+def dhcp_host_options (host, ethernet, dhcp=None):
"""
Yield specific dhcp.conf host { ... } items.
"""
@@ -26,20 +113,17 @@
if host.ip4:
yield 'fixed-address', pvl.dhcp.config.Field(str(host.ip4))
-
- for bootopt in ('next-server', 'filename'):
- if bootopt in host.boot:
- yield bootopt, host.boot[bootopt]
+
+ if dhcp:
+ if dhcp.next_server:
+ yield 'next-server', dhcp.next_server
-def dhcp_host (host,
- subclass = None,
-):
+ if dhcp.filename:
+ yield 'filename', dhcp.filename
+
+def dhcp_host (host):
"""
- Yield pvl.dhcp.config.Block's
-
- Takes dhcp:* extensions as keyword arguments
-
- subclass: name - generate a subclass name $ethernet for this host
+ Yield pvl.dhcp.config.Block's for given Host, with possible HostDHCP extensions.
"""
if not host.ethernet:
@@ -50,6 +134,8 @@
comment = u"Owner: {host.owner}".format(host=host)
else:
comment = None
+
+ dhcp = host.extensions.get('dhcp')
for index, ethernet in host.ethernet.iteritems() :
if index:
@@ -57,12 +143,12 @@
else:
name = '{host.name}'.format(host=host)
- items = list(dhcp_host_options(host, ethernet))
+ items = list(dhcp_host_options(host, ethernet, dhcp=dhcp))
yield pvl.dhcp.config.Block(('host', name), items, comment=comment)
- if subclass:
- yield dhcp_host_subclass(host, subclass, ethernet)
+ if dhcp and dhcp.subclass:
+ yield dhcp_host_subclass(host, dhcp.subclass, ethernet)
def dhcp_hosts (hosts):
"""
@@ -73,9 +159,7 @@
blocks = { }
for host in hosts:
- extensions = host.extensions.get('dhcp', {})
-
- for block in dhcp_host(host, **extensions):
+ for block in dhcp_host(host):
if not block.key:
# TODO: check for unique Item-Blocks
pass