Merge lp:~mpontillo/maas/remove-iscpy-1.8 into lp:maas/1.8
- remove-iscpy-1.8
- Merge into 1.8
Status: | Merged |
---|---|
Approved by: | Mike Pontillo |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4014 |
Proposed branch: | lp:~mpontillo/maas/remove-iscpy-1.8 |
Merge into: | lp:maas/1.8 |
Diff against target: |
546 lines (+470/-10) 5 files modified
buildout.cfg (+0/-1) src/maasserver/management/commands/edit_named_options.py (+8/-8) src/maasserver/utils/isc.py (+283/-0) src/maasserver/utils/tests/test_isc.py (+179/-0) versions.cfg (+0/-1) |
To merge this branch: | bzr merge lp:~mpontillo/maas/remove-iscpy-1.8 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Review via email: mp+263552@code.launchpad.net |
Commit message
Remove dependency on iscpy. Add MAAS utility (based on iscpy) for parsing named.options. Add tests for iscpy-derived code.
Description of the change
Mike Pontillo (mpontillo) wrote : | # |
Mike Pontillo (mpontillo) wrote : | # |
Self-approving. 1.7 and trunk have already landed. I don't want 1.8 to feel left out. =)
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~mpontillo/maas/remove-iscpy-1.8 into lp:maas/1.8 failed. Below is the output from the failed tests.
Ign http://
Get:1 http://
Get:2 http://
Ign http://
Ign http://
Hit http://
Get:3 http://
Hit http://
Get:4 http://
Get:5 http://
Get:6 http://
Hit http://
Get:7 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Get:8 http://
Get:9 http://
Hit http://
Hit http://
Get:10 http://
Get:11 http://
Get:12 http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 1,848 kB in 3s (600 kB/s)
Reading package lists...
sudo DEBIAN_
--
Preview Diff
1 | === modified file 'buildout.cfg' | |||
2 | --- buildout.cfg 2015-05-29 12:30:52 +0000 | |||
3 | +++ buildout.cfg 2015-07-04 09:40:25 +0000 | |||
4 | @@ -71,7 +71,6 @@ | |||
5 | 71 | djorm-ext-pgarray | 71 | djorm-ext-pgarray |
6 | 72 | docutils | 72 | docutils |
7 | 73 | crochet | 73 | crochet |
8 | 74 | iscpy | ||
9 | 75 | entry-points = | 74 | entry-points = |
10 | 76 | maas-region-admin=maasserver:execute_from_command_line | 75 | maas-region-admin=maasserver:execute_from_command_line |
11 | 77 | twistd.region=twisted.scripts.twistd:run | 76 | twistd.region=twisted.scripts.twistd:run |
12 | 78 | 77 | ||
13 | === modified file 'src/maasserver/management/commands/edit_named_options.py' | |||
14 | --- src/maasserver/management/commands/edit_named_options.py 2015-05-07 18:14:38 +0000 | |||
15 | +++ src/maasserver/management/commands/edit_named_options.py 2015-07-04 09:40:25 +0000 | |||
16 | @@ -28,9 +28,9 @@ | |||
17 | 28 | BaseCommand, | 28 | BaseCommand, |
18 | 29 | CommandError, | 29 | CommandError, |
19 | 30 | ) | 30 | ) |
23 | 31 | from iscpy import ( | 31 | from maasserver.utils.isc import ( |
24 | 32 | MakeISC, | 32 | make_isc_string, |
25 | 33 | ParseISCString, | 33 | parse_isc_string, |
26 | 34 | ) | 34 | ) |
27 | 35 | from provisioningserver.dns.config import MAAS_NAMED_CONF_OPTIONS_INSIDE_NAME | 35 | from provisioningserver.dns.config import MAAS_NAMED_CONF_OPTIONS_INSIDE_NAME |
28 | 36 | 36 | ||
29 | @@ -61,12 +61,12 @@ | |||
30 | 61 | return options_file | 61 | return options_file |
31 | 62 | 62 | ||
32 | 63 | def parse_file(self, config_path, options_file): | 63 | def parse_file(self, config_path, options_file): |
34 | 64 | """Read the named.conf.options file and parse it with iscpy. | 64 | """Read the named.conf.options file and parse it. |
35 | 65 | 65 | ||
37 | 66 | We also use iscpy to insert the include statement that we need. | 66 | Then insert the include statement that we need. |
38 | 67 | """ | 67 | """ |
39 | 68 | try: | 68 | try: |
41 | 69 | config_dict = ParseISCString(options_file) | 69 | config_dict = parse_isc_string(options_file) |
42 | 70 | except Exception as e: | 70 | except Exception as e: |
43 | 71 | # Yes, it throws bare exceptions :( | 71 | # Yes, it throws bare exceptions :( |
44 | 72 | raise CommandError("Failed to parse %s: %s" % ( | 72 | raise CommandError("Failed to parse %s: %s" % ( |
45 | @@ -81,7 +81,7 @@ | |||
46 | 81 | return config_dict | 81 | return config_dict |
47 | 82 | 82 | ||
48 | 83 | def set_up_include_statement(self, options_block, config_path): | 83 | def set_up_include_statement(self, options_block, config_path): |
50 | 84 | """Insert the 'include' directive into the iscpy-parsed options.""" | 84 | """Insert the 'include' directive into the parsed options.""" |
51 | 85 | dir = os.path.join(os.path.dirname(config_path), "maas") | 85 | dir = os.path.join(os.path.dirname(config_path), "maas") |
52 | 86 | options_block['include'] = '"%s%s%s"' % ( | 86 | options_block['include'] = '"%s%s%s"' % ( |
53 | 87 | dir, os.path.sep, MAAS_NAMED_CONF_OPTIONS_INSIDE_NAME) | 87 | dir, os.path.sep, MAAS_NAMED_CONF_OPTIONS_INSIDE_NAME) |
54 | @@ -128,7 +128,7 @@ | |||
55 | 128 | self.set_up_include_statement(options_block, config_path) | 128 | self.set_up_include_statement(options_block, config_path) |
56 | 129 | self.remove_forwarders(options_block) | 129 | self.remove_forwarders(options_block) |
57 | 130 | self.remove_dnssec_validation(options_block) | 130 | self.remove_dnssec_validation(options_block) |
59 | 131 | new_content = MakeISC(config_dict) | 131 | new_content = make_isc_string(config_dict) |
60 | 132 | 132 | ||
61 | 133 | # Back up and write new file. | 133 | # Back up and write new file. |
62 | 134 | self.back_up_existing_file(config_path) | 134 | self.back_up_existing_file(config_path) |
63 | 135 | 135 | ||
64 | === added file 'src/maasserver/utils/isc.py' | |||
65 | --- src/maasserver/utils/isc.py 1970-01-01 00:00:00 +0000 | |||
66 | +++ src/maasserver/utils/isc.py 2015-07-04 09:40:25 +0000 | |||
67 | @@ -0,0 +1,283 @@ | |||
68 | 1 | # Copyright (c) 2009, Purdue University | ||
69 | 2 | # Copyright (c) 2015, Canonical Ltd. | ||
70 | 3 | # All rights reserved. | ||
71 | 4 | # | ||
72 | 5 | # Redistribution and use in source and binary forms, with or without | ||
73 | 6 | # modification, are permitted provided that the following conditions are met: | ||
74 | 7 | # | ||
75 | 8 | # Redistributions of source code must retain the above copyright notice, this | ||
76 | 9 | # list of conditions and the following disclaimer. | ||
77 | 10 | # | ||
78 | 11 | # Redistributions in binary form must reproduce the above copyright notice, | ||
79 | 12 | # this list of conditions and the following disclaimer in the documentation | ||
80 | 13 | # and/or other materials provided with the distribution. | ||
81 | 14 | # | ||
82 | 15 | # Neither the name of the Purdue University nor the names of its contributors | ||
83 | 16 | # may be used to endorse or promote products derived from this software without | ||
84 | 17 | # specific prior written permission. | ||
85 | 18 | # | ||
86 | 19 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
87 | 20 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
88 | 21 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
89 | 22 | # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
90 | 23 | # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
91 | 24 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
92 | 25 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
93 | 26 | # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
94 | 27 | # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
95 | 28 | # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
96 | 29 | # POSSIBILITY OF SUCH DAMAGE. | ||
97 | 30 | |||
98 | 31 | from __future__ import ( | ||
99 | 32 | absolute_import, | ||
100 | 33 | print_function, | ||
101 | 34 | unicode_literals, | ||
102 | 35 | ) | ||
103 | 36 | |||
104 | 37 | str = None | ||
105 | 38 | |||
106 | 39 | __metaclass__ = type | ||
107 | 40 | __all__ = [ | ||
108 | 41 | 'make_isc_string', | ||
109 | 42 | 'parse_isc_string', | ||
110 | 43 | ] | ||
111 | 44 | |||
112 | 45 | import copy | ||
113 | 46 | |||
114 | 47 | |||
115 | 48 | def _clip(char_list): | ||
116 | 49 | """Clips char_list to individual stanza. | ||
117 | 50 | |||
118 | 51 | Inputs: | ||
119 | 52 | char_list: partial of char_list from _parse_tokens | ||
120 | 53 | |||
121 | 54 | Outputs: | ||
122 | 55 | tuple: (int: skip to char list index, list: shortened char_list) | ||
123 | 56 | """ | ||
124 | 57 | assert char_list[0] == '{' | ||
125 | 58 | char_list.pop(0) | ||
126 | 59 | skip = 0 | ||
127 | 60 | for index, item in enumerate(char_list): | ||
128 | 61 | if item == '{': | ||
129 | 62 | skip += 1 | ||
130 | 63 | elif item == '}' and skip == 0: | ||
131 | 64 | return index, char_list[:index] | ||
132 | 65 | elif item == '}': | ||
133 | 66 | skip -= 1 | ||
134 | 67 | raise Exception("Invalid brackets.") | ||
135 | 68 | |||
136 | 69 | |||
137 | 70 | def _parse_tokens(char_list): | ||
138 | 71 | """Parses exploded isc named.conf portions. | ||
139 | 72 | |||
140 | 73 | Inputs: | ||
141 | 74 | char_list: List of isc file parts | ||
142 | 75 | |||
143 | 76 | Outputs: | ||
144 | 77 | dict: fragment or full isc file dict | ||
145 | 78 | Recursive dictionary of isc file, dict values can be of 3 types, | ||
146 | 79 | dict, string and bool. Boolean values are always true. Booleans are false | ||
147 | 80 | if key is absent. Booleans represent situations in isc files such as: | ||
148 | 81 | acl "registered" { 10.1.0/32; 10.1.1:/32;}} | ||
149 | 82 | |||
150 | 83 | Example: | ||
151 | 84 | |||
152 | 85 | {'stanza1 "new"': 'test_info', 'stanza1 "embedded"': {'acl "registered"': | ||
153 | 86 | {'10.1.0/32': True, '10.1.1/32': True}}} | ||
154 | 87 | """ | ||
155 | 88 | index = 0 | ||
156 | 89 | dictionary_fragment = {} | ||
157 | 90 | new_char_list = copy.deepcopy(char_list) | ||
158 | 91 | if type(new_char_list) == str: | ||
159 | 92 | return new_char_list | ||
160 | 93 | if type(new_char_list) == dict: | ||
161 | 94 | return new_char_list | ||
162 | 95 | last_open = None | ||
163 | 96 | continuous_line = False | ||
164 | 97 | temp_list = [] | ||
165 | 98 | |||
166 | 99 | # Prevent "may be referenced before assignment" error | ||
167 | 100 | key = None | ||
168 | 101 | |||
169 | 102 | while index < len(new_char_list): | ||
170 | 103 | if new_char_list[index] == '{': | ||
171 | 104 | last_open = index | ||
172 | 105 | if new_char_list[index] == ';' and continuous_line: | ||
173 | 106 | dictionary_fragment = temp_list | ||
174 | 107 | temp_list = [] | ||
175 | 108 | continuous_line = False | ||
176 | 109 | if new_char_list[index] == ';': | ||
177 | 110 | continuous_line = False | ||
178 | 111 | if (len(new_char_list) > index + 1 and | ||
179 | 112 | new_char_list[index] == '}' and | ||
180 | 113 | new_char_list[index + 1] != ';'): | ||
181 | 114 | skip, value = _clip(new_char_list[last_open:]) | ||
182 | 115 | temp_list.append({key: copy.deepcopy(_parse_tokens(value))}) | ||
183 | 116 | continuous_line = True | ||
184 | 117 | if len(new_char_list) > index + 1 and new_char_list[index + 1] == '{': | ||
185 | 118 | # assert key is not None | ||
186 | 119 | key = new_char_list.pop(index) | ||
187 | 120 | skip, dict_value = _clip(new_char_list[index:]) | ||
188 | 121 | if continuous_line: | ||
189 | 122 | temp_list.append( | ||
190 | 123 | {key: copy.deepcopy(_parse_tokens(dict_value))}) | ||
191 | 124 | else: | ||
192 | 125 | dictionary_fragment[key] = copy.deepcopy( | ||
193 | 126 | _parse_tokens(dict_value)) | ||
194 | 127 | index += skip | ||
195 | 128 | else: | ||
196 | 129 | if len(new_char_list[ | ||
197 | 130 | index].split()) == 1 and '{' not in new_char_list: | ||
198 | 131 | for item in new_char_list: | ||
199 | 132 | if item in [';']: | ||
200 | 133 | continue | ||
201 | 134 | dictionary_fragment[item] = True | ||
202 | 135 | |||
203 | 136 | # If there are more than 1 'keywords' at new_char_list[index] | ||
204 | 137 | # ex - "recursion no;" | ||
205 | 138 | elif len(new_char_list[index].split()) >= 2: | ||
206 | 139 | dictionary_fragment[ | ||
207 | 140 | new_char_list[index].split()[0]] = ( | ||
208 | 141 | ' '.join(new_char_list[index].split()[1:])) | ||
209 | 142 | index += 1 | ||
210 | 143 | |||
211 | 144 | # If there is just 1 'keyword' at new_char_list[index] | ||
212 | 145 | # ex "recursion;" (not a valid option, but for example's sake it's | ||
213 | 146 | # fine) | ||
214 | 147 | elif new_char_list[index] not in ['{', ';', '}']: | ||
215 | 148 | key = new_char_list[index] | ||
216 | 149 | dictionary_fragment[key] = '' | ||
217 | 150 | index += 1 | ||
218 | 151 | index += 1 | ||
219 | 152 | |||
220 | 153 | return dictionary_fragment | ||
221 | 154 | |||
222 | 155 | |||
223 | 156 | def _scrub_comments(isc_string): | ||
224 | 157 | """Clears comments from an isc file | ||
225 | 158 | |||
226 | 159 | Inputs: | ||
227 | 160 | isc_string: string of isc file | ||
228 | 161 | Outputs: | ||
229 | 162 | string: string of scrubbed isc file | ||
230 | 163 | """ | ||
231 | 164 | isc_list = [] | ||
232 | 165 | if isc_string is None: | ||
233 | 166 | return '' | ||
234 | 167 | expanded_comment = False | ||
235 | 168 | for line in isc_string.split('\n'): | ||
236 | 169 | no_comment_line = "" | ||
237 | 170 | # Vet out any inline comments | ||
238 | 171 | if '/*' in line.strip(): | ||
239 | 172 | try: | ||
240 | 173 | striped_line = line.strip() | ||
241 | 174 | chars = enumerate(striped_line) | ||
242 | 175 | while True: | ||
243 | 176 | i, c = chars.next() | ||
244 | 177 | try: | ||
245 | 178 | if c == '/' and striped_line[i + 1] == '*': | ||
246 | 179 | expanded_comment = True | ||
247 | 180 | chars.next() # Skip '*' | ||
248 | 181 | continue | ||
249 | 182 | elif c == '*' and striped_line[i + 1] == '/': | ||
250 | 183 | expanded_comment = False | ||
251 | 184 | chars.next() # Skip '/' | ||
252 | 185 | continue | ||
253 | 186 | except IndexError: | ||
254 | 187 | continue # We are at the end of the line | ||
255 | 188 | if expanded_comment: | ||
256 | 189 | continue | ||
257 | 190 | else: | ||
258 | 191 | no_comment_line += c | ||
259 | 192 | except StopIteration: | ||
260 | 193 | if no_comment_line: | ||
261 | 194 | isc_list.append(no_comment_line) | ||
262 | 195 | continue | ||
263 | 196 | |||
264 | 197 | if expanded_comment: | ||
265 | 198 | if '*/' in line.strip(): | ||
266 | 199 | expanded_comment = False | ||
267 | 200 | isc_list.append(line.split('*/')[-1]) | ||
268 | 201 | continue | ||
269 | 202 | else: | ||
270 | 203 | continue | ||
271 | 204 | if line.strip().startswith(('#', '//')): | ||
272 | 205 | continue | ||
273 | 206 | else: | ||
274 | 207 | isc_list.append(line.split('#')[0].split('//')[0].strip()) | ||
275 | 208 | return '\n'.join(isc_list) | ||
276 | 209 | |||
277 | 210 | |||
278 | 211 | def _explode(isc_string): | ||
279 | 212 | """Explodes isc file into relevant tokens. | ||
280 | 213 | |||
281 | 214 | Inputs: | ||
282 | 215 | isc_string: String of isc file | ||
283 | 216 | |||
284 | 217 | Outputs: | ||
285 | 218 | list: list of isc file tokens delimited by brackets and semicolons | ||
286 | 219 | ['stanza1 "new"', '{', 'test_info', ';', '}'] | ||
287 | 220 | """ | ||
288 | 221 | str_array = [] | ||
289 | 222 | temp_string = [] | ||
290 | 223 | for char in isc_string: | ||
291 | 224 | if char in ['\n']: | ||
292 | 225 | continue | ||
293 | 226 | if char in ['{', '}', ';']: | ||
294 | 227 | if ''.join(temp_string).strip() == '': | ||
295 | 228 | str_array.append(char) | ||
296 | 229 | else: | ||
297 | 230 | str_array.append(''.join(temp_string).strip()) | ||
298 | 231 | str_array.append(char) | ||
299 | 232 | temp_string = [] | ||
300 | 233 | else: | ||
301 | 234 | temp_string.append(char) | ||
302 | 235 | return str_array | ||
303 | 236 | |||
304 | 237 | |||
305 | 238 | def parse_isc_string(isc_string): | ||
306 | 239 | """Makes a dictionary from an ISC file string | ||
307 | 240 | |||
308 | 241 | Inputs: | ||
309 | 242 | isc_string: string of isc file | ||
310 | 243 | |||
311 | 244 | Outputs: | ||
312 | 245 | dict: dictionary of ISC file representation | ||
313 | 246 | """ | ||
314 | 247 | return _parse_tokens(_explode(_scrub_comments(isc_string))) | ||
315 | 248 | |||
316 | 249 | |||
317 | 250 | def make_isc_string(isc_dict, terminate=True): | ||
318 | 251 | """Outputs an isc formatted file string from a dict | ||
319 | 252 | |||
320 | 253 | Inputs: | ||
321 | 254 | isc_dict: a recursive dictionary to be turned into an isc file | ||
322 | 255 | (from ParseTokens) | ||
323 | 256 | |||
324 | 257 | Outputs: | ||
325 | 258 | str: string of isc file without indentation | ||
326 | 259 | """ | ||
327 | 260 | if terminate: | ||
328 | 261 | terminator = ';' | ||
329 | 262 | else: | ||
330 | 263 | terminator = '' | ||
331 | 264 | if type(isc_dict) == str: | ||
332 | 265 | return isc_dict | ||
333 | 266 | isc_list = [] | ||
334 | 267 | for option in isc_dict: | ||
335 | 268 | if type(isc_dict[option]) == bool: | ||
336 | 269 | isc_list.append('%s%s' % (option, terminator)) | ||
337 | 270 | elif (type(isc_dict[option]) == str or | ||
338 | 271 | type(isc_dict[option]) == unicode): | ||
339 | 272 | isc_list.append('%s %s%s' % (option, isc_dict[option], terminator)) | ||
340 | 273 | elif type(isc_dict[option]) == list: | ||
341 | 274 | new_list = [] | ||
342 | 275 | for item in isc_dict[option]: | ||
343 | 276 | new_list.append(make_isc_string(item, terminate=False)) | ||
344 | 277 | new_list[-1] = '%s%s' % (new_list[-1], terminator) | ||
345 | 278 | isc_list.append( | ||
346 | 279 | '%s { %s }%s' % (option, ' '.join(new_list), terminator)) | ||
347 | 280 | elif type(isc_dict[option]) == dict: | ||
348 | 281 | isc_list.append('%s { %s }%s' % ( | ||
349 | 282 | option, make_isc_string(isc_dict[option]), terminator)) | ||
350 | 283 | return '\n'.join(isc_list) | ||
351 | 0 | 284 | ||
352 | === added file 'src/maasserver/utils/tests/test_isc.py' | |||
353 | --- src/maasserver/utils/tests/test_isc.py 1970-01-01 00:00:00 +0000 | |||
354 | +++ src/maasserver/utils/tests/test_isc.py 2015-07-04 09:40:25 +0000 | |||
355 | @@ -0,0 +1,179 @@ | |||
356 | 1 | # Copyright 2015 Canonical Ltd. This software is licensed under the | ||
357 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
358 | 3 | |||
359 | 4 | """Test ISC configuration file parser/generator.""" | ||
360 | 5 | |||
361 | 6 | from __future__ import ( | ||
362 | 7 | absolute_import, | ||
363 | 8 | print_function, | ||
364 | 9 | unicode_literals, | ||
365 | 10 | ) | ||
366 | 11 | |||
367 | 12 | str = None | ||
368 | 13 | |||
369 | 14 | __metaclass__ = type | ||
370 | 15 | __all__ = [] | ||
371 | 16 | |||
372 | 17 | |||
373 | 18 | from textwrap import dedent | ||
374 | 19 | |||
375 | 20 | from maasserver.utils.isc import ( | ||
376 | 21 | make_isc_string, | ||
377 | 22 | parse_isc_string, | ||
378 | 23 | ) | ||
379 | 24 | from maastesting.testcase import MAASTestCase | ||
380 | 25 | |||
381 | 26 | |||
382 | 27 | class TestParseISCString(MAASTestCase): | ||
383 | 28 | |||
384 | 29 | def test_parses_simple_bind_options(self): | ||
385 | 30 | testdata = dedent("""\ | ||
386 | 31 | options { | ||
387 | 32 | directory "/var/cache/bind"; | ||
388 | 33 | |||
389 | 34 | dnssec-validation auto; | ||
390 | 35 | |||
391 | 36 | auth-nxdomain no; # conform to RFC1035 | ||
392 | 37 | listen-on-v6 { any; }; | ||
393 | 38 | }; | ||
394 | 39 | """) | ||
395 | 40 | options = parse_isc_string(testdata) | ||
396 | 41 | self.assertEqual( | ||
397 | 42 | {u'options': {u'auth-nxdomain': u'no', | ||
398 | 43 | u'directory': u'"/var/cache/bind"', | ||
399 | 44 | u'dnssec-validation': u'auto', | ||
400 | 45 | u'listen-on-v6': {u'any': True}}}, options) | ||
401 | 46 | |||
402 | 47 | def test_parses_bind_acl(self): | ||
403 | 48 | testdata = dedent("""\ | ||
404 | 49 | acl goodclients { | ||
405 | 50 | 192.0.2.0/24; | ||
406 | 51 | localhost; | ||
407 | 52 | localnets; | ||
408 | 53 | }; | ||
409 | 54 | """) | ||
410 | 55 | acl = parse_isc_string(testdata) | ||
411 | 56 | self.assertEqual( | ||
412 | 57 | {u'acl goodclients': {u'192.0.2.0/24': True, | ||
413 | 58 | u'localhost': True, | ||
414 | 59 | u'localnets': True}}, acl) | ||
415 | 60 | |||
416 | 61 | def test_parses_multiple_forwarders(self): | ||
417 | 62 | testdata = dedent("""\ | ||
418 | 63 | forwarders { | ||
419 | 64 | 91.189.94.2; | ||
420 | 65 | 91.189.94.3; | ||
421 | 66 | 91.189.94.4; | ||
422 | 67 | 91.189.94.5; | ||
423 | 68 | 91.189.94.6; | ||
424 | 69 | }; | ||
425 | 70 | """) | ||
426 | 71 | forwarders = parse_isc_string(testdata) | ||
427 | 72 | self.assertEqual( | ||
428 | 73 | {u'forwarders': {u'91.189.94.2': True, | ||
429 | 74 | u'91.189.94.3': True, | ||
430 | 75 | u'91.189.94.4': True, | ||
431 | 76 | u'91.189.94.5': True, | ||
432 | 77 | u'91.189.94.6': True}}, forwarders) | ||
433 | 78 | |||
434 | 79 | def test_parses_bug_1413388_config(self): | ||
435 | 80 | testdata = dedent("""\ | ||
436 | 81 | acl canonical-int-ns { 91.189.90.151; 91.189.89.192; }; | ||
437 | 82 | |||
438 | 83 | options { | ||
439 | 84 | directory "/var/cache/bind"; | ||
440 | 85 | |||
441 | 86 | forwarders { | ||
442 | 87 | 91.189.94.2; | ||
443 | 88 | 91.189.94.2; | ||
444 | 89 | }; | ||
445 | 90 | |||
446 | 91 | dnssec-validation auto; | ||
447 | 92 | |||
448 | 93 | auth-nxdomain no; # conform to RFC1035 | ||
449 | 94 | listen-on-v6 { any; }; | ||
450 | 95 | |||
451 | 96 | allow-query { any; }; | ||
452 | 97 | allow-transfer { 10.222.64.1; canonical-int-ns; }; | ||
453 | 98 | |||
454 | 99 | notify explicit; | ||
455 | 100 | also-notify { 91.189.90.151; 91.189.89.192; }; | ||
456 | 101 | |||
457 | 102 | allow-query-cache { 10.222.64.0/18; }; | ||
458 | 103 | recursion yes; | ||
459 | 104 | }; | ||
460 | 105 | |||
461 | 106 | zone "." { type master; file "/etc/bind/db.special"; }; | ||
462 | 107 | """) | ||
463 | 108 | config = parse_isc_string(testdata) | ||
464 | 109 | self.assertEqual( | ||
465 | 110 | {u'acl canonical-int-ns': | ||
466 | 111 | {u'91.189.89.192': True, u'91.189.90.151': True}, | ||
467 | 112 | u'options': {u'allow-query': {u'any': True}, | ||
468 | 113 | u'allow-query-cache': {u'10.222.64.0/18': True}, | ||
469 | 114 | u'allow-transfer': {u'10.222.64.1': True, | ||
470 | 115 | u'canonical-int-ns': True}, | ||
471 | 116 | u'also-notify': {u'91.189.89.192': True, | ||
472 | 117 | u'91.189.90.151': True}, | ||
473 | 118 | u'auth-nxdomain': u'no', | ||
474 | 119 | u'directory': u'"/var/cache/bind"', | ||
475 | 120 | u'dnssec-validation': u'auto', | ||
476 | 121 | u'forwarders': {u'91.189.94.2': True}, | ||
477 | 122 | u'listen-on-v6': {u'any': True}, | ||
478 | 123 | u'notify': u'explicit', | ||
479 | 124 | u'recursion': u'yes'}, | ||
480 | 125 | u'zone "."': | ||
481 | 126 | {u'file': u'"/etc/bind/db.special"', u'type': u'master'}}, | ||
482 | 127 | config) | ||
483 | 128 | |||
484 | 129 | def test_parse_then_make_then_parse_generates_identical_config(self): | ||
485 | 130 | testdata = dedent("""\ | ||
486 | 131 | acl canonical-int-ns { 91.189.90.151; 91.189.89.192; }; | ||
487 | 132 | |||
488 | 133 | options { | ||
489 | 134 | directory "/var/cache/bind"; | ||
490 | 135 | |||
491 | 136 | forwarders { | ||
492 | 137 | 91.189.94.2; | ||
493 | 138 | 91.189.94.2; | ||
494 | 139 | }; | ||
495 | 140 | |||
496 | 141 | dnssec-validation auto; | ||
497 | 142 | |||
498 | 143 | auth-nxdomain no; # conform to RFC1035 | ||
499 | 144 | listen-on-v6 { any; }; | ||
500 | 145 | |||
501 | 146 | allow-query { any; }; | ||
502 | 147 | allow-transfer { 10.222.64.1; canonical-int-ns; }; | ||
503 | 148 | |||
504 | 149 | notify explicit; | ||
505 | 150 | also-notify { 91.189.90.151; 91.189.89.192; }; | ||
506 | 151 | |||
507 | 152 | allow-query-cache { 10.222.64.0/18; }; | ||
508 | 153 | recursion yes; | ||
509 | 154 | }; | ||
510 | 155 | |||
511 | 156 | zone "." { type master; file "/etc/bind/db.special"; }; | ||
512 | 157 | """) | ||
513 | 158 | config = parse_isc_string(testdata) | ||
514 | 159 | config_string = make_isc_string(config) | ||
515 | 160 | config = parse_isc_string(config_string) | ||
516 | 161 | self.assertEqual( | ||
517 | 162 | {u'acl canonical-int-ns': | ||
518 | 163 | {u'91.189.89.192': True, u'91.189.90.151': True}, | ||
519 | 164 | u'options': {u'allow-query': {u'any': True}, | ||
520 | 165 | u'allow-query-cache': {u'10.222.64.0/18': True}, | ||
521 | 166 | u'allow-transfer': {u'10.222.64.1': True, | ||
522 | 167 | u'canonical-int-ns': True}, | ||
523 | 168 | u'also-notify': {u'91.189.89.192': True, | ||
524 | 169 | u'91.189.90.151': True}, | ||
525 | 170 | u'auth-nxdomain': u'no', | ||
526 | 171 | u'directory': u'"/var/cache/bind"', | ||
527 | 172 | u'dnssec-validation': u'auto', | ||
528 | 173 | u'forwarders': {u'91.189.94.2': True}, | ||
529 | 174 | u'listen-on-v6': {u'any': True}, | ||
530 | 175 | u'notify': u'explicit', | ||
531 | 176 | u'recursion': u'yes'}, | ||
532 | 177 | u'zone "."': | ||
533 | 178 | {u'file': u'"/etc/bind/db.special"', u'type': u'master'}}, | ||
534 | 179 | config) | ||
535 | 0 | 180 | ||
536 | === modified file 'versions.cfg' | |||
537 | --- versions.cfg 2015-05-06 09:51:19 +0000 | |||
538 | +++ versions.cfg 2015-07-04 09:40:25 +0000 | |||
539 | @@ -35,7 +35,6 @@ | |||
540 | 35 | extras = 0.0.3 | 35 | extras = 0.0.3 |
541 | 36 | fixtures = 0.3.14 | 36 | fixtures = 0.3.14 |
542 | 37 | httplib2 = 0.8 | 37 | httplib2 = 0.8 |
543 | 38 | iscpy = 1.05 | ||
544 | 39 | iso8601 = 0.1.4 | 38 | iso8601 = 0.1.4 |
545 | 40 | junitxml = 0.6 | 39 | junitxml = 0.6 |
546 | 41 | nose = 1.3.1 | 40 | nose = 1.3.1 |
This is the same code that was approved for 1.7 and trunk, and landed on trunk. (Self-review might be appropriate in this circumstance?)