Merge lp:~lamont/maas/bug-1606508 into lp:~maas-committers/maas/trunk

Proposed by LaMont Jones
Status: Merged
Approved by: LaMont Jones
Approved revision: no longer in the source branch.
Merged at revision: 5496
Proposed branch: lp:~lamont/maas/bug-1606508
Merge into: lp:~maas-committers/maas/trunk
Diff against target: 208 lines (+131/-12)
5 files modified
src/maasserver/models/iprange.py (+4/-0)
src/maasserver/models/tests/test_iprange.py (+14/-0)
src/provisioningserver/dhcp/config.py (+45/-10)
src/provisioningserver/dhcp/tests/test_config.py (+66/-0)
src/provisioningserver/templates/dhcp/dhcpd6.conf.template (+2/-2)
To merge this branch: bzr merge lp:~lamont/maas/bug-1606508
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+308854@code.launchpad.net

Commit message

Properly configure DHCPv6 for high availability.

Description of the change

Properly configure DHCPv6 for high availability.

To post a comment you must log in.
Revision history for this message
LaMont Jones (lamont) wrote :

FWIW, I struggled to find a place where the tests for dhcp/config.py are not skipped, and failed (lxc, vm, and bare metal all skip them when running xenial, at a minimum.)

Fix verified via install-and-look technology.

Revision history for this message
Mike Pontillo (mpontillo) wrote :

Missing some unit tests. (Other than that, looks good.)

review: Needs Fixing
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Looks good. Thanks for adding the tests.

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

The attempt to merge lp:~lamont/maas/bug-1606508 into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease
Get:2 http://security.ubuntu.com/ubuntu xenial-security InRelease [94.5 kB]
Get:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease [95.7 kB]
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Fetched 190 kB in 0s (293 kB/s)

