Merge lp:~raharper/curtin/fix-network-package-install into lp:~curtin-dev/curtin/trunk
- fix-network-package-install
- Merge into trunk
Proposed by
Ryan Harper
Status: | Merged |
---|---|
Merged at revision: | 269 |
Proposed branch: | lp:~raharper/curtin/fix-network-package-install |
Merge into: | lp:~curtin-dev/curtin/trunk |
Diff against target: |
476 lines (+339/-23) 6 files modified
curtin/commands/curthooks.py (+32/-17) curtin/net/__init__.py (+15/-2) curtin/net/network_state.py (+28/-0) examples/tests/bonding_network.yaml (+27/-0) tests/unittests/test_net.py (+2/-4) tests/vmtests/test_bonding.py (+235/-0) |
To merge this branch: | bzr merge lp:~raharper/curtin/fix-network-package-install |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Scott Moser (community) | Approve | ||
Review via email: mp+270212@code.launchpad.net |
Commit message
Description of the change
- Fixes lp:1491994
- Enforce order on rendering of /etc/network/
- Don't emit hwaddress for anything except bonds or bridges
- Introduce a bonding vmtest
- lint and unit-test fixes.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'curtin/commands/curthooks.py' | |||
2 | --- curtin/commands/curthooks.py 2015-09-04 17:01:28 +0000 | |||
3 | +++ curtin/commands/curthooks.py 2015-09-04 19:23:11 +0000 | |||
4 | @@ -573,24 +573,39 @@ | |||
5 | 573 | update_initramfs(target, all_kernels=True) | 573 | update_initramfs(target, all_kernels=True) |
6 | 574 | 574 | ||
7 | 575 | 575 | ||
17 | 576 | def install_missing_storage_packages(cfg, target): | 576 | def install_missing_packages(cfg, target): |
18 | 577 | # Do nothing if no custom storage. | 577 | ''' describe which operation types will require specific packages |
19 | 578 | if 'storage' not in cfg: | 578 | |
20 | 579 | return | 579 | 'custom_config_key': { |
21 | 580 | 580 | 'pkg1': ['op_name_1', 'op_name_2', ...] | |
22 | 581 | all_types = set( | 581 | } |
23 | 582 | operation['type'] | 582 | ''' |
24 | 583 | for operation in cfg['storage']['config'] | 583 | custom_configs = { |
25 | 584 | ) | 584 | 'storage': { |
26 | 585 | 'lvm2': ['lvm_volgroup', 'lvm_partition'], | ||
27 | 586 | 'mdadm': ['raid'], | ||
28 | 587 | 'bcache-tools': ['bcache']}, | ||
29 | 588 | 'network': { | ||
30 | 589 | 'vlan': ['vlan'], | ||
31 | 590 | 'ifenslave': ['bond'], | ||
32 | 591 | 'bridge-utils': ['bridge']}, | ||
33 | 592 | } | ||
34 | 593 | |||
35 | 585 | needed_packages = [] | 594 | needed_packages = [] |
36 | 586 | installed_packages = get_installed_packages(target) | 595 | installed_packages = get_installed_packages(target) |
44 | 587 | if ('lvm_volgroup' in all_types or 'lvm_partition' in all_types) and \ | 596 | for cust_cfg, pkg_reqs in custom_configs.items(): |
45 | 588 | 'lvm2' not in installed_packages: | 597 | if cust_cfg not in cfg: |
46 | 589 | needed_packages.append('lvm2') | 598 | continue |
47 | 590 | if 'raid' in all_types and 'mdadm' not in installed_packages: | 599 | |
48 | 591 | needed_packages.append('mdadm') | 600 | all_types = set( |
49 | 592 | if 'bcache' in all_types and 'bcache-tools' not in installed_packages: | 601 | operation['type'] |
50 | 593 | needed_packages.append('bcache-tools') | 602 | for operation in cfg[cust_cfg]['config'] |
51 | 603 | ) | ||
52 | 604 | for pkg, types in pkg_reqs.items(): | ||
53 | 605 | if set(types).intersection(all_types) and \ | ||
54 | 606 | pkg not in installed_packages: | ||
55 | 607 | needed_packages.append(pkg) | ||
56 | 608 | |||
57 | 594 | if needed_packages: | 609 | if needed_packages: |
58 | 595 | util.install_packages(needed_packages, target=target) | 610 | util.install_packages(needed_packages, target=target) |
59 | 596 | 611 | ||
60 | @@ -630,7 +645,7 @@ | |||
61 | 630 | 645 | ||
62 | 631 | detect_and_handle_multipath(cfg, target) | 646 | detect_and_handle_multipath(cfg, target) |
63 | 632 | 647 | ||
65 | 633 | install_missing_storage_packages(cfg, target) | 648 | install_missing_packages(cfg, target) |
66 | 634 | 649 | ||
67 | 635 | # If a crypttab file was created by block_meta than it needs to be copied | 650 | # If a crypttab file was created by block_meta than it needs to be copied |
68 | 636 | # onto the target system, and update_initramfs() needs to be run, so that | 651 | # onto the target system, and update_initramfs() needs to be run, so that |
69 | 637 | 652 | ||
70 | === modified file 'curtin/net/__init__.py' | |||
71 | --- curtin/net/__init__.py 2015-09-02 13:55:39 +0000 | |||
72 | +++ curtin/net/__init__.py 2015-09-04 19:23:11 +0000 | |||
73 | @@ -265,7 +265,6 @@ | |||
74 | 265 | 'metric', | 265 | 'metric', |
75 | 266 | 'gateway', | 266 | 'gateway', |
76 | 267 | 'pointopoint', | 267 | 'pointopoint', |
77 | 268 | 'hwaddress', | ||
78 | 269 | 'mtu', | 268 | 'mtu', |
79 | 270 | 'scope', | 269 | 'scope', |
80 | 271 | ] | 270 | ] |
81 | @@ -289,6 +288,9 @@ | |||
82 | 289 | 'index', | 288 | 'index', |
83 | 290 | 'subnets', | 289 | 'subnets', |
84 | 291 | ] | 290 | ] |
85 | 291 | if iface['type'] not in ['bond', 'bridge']: | ||
86 | 292 | ignore_map.append('mac_address') | ||
87 | 293 | |||
88 | 292 | for key, value in iface.items(): | 294 | for key, value in iface.items(): |
89 | 293 | if value and key not in ignore_map: | 295 | if value and key not in ignore_map: |
90 | 294 | if type(value) == list: | 296 | if type(value) == list: |
91 | @@ -319,7 +321,18 @@ | |||
92 | 319 | 321 | ||
93 | 320 | content = "" | 322 | content = "" |
94 | 321 | interfaces = network_state.get('interfaces') | 323 | interfaces = network_state.get('interfaces') |
96 | 322 | for iface in interfaces.values(): | 324 | ''' Apply a sort order to ensure that we write out |
97 | 325 | the physical interfaces first; this is critical for | ||
98 | 326 | bonding | ||
99 | 327 | ''' | ||
100 | 328 | order = { | ||
101 | 329 | 'physical': 0, | ||
102 | 330 | 'bond': 1, | ||
103 | 331 | 'bridge': 2, | ||
104 | 332 | 'vlan': 3, | ||
105 | 333 | } | ||
106 | 334 | for iface in sorted(interfaces.values(), | ||
107 | 335 | key=lambda k: (order[k['type']], k['name'])): | ||
108 | 323 | content += "auto {name}\n".format(**iface) | 336 | content += "auto {name}\n".format(**iface) |
109 | 324 | 337 | ||
110 | 325 | subnets = iface.get('subnets', {}) | 338 | subnets = iface.get('subnets', {}) |
111 | 326 | 339 | ||
112 | === modified file 'curtin/net/network_state.py' | |||
113 | --- curtin/net/network_state.py 2015-09-02 13:55:39 +0000 | |||
114 | +++ curtin/net/network_state.py 2015-09-04 19:23:11 +0000 | |||
115 | @@ -116,6 +116,8 @@ | |||
116 | 116 | 116 | ||
117 | 117 | interfaces = self.network_state.get('interfaces') | 117 | interfaces = self.network_state.get('interfaces') |
118 | 118 | iface = interfaces.get(command['name'], {}) | 118 | iface = interfaces.get(command['name'], {}) |
119 | 119 | for param, val in command.get('params', {}).items(): | ||
120 | 120 | iface.update({param: val}) | ||
121 | 119 | iface.update({ | 121 | iface.update({ |
122 | 120 | 'name': command.get('name'), | 122 | 'name': command.get('name'), |
123 | 121 | 'type': command.get('type'), | 123 | 'type': command.get('type'), |
124 | @@ -160,15 +162,20 @@ | |||
125 | 160 | #/etc/network/interfaces | 162 | #/etc/network/interfaces |
126 | 161 | auto eth0 | 163 | auto eth0 |
127 | 162 | iface eth0 inet manual | 164 | iface eth0 inet manual |
128 | 165 | bond-master bond0 | ||
129 | 166 | bond-mode 802.3ad | ||
130 | 163 | 167 | ||
131 | 164 | auto eth1 | 168 | auto eth1 |
132 | 165 | iface eth1 inet manual | 169 | iface eth1 inet manual |
133 | 170 | bond-master bond0 | ||
134 | 171 | bond-mode 802.3ad | ||
135 | 166 | 172 | ||
136 | 167 | auto bond0 | 173 | auto bond0 |
137 | 168 | iface bond0 inet static | 174 | iface bond0 inet static |
138 | 169 | address 192.168.0.10 | 175 | address 192.168.0.10 |
139 | 170 | gateway 192.168.0.1 | 176 | gateway 192.168.0.1 |
140 | 171 | netmask 255.255.255.0 | 177 | netmask 255.255.255.0 |
141 | 178 | bond-slaves none | ||
142 | 172 | bond-mode 802.3ad | 179 | bond-mode 802.3ad |
143 | 173 | bond-miimon 100 | 180 | bond-miimon 100 |
144 | 174 | bond-downdelay 200 | 181 | bond-downdelay 200 |
145 | @@ -190,6 +197,7 @@ | |||
146 | 190 | iface = interfaces.get(command.get('name'), {}) | 197 | iface = interfaces.get(command.get('name'), {}) |
147 | 191 | for param, val in command.get('params').items(): | 198 | for param, val in command.get('params').items(): |
148 | 192 | iface.update({param: val}) | 199 | iface.update({param: val}) |
149 | 200 | iface.update({'bond-slaves': 'none'}) | ||
150 | 193 | self.network_state['interfaces'].update({iface['name']: iface}) | 201 | self.network_state['interfaces'].update({iface['name']: iface}) |
151 | 194 | 202 | ||
152 | 195 | # handle bond slaves | 203 | # handle bond slaves |
153 | @@ -205,6 +213,9 @@ | |||
154 | 205 | interfaces = self.network_state.get('interfaces') | 213 | interfaces = self.network_state.get('interfaces') |
155 | 206 | bond_if = interfaces.get(ifname) | 214 | bond_if = interfaces.get(ifname) |
156 | 207 | bond_if['bond-master'] = command.get('name') | 215 | bond_if['bond-master'] = command.get('name') |
157 | 216 | # copy in bond config into slave | ||
158 | 217 | for param, val in command.get('params').items(): | ||
159 | 218 | bond_if.update({param: val}) | ||
160 | 208 | self.network_state['interfaces'].update({ifname: bond_if}) | 219 | self.network_state['interfaces'].update({ifname: bond_if}) |
161 | 209 | 220 | ||
162 | 210 | def handle_bridge(self, command): | 221 | def handle_bridge(self, command): |
163 | @@ -354,7 +365,24 @@ | |||
164 | 354 | print("NS1 == NS2 ?=> {}".format( | 365 | print("NS1 == NS2 ?=> {}".format( |
165 | 355 | ns1.network_state == ns2.network_state)) | 366 | ns1.network_state == ns2.network_state)) |
166 | 356 | 367 | ||
167 | 368 | def test_output(network_config): | ||
168 | 369 | (version, config) = load_config(network_config) | ||
169 | 370 | ns1 = NetworkState(version=version, config=config) | ||
170 | 371 | ns1.parse_config() | ||
171 | 372 | random.shuffle(config) | ||
172 | 373 | ns2 = NetworkState(version=version, config=config) | ||
173 | 374 | ns2.parse_config() | ||
174 | 375 | print("NS1 == NS2 ?=> {}".format( | ||
175 | 376 | ns1.network_state == ns2.network_state)) | ||
176 | 377 | eni_1 = net.render_interfaces(ns1.network_state) | ||
177 | 378 | eni_2 = net.render_interfaces(ns2.network_state) | ||
178 | 379 | print(eni_1) | ||
179 | 380 | print(eni_2) | ||
180 | 381 | print("eni_1 == eni_2 ?=> {}".format( | ||
181 | 382 | eni_1 == eni_2)) | ||
182 | 383 | |||
183 | 357 | y = curtin_config.load_config(sys.argv[1]) | 384 | y = curtin_config.load_config(sys.argv[1]) |
184 | 358 | network_config = y.get('network') | 385 | network_config = y.get('network') |
185 | 359 | test_parse(network_config) | 386 | test_parse(network_config) |
186 | 360 | test_dump_and_load(network_config) | 387 | test_dump_and_load(network_config) |
187 | 388 | test_output(network_config) | ||
188 | 361 | 389 | ||
189 | === added file 'examples/tests/bonding_network.yaml' | |||
190 | --- examples/tests/bonding_network.yaml 1970-01-01 00:00:00 +0000 | |||
191 | +++ examples/tests/bonding_network.yaml 2015-09-04 19:23:11 +0000 | |||
192 | @@ -0,0 +1,27 @@ | |||
193 | 1 | network: | ||
194 | 2 | version: 1 | ||
195 | 3 | config: | ||
196 | 4 | # Physical interfaces. | ||
197 | 5 | - type: physical | ||
198 | 6 | name: eth0 | ||
199 | 7 | mac_address: "52:54:00:12:34:00" | ||
200 | 8 | subnets: | ||
201 | 9 | - type: dhcp4 | ||
202 | 10 | - type: physical | ||
203 | 11 | name: eth1 | ||
204 | 12 | mac_address: "52:54:00:12:34:02" | ||
205 | 13 | - type: physical | ||
206 | 14 | name: eth2 | ||
207 | 15 | mac_address: "52:54:00:12:34:04" | ||
208 | 16 | # Bond. | ||
209 | 17 | - type: bond | ||
210 | 18 | name: bond0 | ||
211 | 19 | mac_address: "52:54:00:12:34:06" | ||
212 | 20 | bond_interfaces: | ||
213 | 21 | - eth1 | ||
214 | 22 | - eth2 | ||
215 | 23 | params: | ||
216 | 24 | bond-mode: active-backup | ||
217 | 25 | subnets: | ||
218 | 26 | - type: static | ||
219 | 27 | address: 10.23.23.2/24 | ||
220 | 0 | 28 | ||
221 | === modified file 'tests/unittests/test_net.py' | |||
222 | --- tests/unittests/test_net.py 2015-09-02 13:55:39 +0000 | |||
223 | +++ tests/unittests/test_net.py 2015-09-04 19:23:11 +0000 | |||
224 | @@ -307,10 +307,8 @@ | |||
225 | 307 | 307 | ||
226 | 308 | def test_render_interfaces(self): | 308 | def test_render_interfaces(self): |
227 | 309 | ns = self.get_net_state() | 309 | ns = self.get_net_state() |
232 | 310 | ifaces = ('auto eth1\n' + 'iface eth1 inet manual\n' + | 310 | ifaces = ('auto eth0\n' + 'iface eth0 inet dhcp\n\n' + |
233 | 311 | ' hwaddress cf:d6:af:48:e8:80\n\n' + | 311 | 'auto eth1\n' + 'iface eth1 inet manual\n\n') |
230 | 312 | 'auto eth0\n' + 'iface eth0 inet dhcp\n' + | ||
231 | 313 | ' hwaddress c0:d6:9f:2c:e8:80\n\n') | ||
234 | 314 | net_ifaces = net.render_interfaces(ns.network_state) | 312 | net_ifaces = net.render_interfaces(ns.network_state) |
235 | 315 | self.assertEqual(sorted(ifaces.split('\n')), | 313 | self.assertEqual(sorted(ifaces.split('\n')), |
236 | 316 | sorted(net_ifaces.split('\n'))) | 314 | sorted(net_ifaces.split('\n'))) |
237 | 317 | 315 | ||
238 | === added file 'tests/vmtests/test_bonding.py' | |||
239 | --- tests/vmtests/test_bonding.py 1970-01-01 00:00:00 +0000 | |||
240 | +++ tests/vmtests/test_bonding.py 2015-09-04 19:23:11 +0000 | |||
241 | @@ -0,0 +1,235 @@ | |||
242 | 1 | from . import VMBaseClass | ||
243 | 2 | from unittest import TestCase | ||
244 | 3 | |||
245 | 4 | import ipaddress | ||
246 | 5 | import os | ||
247 | 6 | import re | ||
248 | 7 | import textwrap | ||
249 | 8 | import yaml | ||
250 | 9 | |||
251 | 10 | |||
252 | 11 | def iface_extract(input): | ||
253 | 12 | mo = re.search(r'^(?P<interface>\w+|\w+:\d+)\s+' + | ||
254 | 13 | r'Link encap:(?P<link_encap>\S+)\s+' + | ||
255 | 14 | r'(HWaddr\s+(?P<mac_address>\S+))?' + | ||
256 | 15 | r'(\s+inet addr:(?P<address>\S+))?' + | ||
257 | 16 | r'(\s+Bcast:(?P<broadcast>\S+)\s+)?' + | ||
258 | 17 | r'(Mask:(?P<netmask>\S+)\s+)?', | ||
259 | 18 | input, re.MULTILINE) | ||
260 | 19 | |||
261 | 20 | mtu = re.search(r'(\s+MTU:(?P<mtu>\d+)\s+)\s+', input, re.MULTILINE) | ||
262 | 21 | mtu_info = mtu.groupdict('') | ||
263 | 22 | mtu_info['mtu'] = int(mtu_info['mtu']) | ||
264 | 23 | |||
265 | 24 | if mo: | ||
266 | 25 | info = mo.groupdict('') | ||
267 | 26 | info['running'] = False | ||
268 | 27 | info['up'] = False | ||
269 | 28 | info['multicast'] = False | ||
270 | 29 | if 'RUNNING' in input: | ||
271 | 30 | info['running'] = True | ||
272 | 31 | if 'UP' in input: | ||
273 | 32 | info['up'] = True | ||
274 | 33 | if 'MULTICAST' in input: | ||
275 | 34 | info['multicast'] = True | ||
276 | 35 | info.update(mtu_info) | ||
277 | 36 | return info | ||
278 | 37 | return {} | ||
279 | 38 | |||
280 | 39 | |||
281 | 40 | def ifconfig_to_dict(ifconfig): | ||
282 | 41 | interfaces = {} | ||
283 | 42 | for iface in [iface_extract(iface) for iface in ifconfig.split('\n\n') | ||
284 | 43 | if iface.strip()]: | ||
285 | 44 | interfaces[iface['interface']] = iface | ||
286 | 45 | |||
287 | 46 | return interfaces | ||
288 | 47 | |||
289 | 48 | |||
290 | 49 | class TestNetworkAbs(VMBaseClass): | ||
291 | 50 | __test__ = False | ||
292 | 51 | interactive = False | ||
293 | 52 | conf_file = "examples/tests/bonding_network.yaml" | ||
294 | 53 | install_timeout = 600 | ||
295 | 54 | boot_timeout = 600 | ||
296 | 55 | extra_disks = [] | ||
297 | 56 | extra_nics = [] | ||
298 | 57 | user_data = textwrap.dedent("""\ | ||
299 | 58 | #cloud-config | ||
300 | 59 | password: passw0rd | ||
301 | 60 | chpasswd: { expire: False } | ||
302 | 61 | bootcmd: | ||
303 | 62 | - mkdir -p /media/output | ||
304 | 63 | - mount /dev/vdb /media/output | ||
305 | 64 | runcmd: | ||
306 | 65 | - ifconfig -a > /media/output/ifconfig_a | ||
307 | 66 | - cp -av /etc/network/interfaces /media/output | ||
308 | 67 | - cp -av /etc/udev/rules.d/70-persistent-net.rules /media/output | ||
309 | 68 | - ip -o route show > /media/output/ip_route_show | ||
310 | 69 | - route -n > /media/output/route_n | ||
311 | 70 | - dpkg-query -W -f '${Status}' ifenslave > \ | ||
312 | 71 | /media/output/ifenslave_installed | ||
313 | 72 | power_state: | ||
314 | 73 | mode: poweroff | ||
315 | 74 | """) | ||
316 | 75 | |||
317 | 76 | def test_output_files_exist(self): | ||
318 | 77 | self.output_files_exist(["ifconfig_a", | ||
319 | 78 | "interfaces", | ||
320 | 79 | "70-persistent-net.rules", | ||
321 | 80 | "ip_route_show", | ||
322 | 81 | "ifenslave_installed", | ||
323 | 82 | "route_n"]) | ||
324 | 83 | |||
325 | 84 | def test_ifenslave_installed(self): | ||
326 | 85 | with open(os.path.join(self.td.mnt, "ifenslave_installed")) as fp: | ||
327 | 86 | status = fp.read().strip() | ||
328 | 87 | print('ifenslave installed: {}'.format(status)) | ||
329 | 88 | self.assertEqual('install ok installed', status) | ||
330 | 89 | |||
331 | 90 | def test_etc_network_interfaces(self): | ||
332 | 91 | with open(os.path.join(self.td.mnt, "interfaces")) as fp: | ||
333 | 92 | eni = fp.read() | ||
334 | 93 | print('etc/network/interfaces:\n{}'.format(eni)) | ||
335 | 94 | |||
336 | 95 | expected_eni = self.get_expected_etc_network_interfaces() | ||
337 | 96 | eni_lines = eni.split('\n') | ||
338 | 97 | for line in expected_eni.split('\n'): | ||
339 | 98 | self.assertTrue(line in eni_lines) | ||
340 | 99 | |||
341 | 100 | def test_ifconfig_output(self): | ||
342 | 101 | '''check ifconfig output with test input''' | ||
343 | 102 | network_state = self.get_network_state() | ||
344 | 103 | print('expected_network_state:\n{}'.format( | ||
345 | 104 | yaml.dump(network_state, default_flow_style=False, indent=4))) | ||
346 | 105 | |||
347 | 106 | with open(os.path.join(self.td.mnt, "ifconfig_a")) as fp: | ||
348 | 107 | ifconfig_a = fp.read() | ||
349 | 108 | print('ifconfig -a:\n{}'.format(ifconfig_a)) | ||
350 | 109 | |||
351 | 110 | ifconfig_dict = ifconfig_to_dict(ifconfig_a) | ||
352 | 111 | print('parsed ifcfg dict:\n{}'.format( | ||
353 | 112 | yaml.dump(ifconfig_dict, default_flow_style=False, indent=4))) | ||
354 | 113 | |||
355 | 114 | with open(os.path.join(self.td.mnt, "ip_route_show")) as fp: | ||
356 | 115 | ip_route_show = fp.read() | ||
357 | 116 | print("ip route show:\n{}".format(ip_route_show)) | ||
358 | 117 | for line in [line for line in ip_route_show.split('\n') | ||
359 | 118 | if 'src' in line]: | ||
360 | 119 | m = re.search(r'^(?P<network>\S+)\sdev\s' + | ||
361 | 120 | r'(?P<devname>\S+)\s+' + | ||
362 | 121 | r'proto kernel\s+scope link' + | ||
363 | 122 | r'\s+src\s(?P<src_ip>\S+)', | ||
364 | 123 | line) | ||
365 | 124 | route_info = m.groupdict('') | ||
366 | 125 | print(route_info) | ||
367 | 126 | |||
368 | 127 | with open(os.path.join(self.td.mnt, "route_n")) as fp: | ||
369 | 128 | route_n = fp.read() | ||
370 | 129 | print("route -n:\n{}".format(route_n)) | ||
371 | 130 | |||
372 | 131 | interfaces = network_state.get('interfaces') | ||
373 | 132 | for iface in interfaces.values(): | ||
374 | 133 | subnets = iface.get('subnets', {}) | ||
375 | 134 | if subnets: | ||
376 | 135 | for index, subnet in zip(range(0, len(subnets)), subnets): | ||
377 | 136 | iface['index'] = index | ||
378 | 137 | if index == 0: | ||
379 | 138 | ifname = "{name}".format(**iface) | ||
380 | 139 | else: | ||
381 | 140 | ifname = "{name}:{index}".format(**iface) | ||
382 | 141 | |||
383 | 142 | self.check_interface(iface, | ||
384 | 143 | ifconfig_dict.get(ifname), | ||
385 | 144 | route_n) | ||
386 | 145 | else: | ||
387 | 146 | iface['index'] = 0 | ||
388 | 147 | self.check_interface(iface, | ||
389 | 148 | ifconfig_dict.get(iface['name']), | ||
390 | 149 | route_n) | ||
391 | 150 | |||
392 | 151 | def check_interface(self, iface, ifconfig, route_n): | ||
393 | 152 | print('testing iface:\n{}\n\nifconfig:\n{}'.format( | ||
394 | 153 | iface, ifconfig)) | ||
395 | 154 | subnets = iface.get('subnets', {}) | ||
396 | 155 | if subnets and iface['index'] != 0: | ||
397 | 156 | ifname = "{name}:{index}".format(**iface) | ||
398 | 157 | else: | ||
399 | 158 | ifname = "{name}".format(**iface) | ||
400 | 159 | |||
401 | 160 | # initial check, do we have the correct iface ? | ||
402 | 161 | print('ifname={}'.format(ifname)) | ||
403 | 162 | print("ifconfig['interface']={}".format(ifconfig['interface'])) | ||
404 | 163 | self.assertEqual(ifname, ifconfig['interface']) | ||
405 | 164 | |||
406 | 165 | # check physical interface attributes | ||
407 | 166 | # FIXME: can't check mac_addr under bonding since | ||
408 | 167 | # the bond might change slave mac addrs | ||
409 | 168 | for key in ['mtu']: | ||
410 | 169 | if key in iface and iface[key]: | ||
411 | 170 | self.assertEqual(iface[key], | ||
412 | 171 | ifconfig[key]) | ||
413 | 172 | |||
414 | 173 | def __get_subnet(subnets, subidx): | ||
415 | 174 | for index, subnet in zip(range(0, len(subnets)), subnets): | ||
416 | 175 | if index == subidx: | ||
417 | 176 | break | ||
418 | 177 | return subnet | ||
419 | 178 | |||
420 | 179 | # check subnet related attributes, and specifically only | ||
421 | 180 | # the subnet specified by iface['index'] | ||
422 | 181 | subnets = iface.get('subnets', {}) | ||
423 | 182 | if subnets: | ||
424 | 183 | subnet = __get_subnet(subnets, iface['index']) | ||
425 | 184 | if 'address' in subnet and subnet['address']: | ||
426 | 185 | if ':' in subnet['address']: | ||
427 | 186 | inet_iface = ipaddress.IPv6Interface( | ||
428 | 187 | subnet['address']) | ||
429 | 188 | else: | ||
430 | 189 | inet_iface = ipaddress.IPv4Interface( | ||
431 | 190 | subnet['address']) | ||
432 | 191 | |||
433 | 192 | # check ip addr | ||
434 | 193 | self.assertEqual(str(inet_iface.ip), | ||
435 | 194 | ifconfig['address']) | ||
436 | 195 | |||
437 | 196 | self.assertEqual(str(inet_iface.netmask), | ||
438 | 197 | ifconfig['netmask']) | ||
439 | 198 | |||
440 | 199 | self.assertEqual( | ||
441 | 200 | str(inet_iface.network.broadcast_address), | ||
442 | 201 | ifconfig['broadcast']) | ||
443 | 202 | |||
444 | 203 | # handle gateway by looking at routing table | ||
445 | 204 | if 'gateway' in subnet and subnet['gateway']: | ||
446 | 205 | gw_ip = subnet['gateway'] | ||
447 | 206 | gateways = [line for line in route_n.split('\n') | ||
448 | 207 | if 'UG' in line and gw_ip in line] | ||
449 | 208 | print('matching gateways:\n{}'.format(gateways)) | ||
450 | 209 | self.assertEqual(len(gateways), 1) | ||
451 | 210 | [gateways] = gateways | ||
452 | 211 | (dest, gw, genmask, flags, metric, ref, use, iface) = \ | ||
453 | 212 | gateways.split() | ||
454 | 213 | print('expected gw:{} found gw:{}'.format(gw_ip, gw)) | ||
455 | 214 | self.assertEqual(gw_ip, gw) | ||
456 | 215 | |||
457 | 216 | |||
458 | 217 | class TrustyTestBasic(TestNetworkAbs, TestCase): | ||
459 | 218 | __test__ = False | ||
460 | 219 | repo = "maas-daily" | ||
461 | 220 | release = "trusty" | ||
462 | 221 | arch = "amd64" | ||
463 | 222 | |||
464 | 223 | |||
465 | 224 | class WilyTestBasic(TestNetworkAbs, TestCase): | ||
466 | 225 | __test__ = True | ||
467 | 226 | repo = "maas-daily" | ||
468 | 227 | release = "wily" | ||
469 | 228 | arch = "amd64" | ||
470 | 229 | |||
471 | 230 | |||
472 | 231 | class VividTestBasic(TestNetworkAbs, TestCase): | ||
473 | 232 | __test__ = True | ||
474 | 233 | repo = "maas-daily" | ||
475 | 234 | release = "vivid" | ||
476 | 235 | arch = "amd64" |
llooks good.