changeset 673 | 0eda16e29613 |
parent 668 | 794f943c835d |
child 675 | f1335a4301d0 |
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 |