sudo: unable to resolve host juju-prod-cdo-maas-machine-3
E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/iprange.py'
2--- src/maasserver/models/iprange.py 2016-05-12 19:07:37 +0000
3+++ src/maasserver/models/iprange.py 2016-10-19 22:51:50 +0000
4@@ -161,6 +161,10 @@
5 if end_ip not in cidr:
6 raise ValidationError(
7 "End IP address must be within subnet: %s." % cidr)
8+ if (start_ip.version == 6 and self.type == IPRANGE_TYPE.DYNAMIC and
9+ netaddr.IPRange(start_ip, end_ip).size < 256):
10+ raise ValidationError(
11+ "IPv6 dynamic range must be at least 256 addresses in size.")
12 self.clean_prevent_dupes_and_overlaps()
13
14 @property
15
16=== modified file 'src/maasserver/models/tests/test_iprange.py'
17--- src/maasserver/models/tests/test_iprange.py 2016-06-09 23:26:48 +0000
18+++ src/maasserver/models/tests/test_iprange.py 2016-10-19 22:51:50 +0000
19@@ -134,6 +134,20 @@
20 ValidationError, '.*End IP address must not be less than.*'):
21 iprange.save()
22
23+ def test__requires_256_addresses_for_ipv6_dynamic(self):
24+ subnet = factory.make_Subnet(
25+ cidr='2001:db8::/64', gateway_ip='fe80::1', dns_servers=[])
26+ iprange = IPRange(
27+ start_ip='2001:db8::', end_ip='2001:db8::fe',
28+ user=factory.make_User(), subnet=subnet,
29+ type=IPRANGE_TYPE.DYNAMIC,
30+ comment="This is a comment.")
31+ with ExpectedException(
32+ ValidationError,
33+ ".*IPv6 dynamic range must be "
34+ "at least 256 addresses in size."):
35+ iprange.save()
36+
37 def test__requires_type(self):
38 subnet = make_plain_subnet()
39 iprange = IPRange(
40
41=== modified file 'src/provisioningserver/dhcp/config.py'
42--- src/provisioningserver/dhcp/config.py 2016-09-26 15:35:54 +0000
43+++ src/provisioningserver/dhcp/config.py 2016-10-19 22:51:50 +0000
44@@ -20,6 +20,7 @@
45 from netaddr import (
46 IPAddress,
47 IPNetwork,
48+ IPRange,
49 )
50 from provisioningserver.boot import BootMethodRegistry
51 from provisioningserver.utils import (
52@@ -296,6 +297,35 @@
53 IPAddress(addr)
54 for addr in net_utils.get_all_interface_addresses()]
55
56+ shared_networks = _process_network_parameters_v6(
57+ rack_addrs, failover_peers, shared_networks)
58+
59+ try:
60+ return template.substitute(
61+ global_dhcp_snippets=global_dhcp_snippets, hosts=hosts,
62+ failover_peers=failover_peers, shared_networks=shared_networks,
63+ platform_codename=platform_codename,
64+ omapi_key=omapi_key, **helpers)
65+ except (KeyError, NameError) as error:
66+ raise DHCPConfigError(
67+ "Failed to render DHCP configuration.") from error
68+
69+
70+def _process_network_parameters_v6(
71+ rack_addrs, failover_peers, shared_networks):
72+ """Preprocess shared_networks prior to rendering the template.
73+
74+ This is a separate function, partly for readability, and partly for ease
75+ of testing.
76+
77+ :param rack_addrs: a list of IPAddress values for the interfaces on this
78+ rack controller.
79+ :param failover_peers: failover_peers from get_config_v6.
80+ :param shared_networks: shared_networks from get_config_v6.
81+ :return: an updated shared_networks, suitable for rendering the template.
82+ """
83+ peers = {x["name"]: x for x in failover_peers}
84+
85 for shared_network in shared_networks:
86 for subnet in shared_network["subnets"]:
87 cidr = IPNetwork(subnet['subnet_cidr'])
88@@ -312,13 +342,18 @@
89 ntp_servers_ipv4, ntp_servers_ipv6 = _get_addresses(*ntp_servers)
90 subnet["ntp_servers_ipv4"] = ", ".join(ntp_servers_ipv4)
91 subnet["ntp_servers_ipv6"] = ", ".join(ntp_servers_ipv6)
92-
93- try:
94- return template.substitute(
95- global_dhcp_snippets=global_dhcp_snippets, hosts=hosts,
96- failover_peers=failover_peers, shared_networks=shared_networks,
97- platform_codename=platform_codename,
98- omapi_key=omapi_key, **helpers)
99- except (KeyError, NameError) as error:
100- raise DHCPConfigError(
101- "Failed to render DHCP configuration.") from error
102+ for pool in subnet["pools"]:
103+ peer = pool.get("failover_peer", None)
104+ if peer is not None:
105+ ip_range = IPRange(
106+ pool["ip_range_low"],
107+ pool["ip_range_high"])
108+ if peers[peer]["mode"] == "primary":
109+ pool["ip_range_high"] = str(
110+ IPAddress(
111+ ip_range.first + int(ip_range.size / 2) - 1))
112+ else:
113+ pool["ip_range_low"] = str(
114+ IPAddress(
115+ ip_range.first + int(ip_range.size / 2)))
116+ return shared_networks
117
118=== modified file 'src/provisioningserver/dhcp/tests/test_config.py'
119--- src/provisioningserver/dhcp/tests/test_config.py 2016-10-18 11:55:44 +0000
120+++ src/provisioningserver/dhcp/tests/test_config.py 2016-10-19 22:51:50 +0000
121@@ -475,6 +475,72 @@
122 Contains(expected))
123
124
125+class Test_process_shared_network_v6(MAASTestCase):
126+ """Tests for `_process_network_parameters_v6`."""
127+
128+ scenarios = (
129+ ('singleton', dict(
130+ expected={
131+ 'ip_range_high': '2001:db8:3:1::ffff',
132+ 'ip_range_low': '2001:db8:3:1::',
133+ 'failover_peer': None},
134+ rack_addrs=[netaddr.IPAddress('2001:db8:3:280::2')],
135+ failover_peers=[])),
136+ ('primary', dict(
137+ expected={
138+ 'ip_range_high': '2001:db8:3:1::7fff',
139+ 'ip_range_low': '2001:db8:3:1::',
140+ 'failover_peer': 'failover-vlan-5020'},
141+ rack_addrs=[netaddr.IPAddress('2001:db8:3:280::2')],
142+ failover_peers=[{
143+ 'mode': 'primary',
144+ 'peer_address': '2001:db8:3:0:1::',
145+ 'name': 'failover-vlan-5020',
146+ 'address': '2001:db8:3:280::2'}])),
147+ ('secondary', dict(
148+ expected={
149+ 'ip_range_high': '2001:db8:3:1::ffff',
150+ 'ip_range_low': '2001:db8:3:1::8000',
151+ 'failover_peer': 'failover-vlan-5020'},
152+ rack_addrs=[netaddr.IPAddress('2001:db8:3:0:1::')],
153+ failover_peers=[{
154+ 'mode': 'secondary',
155+ 'address': '2001:db8:3:0:1::',
156+ 'name': 'failover-vlan-5020',
157+ 'peer_address': '2001:db8:3:280::2'}])))
158+
159+ def test__adjusts_parameters_for_primary(self):
160+ shared_networks = [
161+ {
162+ 'name': 'vlan-5020',
163+ 'subnets': [
164+ {
165+ 'domain_name': 'maas.example.com',
166+ 'pools': [{
167+ 'ip_range_high': '2001:db8:3:1::ffff',
168+ 'ip_range_low': '2001:db8:3:1::',
169+ 'failover_peer': self.expected['failover_peer']}],
170+ 'dns_servers': [],
171+ 'subnet_mask': 'ffff:ffff:ffff:ffff::',
172+ 'ntp_servers': [],
173+ 'broadcast_ip': '2001:db8:3::ffff:ffff:ffff:ffff',
174+ 'router_ip': 'fe80::1',
175+ 'subnet': '2001:db8:3::',
176+ 'subnet_cidr': '2001:db8:3::/64',
177+ 'dhcp_snippets': []
178+ }],
179+ }]
180+ self.patch(
181+ config, 'compose_conditional_bootloader').return_value = ""
182+ self.patch(
183+ config, '_get_addresses').return_value = (
184+ [''], ['2001:db8:280::2'])
185+ actual = config._process_network_parameters_v6(
186+ self.rack_addrs, self.failover_peers, shared_networks)
187+ self.assertDictEqual(
188+ actual[0]['subnets'][0]['pools'][0], self.expected)
189+
190+
191 class TestComposeConditionalBootloader(MAASTestCase):
192 """Tests for `compose_conditional_bootloader`."""
193
194
195=== modified file 'src/provisioningserver/templates/dhcp/dhcpd6.conf.template'
196--- src/provisioningserver/templates/dhcp/dhcpd6.conf.template 2016-10-12 22:07:21 +0000
197+++ src/provisioningserver/templates/dhcp/dhcpd6.conf.template 2016-10-19 22:51:50 +0000
198@@ -50,8 +50,8 @@
199 option dhcp6.sntp-servers {{dhcp_subnet['ntp_servers_ipv6']}};
200 {{endif}}
201
202- default-lease-time 600;
203- max-lease-time 600;
204+ default-lease-time 1800;
205+ max-lease-time 1800;
206 #
207 # Subnet DHCP snippets
208 #