Merge lp:~jtv/maas/write-full-networking-config into lp:~maas-committers/maas/trunk
- write-full-networking-config
- Merge into trunk
Proposed by
Jeroen T. Vermeulen
Status: | Merged |
---|---|
Approved by: | Jeroen T. Vermeulen |
Approved revision: | no longer in the source branch. |
Merged at revision: | 3088 |
Proposed branch: | lp:~jtv/maas/write-full-networking-config |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
618 lines (+566/-1) 2 files modified
src/maasserver/networking_preseed.py (+130/-0) src/maasserver/tests/test_networking_preseed.py (+436/-1) |
To merge this branch: | bzr merge lp:~jtv/maas/write-full-networking-config |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+235935@code.launchpad.net |
Commit message
Code (but no actual call yet) for generating a node's full /etc/network/
This is needed so that we can not only configure static IPv6 addresses, but also optionally disable IPv4.
Description of the change
Before we replace the existing network configuration script, we'll want to produce a udev rules file as well. The rules file assigns fixed names to the network interfaces.
Jeroen
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 'src/maasserver/networking_preseed.py' |
2 | --- src/maasserver/networking_preseed.py 2014-09-24 03:02:54 +0000 |
3 | +++ src/maasserver/networking_preseed.py 2014-09-25 12:39:04 +0000 |
4 | @@ -28,10 +28,13 @@ |
5 | 'generate_networking_config', |
6 | ] |
7 | |
8 | +from collections import defaultdict |
9 | + |
10 | from lxml import etree |
11 | from maasserver.dns.zonegenerator import get_dns_server_address |
12 | from maasserver.exceptions import UnresolvableHost |
13 | from maasserver.models.nodeprobeddetails import get_probed_details |
14 | +from maasserver.models.staticipaddress import StaticIPAddress |
15 | from netaddr import ( |
16 | IPAddress, |
17 | IPNetwork, |
18 | @@ -239,3 +242,130 @@ |
19 | ], |
20 | }, |
21 | } |
22 | + |
23 | + |
24 | +def compose_debian_network_interfaces_ipv4_stanza(interface): |
25 | + """Return a Debian `/etc/network/interfaces` stanza for DHCPv4.""" |
26 | + return "iface %s inet dhcp" % interface |
27 | + |
28 | + |
29 | +def compose_debian_network_interfaces_ipv6_stanza(interface, ip, gateway=None): |
30 | + """Return a Debian `/etc/network/interfaces` stanza for IPv6. |
31 | + |
32 | + The stanza will configure a static address. |
33 | + """ |
34 | + lines = [ |
35 | + 'iface %s inet6 static' % interface, |
36 | + '\tnetmask 64', |
37 | + '\taddress %s' % ip, |
38 | + ] |
39 | + if gateway is not None: |
40 | + lines.append('\tgateway %s' % gateway) |
41 | + return '\n'.join(lines) |
42 | + |
43 | + |
44 | +def extract_mac_string(mac): |
45 | + """Return normalised MAC address string from `MACAddress` model object.""" |
46 | + return normalise_mac(unicode(mac)) |
47 | + |
48 | + |
49 | +def add_ip_to_mapping(mapping, macaddress, ip): |
50 | + """Add IP address to a `defaultdict` keyed by MAC string. |
51 | + |
52 | + :param mapping: A `defaultdict` (defaulting to the empty set) mapping |
53 | + normalised MAC address strings to sets of `IPAddress`. |
54 | + :param macaddress: A `MACAddress`. |
55 | + :param ip: An IP address string. If it is empty or `None`, it will not |
56 | + be added to the mapping. |
57 | + """ |
58 | + if ip in (None, ''): |
59 | + return |
60 | + assert isinstance(ip, (unicode, bytes, IPAddress)) |
61 | + mac = extract_mac_string(macaddress) |
62 | + ip = IPAddress(ip) |
63 | + mapping[mac].add(ip) |
64 | + |
65 | + |
66 | +def extract_ip(mapping, mac, ip_version): |
67 | + """Extract IP address for `mac` and given IP version from `mapping`. |
68 | + |
69 | + :param mapping: A mapping as returned by `map_static_ips` or |
70 | + `map_gateways`. |
71 | + :param mac: A normalised MAC address string. |
72 | + :param ip_version: Either 4 or 6 (for IPv4 or IPv6 respectively). Only a |
73 | + static address for this version will be returned. |
74 | + :return: A matching IP address, or `None`. |
75 | + """ |
76 | + for ip in mapping[mac]: |
77 | + if ip.version == ip_version: |
78 | + return ip |
79 | + return None |
80 | + |
81 | + |
82 | +def map_static_ips(node): |
83 | + """Return a `defaultdict` mapping node's MAC addresses to their static IPs. |
84 | + |
85 | + :param node: A `Node`. |
86 | + :return: A `defaultdict` (defaulting to the empty set) mapping normalised |
87 | + MAC address strings to sets of `IPAddress`. |
88 | + """ |
89 | + mapping = defaultdict(set) |
90 | + for sip in StaticIPAddress.objects.filter(macaddress__node=node): |
91 | + for mac in sip.macaddress_set.all(): |
92 | + add_ip_to_mapping(mapping, mac, sip.ip) |
93 | + return mapping |
94 | + |
95 | + |
96 | +def map_gateways(node): |
97 | + """Return a `defaultdict` mapping node's MAC addresses to their gateways. |
98 | + |
99 | + :param node: A `Node`. |
100 | + :return: A `defaultdict` (defaulting to the empty set) mapping normalised |
101 | + MAC address strings to sets of `IPAddress`. |
102 | + """ |
103 | + mapping = defaultdict(set) |
104 | + for mac in node.macaddress_set.all(): |
105 | + for cluster_interface in mac.get_cluster_interfaces(): |
106 | + if cluster_interface.manages_static_range(): |
107 | + add_ip_to_mapping(mapping, mac, cluster_interface.router_ip) |
108 | + return mapping |
109 | + |
110 | + |
111 | +def has_static_ipv6_address(mapping): |
112 | + """Does `mapping` contain an IPv6 address? |
113 | + |
114 | + :param mapping: A mapping as returned by `map_static_ips`. |
115 | + :return bool: |
116 | + """ |
117 | + for ips in mapping.values(): |
118 | + for ip in ips: |
119 | + if ip.version == 6: |
120 | + return True |
121 | + return False |
122 | + |
123 | + |
124 | +def compose_debian_network_interfaces_file(node): |
125 | + """Return contents for a node's `/etc/network/interfaces` file.""" |
126 | + static_ips = map_static_ips(node) |
127 | + # Should we disable IPv4 on this node? For safety's sake, we won't do this |
128 | + # if the node has no static IPv6 addresses. Otherwise it might become |
129 | + # accidentally unaddressable: it may have IPv6 addresses, but apart from |
130 | + # being able to guess autoconfigured addresses, we won't know what they |
131 | + # are. |
132 | + disable_ipv4 = (node.disable_ipv4 and has_static_ipv6_address(static_ips)) |
133 | + gateways = map_gateways(node) |
134 | + stanzas = [ |
135 | + 'auto lo', |
136 | + ] |
137 | + for interface, mac in extract_network_interfaces(node): |
138 | + stanzas.append('auto %s' % interface) |
139 | + if not disable_ipv4: |
140 | + stanzas.append( |
141 | + compose_debian_network_interfaces_ipv4_stanza(interface)) |
142 | + static_ipv6 = extract_ip(static_ips, mac, 6) |
143 | + if static_ipv6 is not None: |
144 | + gateway = extract_ip(gateways, mac, 6) |
145 | + stanzas.append( |
146 | + compose_debian_network_interfaces_ipv6_stanza( |
147 | + interface, static_ipv6, gateway)) |
148 | + return '%s\n' % '\n\n'.join(stanzas) |
149 | |
150 | === modified file 'src/maasserver/tests/test_networking_preseed.py' |
151 | --- src/maasserver/tests/test_networking_preseed.py 2014-09-24 03:07:01 +0000 |
152 | +++ src/maasserver/tests/test_networking_preseed.py 2014-09-25 12:39:04 +0000 |
153 | @@ -15,25 +15,41 @@ |
154 | __all__ = [ |
155 | ] |
156 | |
157 | +from collections import defaultdict |
158 | from random import randint |
159 | +from textwrap import dedent |
160 | |
161 | from maasserver import networking_preseed |
162 | from maasserver.dns import zonegenerator |
163 | -from maasserver.enum import NODEGROUPINTERFACE_MANAGEMENT |
164 | +from maasserver.enum import ( |
165 | + NODEGROUP_STATUS, |
166 | + NODEGROUPINTERFACE_MANAGEMENT, |
167 | + ) |
168 | from maasserver.exceptions import UnresolvableHost |
169 | from maasserver.networking_preseed import ( |
170 | + add_ip_to_mapping, |
171 | + compose_debian_network_interfaces_file, |
172 | + compose_debian_network_interfaces_ipv4_stanza, |
173 | + compose_debian_network_interfaces_ipv6_stanza, |
174 | + extract_mac_string, |
175 | extract_network_interfaces, |
176 | generate_dns_server_entry, |
177 | generate_ethernet_link_entry, |
178 | generate_network_entry, |
179 | generate_networking_config, |
180 | generate_route_entries, |
181 | + has_static_ipv6_address, |
182 | list_dns_servers, |
183 | + map_gateways, |
184 | + map_static_ips, |
185 | normalise_mac, |
186 | ) |
187 | +import maasserver.networking_preseed as networking_preseed_module |
188 | from maasserver.testing.factory import factory |
189 | from maasserver.testing.testcase import MAASServerTestCase |
190 | from maastesting.matchers import MockCalledOnceWith |
191 | +from mock import ANY |
192 | +from netaddr import IPAddress |
193 | from testtools.matchers import HasLength |
194 | |
195 | |
196 | @@ -481,3 +497,422 @@ |
197 | }, |
198 | ], |
199 | config['network_info']['services']) |
200 | + |
201 | + |
202 | +class TestComposeDebianNetworkInterfaceIPv4Stanza(MAASServerTestCase): |
203 | + |
204 | + def test__produces_dhcp_stanza(self): |
205 | + interface = factory.make_name('eth') |
206 | + expected = "iface %s inet dhcp" % interface |
207 | + self.assertEqual( |
208 | + expected.strip(), |
209 | + compose_debian_network_interfaces_ipv4_stanza(interface).strip()) |
210 | + |
211 | + |
212 | +class TestComposeDebianNetworkInterfacesIPv6Stanza(MAASServerTestCase): |
213 | + |
214 | + def test__produces_static_stanza(self): |
215 | + ip = factory.make_ipv6_address() |
216 | + interface = factory.make_name('eth') |
217 | + expected = dedent("""\ |
218 | + iface %s inet6 static |
219 | + \tnetmask 64 |
220 | + \taddress %s |
221 | + """) % (interface, ip) |
222 | + self.assertEqual( |
223 | + expected.strip(), |
224 | + compose_debian_network_interfaces_ipv6_stanza( |
225 | + interface, ip).strip()) |
226 | + |
227 | + def test__includes_gateway_if_given(self): |
228 | + ip = factory.make_ipv6_address() |
229 | + interface = factory.make_name('eth') |
230 | + gateway = factory.make_ipv6_address() |
231 | + expected = dedent("""\ |
232 | + iface %s inet6 static |
233 | + \tnetmask 64 |
234 | + \taddress %s |
235 | + \tgateway %s |
236 | + """) % (interface, ip, gateway) |
237 | + self.assertEqual( |
238 | + expected.strip(), |
239 | + compose_debian_network_interfaces_ipv6_stanza( |
240 | + interface, ip, gateway).strip()) |
241 | + |
242 | + |
243 | +class TestExtractMACString(MAASServerTestCase): |
244 | + |
245 | + def test__returns_string(self): |
246 | + self.assertIsInstance( |
247 | + extract_mac_string(factory.make_MACAddress()), |
248 | + unicode) |
249 | + |
250 | + def test__returns_MAC_address(self): |
251 | + mac = factory.make_mac_address() |
252 | + self.assertEqual( |
253 | + normalise_mac(mac), |
254 | + extract_mac_string(factory.make_MACAddress(address=mac))) |
255 | + |
256 | + def test__works_even_if_mac_address_is_already_string(self): |
257 | + # The ORM normally presents MACAddress.mac_address as a MAC object. |
258 | + # But a string will work too. |
259 | + mac_string = factory.make_mac_address() |
260 | + mac = factory.make_MACAddress() |
261 | + mac.mac_address = mac_string |
262 | + self.assertIsInstance(mac.mac_address, unicode) |
263 | + self.assertEqual(normalise_mac(mac_string), extract_mac_string(mac)) |
264 | + |
265 | + |
266 | +class TestAddIPToMapping(MAASServerTestCase): |
267 | + |
268 | + def make_mapping(self): |
269 | + return defaultdict(set) |
270 | + |
271 | + def test__adds_to_empty_entry(self): |
272 | + mapping = self.make_mapping() |
273 | + mac = factory.make_MACAddress() |
274 | + ip = factory.make_ipv4_address() |
275 | + add_ip_to_mapping(mapping, mac, ip) |
276 | + self.assertEqual( |
277 | + {mac.mac_address: {IPAddress(ip)}}, |
278 | + mapping) |
279 | + |
280 | + def test__adds_to_nonempty_entry(self): |
281 | + mapping = self.make_mapping() |
282 | + mac = factory.make_MACAddress() |
283 | + ip1 = factory.make_ipv4_address() |
284 | + add_ip_to_mapping(mapping, mac, ip1) |
285 | + ip2 = factory.make_ipv4_address() |
286 | + add_ip_to_mapping(mapping, mac, ip2) |
287 | + self.assertEqual( |
288 | + {mac.mac_address: {IPAddress(ip1), IPAddress(ip2)}}, |
289 | + mapping) |
290 | + |
291 | + def test__does_not_add_None(self): |
292 | + mapping = self.make_mapping() |
293 | + mac = factory.make_MACAddress() |
294 | + add_ip_to_mapping(mapping, mac, None) |
295 | + self.assertEqual({}, mapping) |
296 | + |
297 | + def test__does_not_add_empty_string(self): |
298 | + mapping = self.make_mapping() |
299 | + mac = factory.make_MACAddress() |
300 | + add_ip_to_mapping(mapping, mac, '') |
301 | + self.assertEqual({}, mapping) |
302 | + |
303 | + |
304 | +class TestMapStaticIPs(MAASServerTestCase): |
305 | + |
306 | + def test__returns_empty_if_none_found(self): |
307 | + self.assertEqual({}, map_static_ips(factory.make_Node())) |
308 | + |
309 | + def test__finds_IPv4_address(self): |
310 | + node = factory.make_Node() |
311 | + mac = factory.make_MACAddress(node=node) |
312 | + ip = factory.make_ipv4_address() |
313 | + factory.make_StaticIPAddress(ip=ip, mac=mac) |
314 | + self.assertEqual( |
315 | + {mac.mac_address: {IPAddress(ip)}}, |
316 | + map_static_ips(node)) |
317 | + |
318 | + def test__finds_IPv6_address(self): |
319 | + node = factory.make_Node() |
320 | + mac = factory.make_MACAddress(node=node) |
321 | + ip = factory.make_ipv6_address() |
322 | + factory.make_StaticIPAddress(ip=ip, mac=mac) |
323 | + self.assertEqual( |
324 | + {mac.mac_address: {IPAddress(ip)}}, |
325 | + map_static_ips(node)) |
326 | + |
327 | + def test__finds_addresses_on_multiple_MACs(self): |
328 | + node = factory.make_Node() |
329 | + mac1 = factory.make_MACAddress(node=node) |
330 | + mac2 = factory.make_MACAddress(node=node) |
331 | + ip1 = factory.make_ipv4_address() |
332 | + factory.make_StaticIPAddress(ip=ip1, mac=mac1) |
333 | + ip2 = factory.make_ipv4_address() |
334 | + factory.make_StaticIPAddress(ip=ip2, mac=mac2) |
335 | + self.assertEqual( |
336 | + { |
337 | + mac1.mac_address: {IPAddress(ip1)}, |
338 | + mac2.mac_address: {IPAddress(ip2)}, |
339 | + }, |
340 | + map_static_ips(node)) |
341 | + |
342 | + def test__finds_multiple_addresses_on_MAC(self): |
343 | + node = factory.make_Node() |
344 | + mac = factory.make_MACAddress(node=node) |
345 | + ipv4 = factory.make_ipv4_address() |
346 | + ipv6 = factory.make_ipv6_address() |
347 | + factory.make_StaticIPAddress(ip=ipv4, mac=mac) |
348 | + factory.make_StaticIPAddress(ip=ipv6, mac=mac) |
349 | + self.assertEqual( |
350 | + {mac.mac_address: {IPAddress(ipv4), IPAddress(ipv6)}}, |
351 | + map_static_ips(node)) |
352 | + |
353 | + |
354 | +class TestMapGateways(MAASServerTestCase): |
355 | + |
356 | + def test__returns_empty_if_none_found(self): |
357 | + self.assertEqual({}, map_gateways(factory.make_Node())) |
358 | + |
359 | + def test__finds_IPv4_gateway(self): |
360 | + network = factory.make_ipv4_network(slash=24) |
361 | + gateway = factory.pick_ip_in_network(network) |
362 | + cluster = factory.make_NodeGroup(status=NODEGROUP_STATUS.ACCEPTED) |
363 | + cluster_interface = factory.make_NodeGroupInterface( |
364 | + cluster, network=network, router_ip=gateway, |
365 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
366 | + node = factory.make_Node(nodegroup=cluster) |
367 | + mac = factory.make_MACAddress( |
368 | + node=node, cluster_interface=cluster_interface) |
369 | + |
370 | + self.assertEqual( |
371 | + {mac.mac_address: {IPAddress(gateway)}}, |
372 | + map_gateways(node)) |
373 | + |
374 | + def test__finds_IPv6_gateway(self): |
375 | + network = factory.make_ipv6_network() |
376 | + gateway = factory.pick_ip_in_network(network) |
377 | + cluster = factory.make_NodeGroup(status=NODEGROUP_STATUS.ACCEPTED) |
378 | + net_interface = factory.make_name('eth') |
379 | + ipv4_interface = factory.make_NodeGroupInterface( |
380 | + cluster, interface=net_interface, |
381 | + management=NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED) |
382 | + factory.make_NodeGroupInterface( |
383 | + cluster, network=network, router_ip=gateway, |
384 | + interface=net_interface, |
385 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
386 | + node = factory.make_Node(nodegroup=cluster) |
387 | + mac = factory.make_MACAddress( |
388 | + node=node, cluster_interface=ipv4_interface) |
389 | + |
390 | + self.assertEqual( |
391 | + {mac.mac_address: {IPAddress(gateway)}}, |
392 | + map_gateways(node)) |
393 | + |
394 | + def test__finds_gateways_on_multiple_MACs(self): |
395 | + cluster = factory.make_NodeGroup(status=NODEGROUP_STATUS.ACCEPTED) |
396 | + node = factory.make_Node(nodegroup=cluster) |
397 | + network1 = factory.make_ipv4_network(slash=24) |
398 | + gateway1 = factory.pick_ip_in_network(network1) |
399 | + cluster_interface1 = factory.make_NodeGroupInterface( |
400 | + cluster, network=network1, router_ip=gateway1, |
401 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
402 | + mac1 = factory.make_MACAddress( |
403 | + node=node, cluster_interface=cluster_interface1) |
404 | + network2 = factory.make_ipv4_network(slash=24) |
405 | + gateway2 = factory.pick_ip_in_network(network2) |
406 | + cluster_interface2 = factory.make_NodeGroupInterface( |
407 | + cluster, network=network2, router_ip=gateway2, |
408 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
409 | + mac2 = factory.make_MACAddress( |
410 | + node=node, cluster_interface=cluster_interface2) |
411 | + |
412 | + self.assertEqual( |
413 | + { |
414 | + mac1.mac_address: {IPAddress(gateway1)}, |
415 | + mac2.mac_address: {IPAddress(gateway2)}, |
416 | + }, |
417 | + map_gateways(node)) |
418 | + |
419 | + def test__finds_multiple_gateways_on_MAC(self): |
420 | + cluster = factory.make_NodeGroup(status=NODEGROUP_STATUS.ACCEPTED) |
421 | + net_interface = factory.make_name('eth') |
422 | + ipv4_network = factory.make_ipv4_network(slash=24) |
423 | + ipv4_gateway = factory.pick_ip_in_network(ipv4_network) |
424 | + ipv4_interface = factory.make_NodeGroupInterface( |
425 | + cluster, network=ipv4_network, router_ip=ipv4_gateway, |
426 | + interface=net_interface, |
427 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
428 | + ipv6_network = factory.make_ipv6_network() |
429 | + ipv6_gateway = factory.pick_ip_in_network(ipv6_network) |
430 | + factory.make_NodeGroupInterface( |
431 | + cluster, network=ipv6_network, router_ip=ipv6_gateway, |
432 | + interface=net_interface, |
433 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
434 | + node = factory.make_Node(nodegroup=cluster) |
435 | + mac = factory.make_MACAddress( |
436 | + node=node, cluster_interface=ipv4_interface) |
437 | + |
438 | + self.assertEqual( |
439 | + { |
440 | + mac.mac_address: { |
441 | + IPAddress(ipv4_gateway), |
442 | + IPAddress(ipv6_gateway), |
443 | + }, |
444 | + }, |
445 | + map_gateways(node)) |
446 | + |
447 | + |
448 | +class TestHasStaticIPv6Address(MAASServerTestCase): |
449 | + |
450 | + def make_mapping(self): |
451 | + return defaultdict(set) |
452 | + |
453 | + def test__returns_False_for_empty_mapping(self): |
454 | + self.assertFalse(has_static_ipv6_address(self.make_mapping())) |
455 | + |
456 | + def test__finds_IPv6_address(self): |
457 | + mapping = self.make_mapping() |
458 | + add_ip_to_mapping( |
459 | + mapping, factory.make_MACAddress(), factory.make_ipv6_address()) |
460 | + self.assertTrue(has_static_ipv6_address(mapping)) |
461 | + |
462 | + def test__ignores_IPv4_address(self): |
463 | + mapping = self.make_mapping() |
464 | + add_ip_to_mapping( |
465 | + mapping, factory.make_MACAddress(), factory.make_ipv4_address()) |
466 | + self.assertFalse(has_static_ipv6_address(mapping)) |
467 | + |
468 | + def test__finds_IPv6_address_among_IPv4_addresses(self): |
469 | + mapping = self.make_mapping() |
470 | + add_ip_to_mapping( |
471 | + mapping, factory.make_MACAddress(), factory.make_ipv4_address()) |
472 | + mac = factory.make_MACAddress() |
473 | + add_ip_to_mapping(mapping, mac, factory.make_ipv4_address()) |
474 | + add_ip_to_mapping(mapping, mac, factory.make_ipv6_address()) |
475 | + add_ip_to_mapping(mapping, mac, factory.make_ipv4_address()) |
476 | + add_ip_to_mapping( |
477 | + mapping, factory.make_MACAddress(), factory.make_ipv4_address()) |
478 | + self.assertTrue(has_static_ipv6_address(mapping)) |
479 | + |
480 | + |
481 | +class TestComposeDebianNetworkInterfacesFile(MAASServerTestCase): |
482 | + |
483 | + def patch_node_interfaces(self, interfaces): |
484 | + """Inject given network interfaces data into `node`. |
485 | + |
486 | + The network interfaces data should be an iterabl of tuples, each of |
487 | + an interface name and a MAC address. |
488 | + """ |
489 | + fake = self.patch_autospec( |
490 | + networking_preseed_module, 'extract_network_interfaces') |
491 | + fake.return_value = [ |
492 | + (interface, normalise_mac(mac)) |
493 | + for interface, mac in interfaces |
494 | + ] |
495 | + |
496 | + def test__always_generates_lo(self): |
497 | + self.assertIn( |
498 | + 'auto lo', |
499 | + compose_debian_network_interfaces_file(factory.make_Node())) |
500 | + |
501 | + def test__generates_DHCPv4_config_if_IPv4_not_disabled(self): |
502 | + interface = factory.make_name('eth') |
503 | + node = factory.make_node_with_mac_attached_to_nodegroupinterface( |
504 | + disable_ipv4=False) |
505 | + mac = node.get_primary_mac() |
506 | + ipv6_network = factory.make_ipv6_network() |
507 | + factory.make_NodeGroupInterface( |
508 | + node.nodegroup, network=ipv6_network, |
509 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
510 | + factory.make_StaticIPAddress( |
511 | + ip=factory.pick_ip_in_network(ipv6_network), mac=mac) |
512 | + self.patch_node_interfaces([(interface, mac.mac_address.get_raw())]) |
513 | + |
514 | + self.assertIn( |
515 | + "\niface %s inet dhcp\n" % interface, |
516 | + compose_debian_network_interfaces_file(node)) |
517 | + |
518 | + def test__generates_DHCPv4_config_if_no_IPv6_configured(self): |
519 | + interface = factory.make_name('eth') |
520 | + node = factory.make_node_with_mac_attached_to_nodegroupinterface( |
521 | + disable_ipv4=True) |
522 | + mac = node.get_primary_mac() |
523 | + self.patch_node_interfaces([(interface, mac.mac_address.get_raw())]) |
524 | + self.assertIn( |
525 | + "\niface %s inet dhcp\n" % interface, |
526 | + compose_debian_network_interfaces_file(node)) |
527 | + |
528 | + def test__generates_no_DHCPv4_config_if_node_should_use_IPv6_only(self): |
529 | + interface = factory.make_name('eth') |
530 | + ipv6_network = factory.make_ipv6_network() |
531 | + node = factory.make_node_with_mac_attached_to_nodegroupinterface( |
532 | + disable_ipv4=True) |
533 | + mac = node.get_primary_mac() |
534 | + factory.make_NodeGroupInterface( |
535 | + node.nodegroup, network=ipv6_network, |
536 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
537 | + factory.make_StaticIPAddress( |
538 | + ip=factory.pick_ip_in_network(ipv6_network), mac=mac) |
539 | + self.patch_node_interfaces([(interface, mac.mac_address.get_raw())]) |
540 | + |
541 | + # The space is significant: this should not match the inet6 line! |
542 | + self.assertNotIn( |
543 | + " inet ", |
544 | + compose_debian_network_interfaces_file(node)) |
545 | + |
546 | + def test__generates_static_IPv6_config(self): |
547 | + interface = factory.make_name('eth') |
548 | + ipv6_network = factory.make_ipv6_network() |
549 | + node = factory.make_node_with_mac_attached_to_nodegroupinterface() |
550 | + mac = node.get_primary_mac() |
551 | + factory.make_NodeGroupInterface( |
552 | + node.nodegroup, network=ipv6_network, |
553 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
554 | + factory.make_StaticIPAddress( |
555 | + ip=factory.pick_ip_in_network(ipv6_network), mac=mac) |
556 | + self.patch_node_interfaces([(interface, mac.mac_address.get_raw())]) |
557 | + self.assertIn( |
558 | + "\niface %s inet6 static" % interface, |
559 | + compose_debian_network_interfaces_file(node)) |
560 | + |
561 | + def test__passes_ip_and_gateway_when_creating_IPv6_stanza(self): |
562 | + interface = factory.make_name('eth') |
563 | + ipv6_network = factory.make_ipv6_network() |
564 | + node = factory.make_node_with_mac_attached_to_nodegroupinterface() |
565 | + [ipv4_interface] = node.nodegroup.nodegroupinterface_set.all() |
566 | + mac = node.get_primary_mac() |
567 | + gateway = factory.pick_ip_in_network(ipv6_network) |
568 | + factory.make_NodeGroupInterface( |
569 | + node.nodegroup, network=ipv6_network, router_ip=gateway, |
570 | + interface=ipv4_interface.interface, |
571 | + management=NODEGROUPINTERFACE_MANAGEMENT.DHCP) |
572 | + static_ipv6 = factory.pick_ip_in_network(ipv6_network) |
573 | + factory.make_StaticIPAddress(ip=static_ipv6, mac=mac) |
574 | + self.patch_node_interfaces([(interface, mac.mac_address.get_raw())]) |
575 | + fake = self.patch_autospec( |
576 | + networking_preseed_module, |
577 | + 'compose_debian_network_interfaces_ipv6_stanza') |
578 | + fake.return_value = factory.make_name('stanza') |
579 | + |
580 | + compose_debian_network_interfaces_file(node) |
581 | + |
582 | + self.assertThat( |
583 | + fake, |
584 | + MockCalledOnceWith( |
585 | + interface, IPAddress(static_ipv6), IPAddress(gateway))) |
586 | + |
587 | + def test__omits_gateway_if_not_set(self): |
588 | + interface = factory.make_name('eth') |
589 | + ipv6_network = factory.make_ipv6_network() |
590 | + node = factory.make_node_with_mac_attached_to_nodegroupinterface() |
591 | + [ipv4_interface] = node.nodegroup.nodegroupinterface_set.all() |
592 | + mac = node.get_primary_mac() |
593 | + factory.make_NodeGroupInterface( |
594 | + node.nodegroup, network=ipv6_network, router_ip='', |
595 | + interface=ipv4_interface.interface, |
596 | + management=NODEGROUPINTERFACE_MANAGEMENT.UNMANAGED) |
597 | + static_ipv6 = factory.pick_ip_in_network(ipv6_network) |
598 | + factory.make_StaticIPAddress(ip=static_ipv6, mac=mac) |
599 | + self.patch_node_interfaces([(interface, mac.mac_address.get_raw())]) |
600 | + fake = self.patch_autospec( |
601 | + networking_preseed_module, |
602 | + 'compose_debian_network_interfaces_ipv6_stanza') |
603 | + fake.return_value = factory.make_name('stanza') |
604 | + |
605 | + compose_debian_network_interfaces_file(node) |
606 | + |
607 | + self.assertThat( |
608 | + fake, |
609 | + MockCalledOnceWith(interface, ANY, None)) |
610 | + |
611 | + def test__writes_auto_lines(self): |
612 | + interface = factory.make_name('eth') |
613 | + node = factory.make_node_with_mac_attached_to_nodegroupinterface() |
614 | + self.patch_node_interfaces( |
615 | + [(interface, node.get_primary_mac().mac_address.get_raw())]) |
616 | + interfaces_file = compose_debian_network_interfaces_file(node) |
617 | + self.assertIn('auto %s' % interface, interfaces_file) |
618 | + self.assertEqual(1, interfaces_file.count('auto %s' % interface)) |
A few small comments, but none of them are blockers. Nice new stuff :)