pvl.dhcp.config: make quoting context-sensitive, so that only certain items get special quoting..
authorTero Marttila <terom@paivola.fi>
Mon, 02 Mar 2015 01:00:48 +0200
changeset 679 31adba0f586d
parent 678 5f280b922905
child 680 e9e657b74fc9
pvl.dhcp.config: make quoting context-sensitive, so that only certain items get special quoting..
pvl/dhcp/config.py
pvl/dhcp/tests.py
--- a/pvl/dhcp/config.py	Mon Mar 02 00:38:59 2015 +0200
+++ b/pvl/dhcp/config.py	Mon Mar 02 01:00:48 2015 +0200
@@ -3,7 +3,18 @@
 import string
 
 # simplified model of lexer chars
-TOKEN = string.ascii_letters + string.digits + '-_.:'
+TOKEN_START = string.ascii_letters + string.digits + '-'
+TOKEN = TOKEN_START + '_'
+
+# be far more lenient when parsing
+TOKEN_EXTRA = TOKEN + ':.'
+
+UNQUOTED_CONTEXT = set((
+    ('host', ),
+    ('fixed-address', ),
+    ('next-server', ),
+    ('hardware', 'ethernet'),
+))
 
 class DHCPConfigError(Exception):
     def __init__ (self, parser, error, line=None):
@@ -47,7 +58,7 @@
 
     lexer = shlex.shlex(line, posix=True)
     lexer.commenters = '#'
-    lexer.wordchars = TOKEN
+    lexer.wordchars = TOKEN_EXTRA
 
     while True:
         item = lexer.get_token()
@@ -57,7 +68,7 @@
 
         yield item
 
-def quote (value):
+def quote (value, context=None):
     """
         Build a single field as part of a dhcp.conf line.
 
@@ -70,12 +81,22 @@
         >>> print quote(5)
         5
         >>> print quote('5')
-        "5"
+        5
+        >>> print quote('foo.bar')
+        "foo.bar"
+        >>> print quote('foo.bar', context=('host', ))
+        foo.bar
+        >>> print quote('192.0.2.1', context=('fixed-address', ))
+        192.0.2.1
+        >>> print quote('00:11:22:33:44:55', context=('hardware', 'ethernet'))
+        00:11:22:33:44:55
     """
 
     if isinstance(value, int):
         return str(value)
-    elif all(c in TOKEN for c in value):
+    elif context in UNQUOTED_CONTEXT:
+        return str(value)
+    elif value[0] in TOKEN_START and all(c in TOKEN for c in value):
         return str(value)
     else:
         # quoted
@@ -239,29 +260,32 @@
             for block in self.parse_line(line) :
                 yield block
     
-def build_line (item, end, indent=0):
+def build_line (item, end, indent=0, context=None):
     """
         Build a structured line.
 
-        >>> print build_line(['foo'], ';')
+        >>> print build_line(('foo', ), ';')
         foo;
-        >>> print build_line(['host', 'foo'], ' {')
+        >>> print build_line(('host', 'foo'), ' {')
         host foo {
-        >>> print build_line(['foo', 'bar quux'], ';', indent=1)
+        >>> print build_line(('foo', 'bar quux'), ';', indent=1)
             foo "bar quux";
     """
 
-    return '    '*indent + ' '.join(quote(field) for field in item) + end
+    return '    '*indent + ' '.join(quote(field, context=context) for field in item) + end
 
 def build_item (item, **opts):
     """
         Build a single parameter line.
 
-        >>> print build_item(['foo'])
+        >>> print build_item(('foo', ))
         foo;
     """
 
-    return build_line(item, end=';', **opts)
+    return build_line(item, end=';',
+        context     = item[:-1],
+        **opts
+    )
 
 def build_block (block, indent=0):
     """
--- a/pvl/dhcp/tests.py	Mon Mar 02 00:38:59 2015 +0200
+++ b/pvl/dhcp/tests.py	Mon Mar 02 01:00:48 2015 +0200
@@ -123,6 +123,7 @@
                         ('include', "hosts/test.conf"),
                     ], [
                         config.Block(('host', 'foo'), [
+                            ('option', 'host-name', "foo.test"),
                             ('fixed-address', '192.0.2.1'),
                             ('hardware', 'ethernet', '00:11:22:33:44:55'),
                         ]),
@@ -135,6 +136,7 @@
     include "hosts/test.conf";
 
     host foo {
+        option host-name    "foo.test";
         fixed-address       192.0.2.1;
         hardware ethernet   00:11:22:33:44:55;
     }