pvl/hosts/dhcp.py
changeset 739 5149c39f3dfc
parent 733 45bedeba92e5
equal deleted inserted replaced
738:3104fdf7ea26 739:5149c39f3dfc
     1 import pvl.dhcp.config
     1 import pvl.dhcp.config
     2 import pvl.hosts.host
     2 import pvl.hosts.host
       
     3 
       
     4 def parse_dhcp_boot(boot):
       
     5     """
       
     6         Parse the dhcp boot=... option
       
     7 
       
     8         >>> print parse_dhcp_boot(None)
       
     9         {}
       
    10         >>> print parse_dhcp_boot({'filename': '/foo'})
       
    11         {'filename': '/foo'}
       
    12         >>> print parse_dhcp_boot({'filename': '/foo', 'next-server': 'bar'})
       
    13         {'next-server': 'bar', 'filename': '/foo'}
       
    14         >>> print parse_dhcp_boot('/foo')
       
    15         {'filename': '/foo'}
       
    16         >>> print parse_dhcp_boot('bar:/foo')
       
    17         {'next-server': 'bar', 'filename': '/foo'}
       
    18         >>> print parse_dhcp_boot('bar:')
       
    19         {'next-server': 'bar'}
       
    20         >>> print parse_dhcp_boot('foo')
       
    21         Traceback (most recent call last):
       
    22             ...
       
    23         ValueError: invalid boot=foo
       
    24     """
       
    25     
       
    26     # unpack dict, or str
       
    27     if not boot:
       
    28         filename = next_server = None
       
    29         boot_str = None
       
    30 
       
    31     elif isinstance(boot, dict):
       
    32         filename = boot.pop('filename', None)
       
    33         next_server = boot.pop('next-server', None)
       
    34         boot_str = boot.pop(None, None)
       
    35 
       
    36     else:
       
    37         filename = next_server = None
       
    38         boot_str = boot
       
    39         boot = None
       
    40         
       
    41     if boot:
       
    42         raise ValueError("Invalid boot.*: {instances}".format(instances=' '.join(boot)))
       
    43     
       
    44     # any boot= given overrides boot.* fields
       
    45     if not boot_str:
       
    46         pass
       
    47 
       
    48     elif boot_str.startswith('/'):
       
    49         filename = boot_str
       
    50 
       
    51     elif boot_str.endswith(':'):
       
    52         next_server = boot_str[:-1]
       
    53 
       
    54     elif ':' in boot_str:
       
    55         next_server, filename = boot_str.split(':', 1)
       
    56 
       
    57     else :
       
    58         raise ValueError("invalid boot={boot}".format(boot=boot_str))
       
    59     
       
    60     return next_server, filename
       
    61 
       
    62 @pvl.hosts.host.register_extension
       
    63 class HostDHCP(pvl.hosts.host.HostExtension):
       
    64     EXTENSION = 'dhcp'
       
    65     EXTENSION_FIELDS = (
       
    66             'boot',
       
    67     )
       
    68 
       
    69     @classmethod
       
    70     def build (cls,
       
    71             boot        = None,
       
    72             subclass    = None,
       
    73     ):
       
    74         next_server, filename = parse_dhcp_boot(boot)
       
    75 
       
    76         return cls(
       
    77                 filename    = filename,
       
    78                 subclass    = subclass,
       
    79                 next_server = next_server,
       
    80         )
       
    81 
       
    82     def __init__(self,
       
    83             filename    = None,
       
    84             next_server = None,
       
    85             subclass    = None,
       
    86     ):
       
    87         self.filename = filename
       
    88         self.next_server = next_server
       
    89         self.subclass = subclass
     3 
    90 
     4 def dhcp_host_subclass (host, subclass, ethernet):
    91 def dhcp_host_subclass (host, subclass, ethernet):
     5     """
    92     """
     6         Build a DHCP Item for declaring a subclass for a host.
    93         Build a DHCP Item for declaring a subclass for a host.
     7     """
    94     """
    14     ])
   101     ])
    15 
   102 
    16 class HostDHCPError(pvl.hosts.host.HostError):
   103 class HostDHCPError(pvl.hosts.host.HostError):
    17     pass
   104     pass
    18 
   105 
    19 def dhcp_host_options (host, ethernet, subclass=None):
   106 def dhcp_host_options (host, ethernet, dhcp=None):
    20     """
   107     """
    21         Yield specific dhcp.conf host { ... } items.
   108         Yield specific dhcp.conf host { ... } items.
    22     """
   109     """
    23 
   110 
    24     yield 'option', 'host-name', host.name
   111     yield 'option', 'host-name', host.name
    25     yield 'hardware', 'ethernet', pvl.dhcp.config.Field(ethernet)
   112     yield 'hardware', 'ethernet', pvl.dhcp.config.Field(ethernet)
    26 
   113 
    27     if host.ip4:
   114     if host.ip4:
    28         yield 'fixed-address', pvl.dhcp.config.Field(str(host.ip4))
   115         yield 'fixed-address', pvl.dhcp.config.Field(str(host.ip4))
    29       
   116     
    30     for bootopt in ('next-server', 'filename'):
   117     if dhcp:
    31         if bootopt in host.boot:
   118         if dhcp.next_server:
    32             yield bootopt, host.boot[bootopt]
   119             yield 'next-server', dhcp.next_server
    33 
   120 
    34 def dhcp_host (host,
   121         if dhcp.filename:
    35         subclass    = None,
   122             yield 'filename', dhcp.filename
    36 ):
   123 
       
   124 def dhcp_host (host):
    37     """
   125     """
    38         Yield pvl.dhcp.config.Block's
   126         Yield pvl.dhcp.config.Block's for given Host, with possible HostDHCP extensions.
    39 
       
    40         Takes dhcp:* extensions as keyword arguments
       
    41 
       
    42             subclass: name      - generate a subclass name $ethernet for this host
       
    43     """
   127     """
    44 
   128 
    45     if not host.ethernet:
   129     if not host.ethernet:
    46         # nothing to be seen here
   130         # nothing to be seen here
    47         return
   131         return
    48  
   132  
    49     if host.owner :
   133     if host.owner :
    50         comment = u"Owner: {host.owner}".format(host=host)
   134         comment = u"Owner: {host.owner}".format(host=host)
    51     else:
   135     else:
    52         comment = None
   136         comment = None
       
   137     
       
   138     dhcp = host.extensions.get('dhcp')
    53 
   139 
    54     for index, ethernet in host.ethernet.iteritems() :
   140     for index, ethernet in host.ethernet.iteritems() :
    55         if index:
   141         if index:
    56             name = '{host.name}-{index}'.format(host=host, index=index)
   142             name = '{host.name}-{index}'.format(host=host, index=index)
    57         else:
   143         else:
    58             name = '{host.name}'.format(host=host)
   144             name = '{host.name}'.format(host=host)
    59 
   145 
    60         items = list(dhcp_host_options(host, ethernet))
   146         items = list(dhcp_host_options(host, ethernet, dhcp=dhcp))
    61 
   147 
    62         yield pvl.dhcp.config.Block(('host', name), items, comment=comment)
   148         yield pvl.dhcp.config.Block(('host', name), items, comment=comment)
    63 
   149 
    64         if subclass:
   150         if dhcp and dhcp.subclass:
    65             yield dhcp_host_subclass(host, subclass, ethernet)
   151             yield dhcp_host_subclass(host, dhcp.subclass, ethernet)
    66     
   152     
    67 def dhcp_hosts (hosts):
   153 def dhcp_hosts (hosts):
    68     """
   154     """
    69 
   155 
    70         Verifies that there are no dupliate hosts.
   156         Verifies that there are no dupliate hosts.
    71     """
   157     """
    72 
   158 
    73     blocks = { }
   159     blocks = { }
    74 
   160 
    75     for host in hosts:
   161     for host in hosts:
    76         extensions = host.extensions.get('dhcp', {})
   162         for block in dhcp_host(host):
    77 
       
    78         for block in dhcp_host(host, **extensions):
       
    79             if not block.key:
   163             if not block.key:
    80                 # TODO: check for unique Item-Blocks
   164                 # TODO: check for unique Item-Blocks
    81                 pass
   165                 pass
    82             elif block.key in blocks:
   166             elif block.key in blocks:
    83                 raise HostDHCPError(host, "dhcp {block} conflict with {other}; hosts on multiple networks must use unique ethernet.XXX=... naming".format(block=block, other=blocks[block.key]))
   167                 raise HostDHCPError(host, "dhcp {block} conflict with {other}; hosts on multiple networks must use unique ethernet.XXX=... naming".format(block=block, other=blocks[block.key]))