pvl/dhcp/config.py
changeset 673 0eda16e29613
parent 668 794f943c835d
child 675 f1335a4301d0
equal deleted inserted replaced
672:87dac237b46e 673:0eda16e29613
     1 import logging; log = logging.getLogger('pvl.dhcp.config')
     1 import logging; log = logging.getLogger('pvl.dhcp.config')
     2 import shlex
     2 import shlex
       
     3 import string
       
     4 
       
     5 # simplified model of lexer chars
       
     6 TOKEN_START = string.ascii_letters
       
     7 TOKEN = TOKEN_START + string.digits + '-_.'
     3 
     8 
     4 class DHCPConfigError(Exception):
     9 class DHCPConfigError(Exception):
     5     def __init__ (self, parser, error, line=None):
    10     def __init__ (self, parser, error, line=None):
     6         self.parser = parser
    11         self.parser = parser
     7 
    12 
    30         ['foo', 'asdf quux']
    35         ['foo', 'asdf quux']
    31         >>> list(split('# nevermind'))
    36         >>> list(split('# nevermind'))
    32         []
    37         []
    33         >>> list(split(''))
    38         >>> list(split(''))
    34         []
    39         []
       
    40         >>> list(split('next-server foo'))
       
    41         ['next-server', 'foo']
       
    42         >>> list(split('include "foo/bar.conf";'))
       
    43         ['include', 'foo/bar.conf', ';']
    35     """
    44     """
    36 
    45 
    37     if line is None:
    46     if line is None:
    38         raise TypeError(line)
    47         raise TypeError(line)
    39 
    48 
    40     lexer = shlex.shlex(line, posix=True)
    49     lexer = shlex.shlex(line, posix=True)
    41     lexer.commenters = '#'
    50     lexer.commenters = '#'
    42     lexer.wordchars += '-./'
    51     lexer.wordchars = TOKEN
    43 
    52 
    44     while True:
    53     while True:
    45         item = lexer.get_token()
    54         item = lexer.get_token()
    46 
    55 
    47         if item is None:
    56         if item is None:
    48             break
    57             break
    49 
    58 
    50         yield item
    59         yield item
       
    60 
       
    61 def quote (value):
       
    62     """
       
    63         Build a single field as part of a dhcp.conf line.
       
    64 
       
    65         >>> print quote('foo')
       
    66         foo
       
    67         >>> print quote('foo bar')
       
    68         "foo bar"
       
    69         >>> print quote('foo/bar.conf')
       
    70         "foo/bar.conf"
       
    71         >>> print quote(5)
       
    72         5
       
    73         >>> print quote('5')
       
    74         "5"
       
    75     """
       
    76 
       
    77     if isinstance(value, int):
       
    78         return str(value)
       
    79     elif not value:
       
    80         return ''
       
    81     elif value[0] in TOKEN_START and all(c in TOKEN for c in value):
       
    82         return str(value)
       
    83     else:
       
    84         # quoted
       
    85         return '"{value}"'.format(value=value)
    51 
    86 
    52 class Block (object):
    87 class Block (object):
    53     """
    88     """
    54         A block in a dhcp conf includes parameters and sub-blocks.
    89         A block in a dhcp conf includes parameters and sub-blocks.
    55     """
    90     """
    82     @classmethod
   117     @classmethod
    83     def load (cls, file, name=None):
   118     def load (cls, file, name=None):
    84         """
   119         """
    85             Parse an complete file, returning the top-level Block.
   120             Parse an complete file, returning the top-level Block.
    86 
   121 
    87             >>> DHCPConfigParser.load(['foo;', 'bar {', '\tasdf "quux";', '}'])
   122             >>> DHCPConfigParser.load(['foo;', 'bar {', '\tasdf "quux";', '}'], name='test')
    88             Block(None, items=[('foo', )], blocks=[Block(('bar', ), items=[('asdf', 'quux')], blocks=[])])
   123             Block(None, items=[('foo', )], blocks=[Block(('bar', ), items=[('asdf', 'quux')], blocks=[])])
    89         """
   124         """
    90         
   125         
    91         if name is None:
   126         if name is None:
    92             name = file.name
   127             name = file.name
   205 
   240 
   206         for line in lines:
   241         for line in lines:
   207             for block in self.parse_line(line) :
   242             for block in self.parse_line(line) :
   208                 yield block
   243                 yield block
   209     
   244     
   210 def build_field (value):
       
   211     """
       
   212         Build a single field as part of a dhcp.conf line.
       
   213 
       
   214         >>> print build_field('foo')
       
   215         foo
       
   216         >>> print build_field('foo bar')
       
   217         "foo bar"
       
   218     """
       
   219 
       
   220     if any(c.isspace() for c in value):
       
   221         # quoted
       
   222         return '"{value}"'.format(value=value)
       
   223     else:
       
   224         return value
       
   225 
       
   226 def build_line (item, end, indent=0):
   245 def build_line (item, end, indent=0):
   227     """
   246     """
   228         Build a structured line.
   247         Build a structured line.
   229 
   248 
   230         >>> print build_line(['foo'], ';')
   249         >>> print build_line(['foo'], ';')
   233         host foo {
   252         host foo {
   234         >>> print build_line(['foo', 'bar quux'], ';', indent=1)
   253         >>> print build_line(['foo', 'bar quux'], ';', indent=1)
   235             foo "bar quux";
   254             foo "bar quux";
   236     """
   255     """
   237 
   256 
   238     return '    '*indent + ' '.join(build_field(field) for field in item) + end
   257     return '    '*indent + ' '.join(quote(field) for field in item) + end
   239 
   258 
   240 def build_item (item, **opts):
   259 def build_item (item, **opts):
   241     """
   260     """
   242         Build a single parameter line.
   261         Build a single parameter line.
   243 
   262