Merge lp:~smoser/curtin/add-arm64-support into lp:~newell-jensen/curtin/add-arm64-support
- add-arm64-support
- Merge into add-arm64-support
Proposed by
Scott Moser
Status: | Merged |
---|---|
Approved by: | Newell Jensen |
Approved revision: | 153 |
Merged at revision: | 147 |
Proposed branch: | lp:~smoser/curtin/add-arm64-support |
Merge into: | lp:~newell-jensen/curtin/add-arm64-support |
Diff against target: |
551 lines (+406/-31) 4 files modified
curtin/commands/block_meta.py (+40/-22) curtin/net/__init__.py (+114/-1) helpers/common (+8/-8) tests/unittests/test_net.py (+244/-0) |
To merge this branch: | bzr merge lp:~smoser/curtin/add-arm64-support |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Newell Jensen | Approve | ||
Review via email: mp+227619@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 153. By Scott Moser
-
merge with Newell's branch
Revision history for this message
Scott Moser (smoser) wrote : | # |
fixed merge conflicts.
Revision history for this message
Newell Jensen (newell-jensen) wrote : | # |
Looks good.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'curtin/commands/block_meta.py' |
2 | --- curtin/commands/block_meta.py 2014-07-17 17:39:59 +0000 |
3 | +++ curtin/commands/block_meta.py 2014-07-21 19:24:27 +0000 |
4 | @@ -23,8 +23,9 @@ |
5 | from . import populate_one_subcmd |
6 | |
7 | import os |
8 | -import tempfile |
9 | |
10 | +SIMPLE = 'simple' |
11 | +SIMPLE_BOOT = 'simple-boot' |
12 | |
13 | CMD_ARGUMENTS = ( |
14 | ((('-D', '--devices'), |
15 | @@ -34,16 +35,15 @@ |
16 | 'choices': ['ext4', 'ext3'], 'default': 'ext4'}), |
17 | ('--boot-fstype', {'help': 'boot partition filesystem type', |
18 | 'choices': ['ext4', 'ext3'], 'default': None}), |
19 | - ('mode', {'help': 'meta-mode to use', 'choices': ['raid0', 'simple', 'simple-boot']}), |
20 | + ('mode', {'help': 'meta-mode to use', |
21 | + 'choices': ['raid0', SIMPLE, SIMPLE_BOOT]}), |
22 | ) |
23 | ) |
24 | |
25 | |
26 | def block_meta(args): |
27 | # main entry point for the block-meta command. |
28 | - if args.mode == "simple": |
29 | - meta_simple(args) |
30 | - elif args.mode == "simple-boot": |
31 | + if args.mode in (SIMPLE, SIMPLE_BOOT): |
32 | meta_simple(args) |
33 | else: |
34 | raise NotImplementedError("mode=%s is not implemenbed" % args.mode) |
35 | @@ -68,6 +68,25 @@ |
36 | return block.get_root_device([devname, ]) |
37 | |
38 | |
39 | +def get_bootpt_cfg(cfg, enabled=False, fstype=None): |
40 | + # 'cfg' looks like: |
41 | + # enabled: boolean |
42 | + # fstype: filesystem type (default to 'fstype') |
43 | + # label: filesystem label (default to 'boot') |
44 | + # size: filesystem size in M (default to 512) |
45 | + # parm enable can enable, but not disable |
46 | + # parm fstype overrides cfg['fstype'] |
47 | + ret = {'enabled': False, 'fstype': None, 'size': 512, 'label': 'boot'} |
48 | + ret.update(cfg) |
49 | + if enabled: |
50 | + ret['enabled'] = True |
51 | + if ret['enabled']: |
52 | + if fstype and not ret['fstype']: |
53 | + ret['fstype'] = fstype |
54 | + ret['size'] = int(ret['size']) |
55 | + return ret |
56 | + |
57 | + |
58 | def meta_simple(args): |
59 | """Creates a root partition. If args.mode == 'simple-boot', it will also |
60 | create a separate /boot partition. |
61 | @@ -80,6 +99,10 @@ |
62 | if devices is None: |
63 | devices = cfg.get('block-meta', {}).get('devices', []) |
64 | |
65 | + bootpt = get_bootpt_cfg( |
66 | + cfg.get('block-meta', {}).get('boot-partition', {}), |
67 | + enabled=bool(args.mode is SIMPLE_BOOT), fstype=args.boot_fstype) |
68 | + |
69 | # Remove duplicates but maintain ordering. |
70 | devices = list(OrderedDict.fromkeys(devices)) |
71 | |
72 | @@ -110,7 +133,6 @@ |
73 | sources = cfg.get('sources', {}) |
74 | dd_images = util.get_dd_images(sources) |
75 | |
76 | - # dd images are only for Windows, not Linux |
77 | if len(dd_images): |
78 | # we have at least one dd-able image |
79 | # we will only take the first one |
80 | @@ -123,10 +145,9 @@ |
81 | logtime( |
82 | "partition --format uefi %s" % devnode, |
83 | util.subp, ("partition", "--format", "uefi", devnode)) |
84 | - elif args.mode == 'simple-boot': |
85 | - logtime( |
86 | - "partition %s" % devnode, |
87 | - util.subp, ("partition", "--boot", devnode)) |
88 | + elif bootpt['enabled']: |
89 | + logtime("partition %s" % devnode, |
90 | + util.subp, ("partition", "--boot", devnode)) |
91 | bootdev = devnode + "1" |
92 | rootdev = devnode + "2" |
93 | else: |
94 | @@ -140,25 +161,22 @@ |
95 | logtime(' '.join(cmd), util.subp, cmd) |
96 | util.subp(['mount', rootdev, state['target']]) |
97 | |
98 | - if args.mode == 'simple-boot': |
99 | + if bootpt['enabled']: |
100 | # create 'boot' directory in state['target'] |
101 | boot_dir = os.path.join(state['target'], 'boot') |
102 | util.subp(['mkdir', boot_dir]) |
103 | # mkfs for boot partition and mount |
104 | - if args.boot_fstype: |
105 | - cmd = ['mkfs.%s' % args.boot_fstype, '-q', '-L', 'cloudimg-bootfs', bootdev] |
106 | - else: |
107 | - cmd = ['mkfs.%s' % args.fstype, '-q', '-L', 'cloudimg-bootfs', bootdev] |
108 | + cmd = ['mkfs.%s' % bootpt['fstype'], |
109 | + '-q', '-L', bootpt['label'], bootdev] |
110 | logtime(' '.join(cmd), util.subp, cmd) |
111 | util.subp(['mount', bootdev, boot_dir]) |
112 | - |
113 | + |
114 | with open(state['fstab'], "w") as fp: |
115 | - if args.mode == 'simple-boot': |
116 | - if args.boot_fstype: |
117 | - fp.write("LABEL=%s /boot %s defaults 0 0\n" % ('cloudimg-bootfs', args.boot_fstype)) |
118 | - else: |
119 | - fp.write("LABEL=%s /boot %s defaults 0 0\n" % ('cloudimg-bootfs', args.fstype)) |
120 | - fp.write("LABEL=%s / %s defaults 0 0\n" % ('cloudimg-rootfs', args.fstype)) |
121 | + if bootpt['enabled']: |
122 | + fp.write("LABEL=%s /boot %s defaults 0 0\n" % |
123 | + (bootpt['label'], bootpt['fstype'])) |
124 | + fp.write("LABEL=%s / %s defaults 0 0\n" % |
125 | + ('cloudimg-rootfs', args.fstype)) |
126 | |
127 | return 0 |
128 | |
129 | |
130 | === modified file 'curtin/net/__init__.py' |
131 | --- curtin/net/__init__.py 2014-03-25 23:13:46 +0000 |
132 | +++ curtin/net/__init__.py 2014-07-21 19:24:27 +0000 |
133 | @@ -1,6 +1,7 @@ |
134 | -# Copyright (C) 2013 Canonical Ltd. |
135 | +# Copyright (C) 2013-2014 Canonical Ltd. |
136 | # |
137 | # Author: Scott Moser <scott.moser@canonical.com> |
138 | +# Author: Blake Rouse <blake.rouse@canonical.com> |
139 | # |
140 | # Curtin is free software: you can redistribute it and/or modify it under |
141 | # the terms of the GNU Affero General Public License as published by the |
142 | @@ -22,6 +23,22 @@ |
143 | |
144 | SYS_CLASS_NET = "/sys/class/net/" |
145 | |
146 | +NET_CONFIG_OPTIONS = [ |
147 | + "address", "netmask", "broadcast", "network", "metric", "gateway", |
148 | + "pointtopoint", "media", "mtu", "hostname", "leasehours", "leasetime", |
149 | + "vendor", "client", "bootfile", "server", "hwaddr", "provider", "frame", |
150 | + "netnum", "endpoint", "local", "ttl", |
151 | + ] |
152 | + |
153 | +NET_CONFIG_COMMANDS = [ |
154 | + "pre-up", "up", "post-up", "down", "pre-down", "post-down", |
155 | + ] |
156 | + |
157 | +NET_CONFIG_BRIDGE_OPTIONS = [ |
158 | + "bridge_ageing", "bridge_bridgeprio", "bridge_fd", "bridge_gcinit", |
159 | + "bridge_hello", "bridge_maxage", "bridge_maxwait", "bridge_stp", |
160 | + ] |
161 | + |
162 | |
163 | def sys_dev_path(devname, path=""): |
164 | return SYS_CLASS_NET + devname + "/" + path |
165 | @@ -95,4 +112,100 @@ |
166 | return os.listdir(SYS_CLASS_NET) |
167 | |
168 | |
169 | +class ParserError(Exception): |
170 | + """Raised when parser has issue parsing the interfaces file.""" |
171 | + |
172 | + |
173 | +def parse_deb_config_data(ifaces, contents, path): |
174 | + """Parses the file contents, placing result into ifaces. |
175 | + |
176 | + :param ifaces: interface dictionary |
177 | + :param contents: contents of interfaces file |
178 | + :param path: directory interfaces file was located |
179 | + """ |
180 | + currif = None |
181 | + src_dir = path |
182 | + for line in contents.splitlines(): |
183 | + line = line.strip() |
184 | + if line.startswith('#'): |
185 | + continue |
186 | + split = line.split(' ') |
187 | + option = split[0] |
188 | + if option == "source-directory": |
189 | + src_dir = os.path.join(path, split[1]) |
190 | + elif option == "source": |
191 | + src_path = os.path.join(src_dir, split[1]) |
192 | + with open(src_path, "r") as fp: |
193 | + src_data = fp.read().strip() |
194 | + parse_deb_config_data( |
195 | + ifaces, src_data, |
196 | + os.path.dirname(os.path.abspath(src_path))) |
197 | + elif option == "auto": |
198 | + for iface in split[1:]: |
199 | + if iface not in ifaces: |
200 | + ifaces[iface] = {} |
201 | + ifaces[iface]['auto'] = True |
202 | + elif option == "iface": |
203 | + iface, family, method = split[1:4] |
204 | + if iface not in ifaces: |
205 | + ifaces[iface] = {} |
206 | + elif 'family' in ifaces[iface]: |
207 | + raise ParserError("Cannot define %s interface again.") |
208 | + ifaces[iface]['family'] = family |
209 | + ifaces[iface]['method'] = method |
210 | + currif = iface |
211 | + elif option == "hwaddress": |
212 | + ifaces[currif]['hwaddress'] = split[1] |
213 | + elif option in NET_CONFIG_OPTIONS: |
214 | + ifaces[currif][option] = split[1] |
215 | + elif option in NET_CONFIG_COMMANDS: |
216 | + if option not in ifaces[currif]: |
217 | + ifaces[currif][option] = [] |
218 | + ifaces[currif][option].append(' '.join(split[1:])) |
219 | + elif option.startswith('dns-'): |
220 | + if 'dns' not in ifaces[currif]: |
221 | + ifaces[currif]['dns'] = {} |
222 | + if option == 'dns-search': |
223 | + ifaces[currif]['dns']['search'] = [] |
224 | + for domain in split[1:]: |
225 | + ifaces[currif]['dns']['search'].append(domain) |
226 | + elif option == 'dns-nameservers': |
227 | + ifaces[currif]['dns']['nameservers'] = [] |
228 | + for server in split[1:]: |
229 | + ifaces[currif]['dns']['nameservers'].append(server) |
230 | + elif option.startswith('bridge_'): |
231 | + if 'bridge' not in ifaces[currif]: |
232 | + ifaces[currif]['bridge'] = {} |
233 | + if option in NET_CONFIG_BRIDGE_OPTIONS: |
234 | + bridge_option = option.replace('bridge_', '') |
235 | + ifaces[currif]['bridge'][bridge_option] = split[1] |
236 | + elif option == "bridge_ports": |
237 | + ifaces[currif]['bridge']['ports'] = [] |
238 | + for iface in split[1:]: |
239 | + ifaces[currif]['bridge']['ports'].append(iface) |
240 | + elif option == "bridge_hw" and split[1].lower() == "mac": |
241 | + ifaces[currif]['bridge']['mac'] = split[2] |
242 | + elif option == "bridge_pathcost": |
243 | + if 'pathcost' not in ifaces[currif]['bridge']: |
244 | + ifaces[currif]['bridge']['pathcost'] = {} |
245 | + ifaces[currif]['bridge']['pathcost'][split[1]] = split[2] |
246 | + elif option == "bridge_portprio": |
247 | + if 'portprio' not in ifaces[currif]['bridge']: |
248 | + ifaces[currif]['bridge']['portprio'] = {} |
249 | + ifaces[currif]['bridge']['portprio'][split[1]] = split[2] |
250 | + for iface in ifaces.keys(): |
251 | + if 'auto' not in ifaces[iface]: |
252 | + ifaces[iface]['auto'] = False |
253 | + |
254 | + |
255 | +def parse_deb_config(path): |
256 | + """Parses a debian network configuration file.""" |
257 | + ifaces = {} |
258 | + with open(path, "r") as fp: |
259 | + contents = fp.read().strip() |
260 | + parse_deb_config_data( |
261 | + ifaces, contents, |
262 | + os.path.dirname(os.path.abspath(path))) |
263 | + return ifaces |
264 | + |
265 | # vi: ts=4 expandtab syntax=python |
266 | |
267 | === modified file 'helpers/common' |
268 | --- helpers/common 2014-07-21 17:57:28 +0000 |
269 | +++ helpers/common 2014-07-21 19:24:27 +0000 |
270 | @@ -76,10 +76,10 @@ |
271 | # This is currently the default size we have for the /boot partition |
272 | if [ "$boot" -ge 1 ]; then |
273 | maxend=$((($size/512)-$start-$bootsize)) |
274 | - if [ $maxend -lt 0 ]; then |
275 | - { error "Disk is not big enough for /boot partition on $target"; |
276 | - return 1; } |
277 | - fi |
278 | + if [ $maxend -lt 0 ]; then |
279 | + error "Disk is not big enough for /boot partition on $target"; |
280 | + return 1; |
281 | + fi |
282 | else |
283 | maxend=$((($size/512)-$start)) |
284 | fi |
285 | @@ -89,13 +89,13 @@ |
286 | [ -b "$target" ] && isblk=true |
287 | |
288 | if [ "$boot" -ge 1 ]; then |
289 | - # Creating 'efi', '/boot' and '/' partitions |
290 | + # Creating 'efi', '/boot' and '/' partitions |
291 | sgdisk --new "15:$start:+1M" --typecode=15:ef02 \ |
292 | - --new "1:+1M:+513M" --typecode=1:8300 \ |
293 | - --new "2::$end" --typecode=2:8300 "$target" || |
294 | + --new "1:+1M:+513M" --typecode=1:8300 \ |
295 | + --new "2::$end" --typecode=2:8300 "$target" || |
296 | { error "failed to gpt partition $target"; return 1; } |
297 | else |
298 | - # Creating 'efi' and '/' partitions |
299 | + # Creating 'efi' and '/' partitions |
300 | sgdisk --new "15:2048:+1M" --typecode=15:ef02 \ |
301 | --new "1::$end" --typecode=1:8300 "$target" || |
302 | { error "failed to gpt partition $target"; return 1; } |
303 | |
304 | === added file 'tests/unittests/test_net.py' |
305 | --- tests/unittests/test_net.py 1970-01-01 00:00:00 +0000 |
306 | +++ tests/unittests/test_net.py 2014-07-21 19:24:27 +0000 |
307 | @@ -0,0 +1,244 @@ |
308 | +from unittest import TestCase |
309 | +import os |
310 | +import shutil |
311 | +import tempfile |
312 | + |
313 | +from curtin import net |
314 | +from textwrap import dedent |
315 | + |
316 | + |
317 | +class TestNetParserData(TestCase): |
318 | + |
319 | + def test_parse_deb_config_data_ignores_comments(self): |
320 | + contents = dedent("""\ |
321 | + # ignore |
322 | + # iface eth0 inet static |
323 | + # address 192.168.1.1 |
324 | + """) |
325 | + ifaces = {} |
326 | + net.parse_deb_config_data(ifaces, contents, '') |
327 | + self.assertEqual({}, ifaces) |
328 | + |
329 | + def test_parse_deb_config_data_basic(self): |
330 | + contents = dedent("""\ |
331 | + iface eth0 inet static |
332 | + address 192.168.1.2 |
333 | + netmask 255.255.255.0 |
334 | + hwaddress aa:bb:cc:dd:ee:ff |
335 | + """) |
336 | + ifaces = {} |
337 | + net.parse_deb_config_data(ifaces, contents, '') |
338 | + self.assertEqual({ |
339 | + 'eth0': { |
340 | + 'auto': False, |
341 | + 'family': 'inet', |
342 | + 'method': 'static', |
343 | + 'address': '192.168.1.2', |
344 | + 'netmask': '255.255.255.0', |
345 | + 'hwaddress': 'aa:bb:cc:dd:ee:ff', |
346 | + }, |
347 | + }, ifaces) |
348 | + |
349 | + def test_parse_deb_config_data_auto(self): |
350 | + contents = dedent("""\ |
351 | + auto eth0 eth1 |
352 | + iface eth0 inet manual |
353 | + iface eth1 inet manual |
354 | + """) |
355 | + ifaces = {} |
356 | + net.parse_deb_config_data(ifaces, contents, '') |
357 | + self.assertEqual({ |
358 | + 'eth0': { |
359 | + 'auto': True, |
360 | + 'family': 'inet', |
361 | + 'method': 'manual', |
362 | + }, |
363 | + 'eth1': { |
364 | + 'auto': True, |
365 | + 'family': 'inet', |
366 | + 'method': 'manual', |
367 | + }, |
368 | + }, ifaces) |
369 | + |
370 | + def test_parse_deb_config_data_error_on_redefine(self): |
371 | + contents = dedent("""\ |
372 | + iface eth0 inet static |
373 | + address 192.168.1.2 |
374 | + iface eth0 inet static |
375 | + address 192.168.1.3 |
376 | + """) |
377 | + ifaces = {} |
378 | + self.assertRaises( |
379 | + net.ParserError, |
380 | + net.parse_deb_config_data, ifaces, contents, '') |
381 | + |
382 | + def test_parse_deb_config_data_commands(self): |
383 | + contents = dedent("""\ |
384 | + iface eth0 inet manual |
385 | + pre-up preup1 |
386 | + pre-up preup2 |
387 | + up up1 |
388 | + post-up postup1 |
389 | + pre-down predown1 |
390 | + down down1 |
391 | + down down2 |
392 | + post-down postdown1 |
393 | + """) |
394 | + ifaces = {} |
395 | + net.parse_deb_config_data(ifaces, contents, '') |
396 | + self.assertEqual({ |
397 | + 'eth0': { |
398 | + 'auto': False, |
399 | + 'family': 'inet', |
400 | + 'method': 'manual', |
401 | + 'pre-up': ['preup1', 'preup2'], |
402 | + 'up': ['up1'], |
403 | + 'post-up': ['postup1'], |
404 | + 'pre-down': ['predown1'], |
405 | + 'down': ['down1', 'down2'], |
406 | + 'post-down': ['postdown1'], |
407 | + }, |
408 | + }, ifaces) |
409 | + |
410 | + def test_parse_deb_config_data_dns(self): |
411 | + contents = dedent("""\ |
412 | + iface eth0 inet static |
413 | + dns-nameservers 192.168.1.1 192.168.1.2 |
414 | + dns-search curtin local |
415 | + """) |
416 | + ifaces = {} |
417 | + net.parse_deb_config_data(ifaces, contents, '') |
418 | + self.assertEqual({ |
419 | + 'eth0': { |
420 | + 'auto': False, |
421 | + 'family': 'inet', |
422 | + 'method': 'static', |
423 | + 'dns': { |
424 | + 'nameservers': ['192.168.1.1', '192.168.1.2'], |
425 | + 'search': ['curtin', 'local'], |
426 | + }, |
427 | + }, |
428 | + }, ifaces) |
429 | + |
430 | + def test_parse_deb_config_data_bridge(self): |
431 | + contents = dedent("""\ |
432 | + iface eth0 inet manual |
433 | + iface eth1 inet manual |
434 | + iface br0 inet static |
435 | + address 192.168.1.1 |
436 | + netmask 255.255.255.0 |
437 | + bridge_maxwait 30 |
438 | + bridge_ports eth0 eth1 |
439 | + bridge_pathcost eth0 1 |
440 | + bridge_pathcost eth1 2 |
441 | + bridge_portprio eth0 0 |
442 | + bridge_portprio eth1 1 |
443 | + """) |
444 | + ifaces = {} |
445 | + net.parse_deb_config_data(ifaces, contents, '') |
446 | + self.assertEqual({ |
447 | + 'eth0': { |
448 | + 'auto': False, |
449 | + 'family': 'inet', |
450 | + 'method': 'manual', |
451 | + }, |
452 | + 'eth1': { |
453 | + 'auto': False, |
454 | + 'family': 'inet', |
455 | + 'method': 'manual', |
456 | + }, |
457 | + 'br0': { |
458 | + 'auto': False, |
459 | + 'family': 'inet', |
460 | + 'method': 'static', |
461 | + 'address': '192.168.1.1', |
462 | + 'netmask': '255.255.255.0', |
463 | + 'bridge': { |
464 | + 'maxwait': '30', |
465 | + 'ports': ['eth0', 'eth1'], |
466 | + 'pathcost': { |
467 | + 'eth0': '1', |
468 | + 'eth1': '2', |
469 | + }, |
470 | + 'portprio': { |
471 | + 'eth0': '0', |
472 | + 'eth1': '1' |
473 | + }, |
474 | + }, |
475 | + }, |
476 | + }, ifaces) |
477 | + |
478 | + |
479 | +class TestNetParser(TestCase): |
480 | + |
481 | + def setUp(self): |
482 | + self.target = tempfile.mkdtemp() |
483 | + |
484 | + def tearDown(self): |
485 | + shutil.rmtree(self.target) |
486 | + |
487 | + def make_config(self, path=None, name=None, contents=None, |
488 | + parse=True): |
489 | + if path is None: |
490 | + path = self.target |
491 | + if name is None: |
492 | + name = 'interfaces' |
493 | + path = os.path.join(path, name) |
494 | + if contents is None: |
495 | + contents = dedent("""\ |
496 | + auto eth0 |
497 | + iface eth0 inet static |
498 | + address 192.168.1.2 |
499 | + netmask 255.255.255.0 |
500 | + hwaddress aa:bb:cc:dd:ee:ff |
501 | + """) |
502 | + with open(path, 'w') as stream: |
503 | + stream.write(contents) |
504 | + ifaces = None |
505 | + if parse: |
506 | + ifaces = {} |
507 | + net.parse_deb_config_data(ifaces, contents, '') |
508 | + return path, ifaces |
509 | + |
510 | + def test_parse_deb_config(self): |
511 | + path, data = self.make_config() |
512 | + expected = net.parse_deb_config(path) |
513 | + self.assertEqual(data, expected) |
514 | + |
515 | + def test_parse_deb_config_source(self): |
516 | + path, data = self.make_config(name='interfaces2') |
517 | + contents = dedent("""\ |
518 | + source interfaces2 |
519 | + iface eth1 inet manual |
520 | + """) |
521 | + i_path, _ = self.make_config( |
522 | + contents=contents, parse=False) |
523 | + data['eth1'] = { |
524 | + 'auto': False, |
525 | + 'family': 'inet', |
526 | + 'method': 'manual', |
527 | + } |
528 | + expected = net.parse_deb_config(i_path) |
529 | + self.assertEqual(data, expected) |
530 | + |
531 | + def test_parse_deb_config_source_dir(self): |
532 | + subdir = os.path.join(self.target, 'interfaces.d') |
533 | + os.mkdir(subdir) |
534 | + path, data = self.make_config( |
535 | + path=subdir, name='interfaces2') |
536 | + contents = dedent("""\ |
537 | + source-directory interfaces.d |
538 | + source interfaces2 |
539 | + iface eth1 inet manual |
540 | + """) |
541 | + i_path, _ = self.make_config( |
542 | + contents=contents, parse=False) |
543 | + data['eth1'] = { |
544 | + 'auto': False, |
545 | + 'family': 'inet', |
546 | + 'method': 'manual', |
547 | + } |
548 | + expected = net.parse_deb_config(i_path) |
549 | + self.assertEqual(data, expected) |
550 | + |
551 | +# vi: ts=4 expandtab syntax=python |
Need to fix merge conflict.