Merge ~cgrabowski/maas:ability_to_nest_a_dhcp_snippet_in_a_pool into maas:master
- Git
- lp:~cgrabowski/maas
- ability_to_nest_a_dhcp_snippet_in_a_pool
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Christian Grabowski | ||||
Approved revision: | 051b4eabe55599c62413c6b2b9f6a6c8b4d3c4e3 | ||||
Merge reported by: | MAAS Lander | ||||
Merged at revision: | not available | ||||
Proposed branch: | ~cgrabowski/maas:ability_to_nest_a_dhcp_snippet_in_a_pool | ||||
Merge into: | maas:master | ||||
Diff against target: |
642 lines (+318/-9) 14 files modified
src/maasserver/api/dhcpsnippets.py (+3/-0) src/maasserver/dhcp.py (+22/-2) src/maasserver/forms/dhcpsnippet.py (+14/-1) src/maasserver/forms/tests/test_dhcpsnippet.py (+70/-0) src/maasserver/migrations/maasserver/0239_add_iprange_specific_dhcp_snippets.py (+24/-0) src/maasserver/models/dhcpsnippet.py (+15/-0) src/maasserver/models/tests/test_dhcpsnippet.py (+18/-0) src/maasserver/testing/factory.py (+2/-0) src/maasserver/tests/test_dhcp.py (+86/-4) src/maasserver/websockets/handlers/tests/test_dhcpsnippet.py (+4/-0) src/provisioningserver/dhcp/testing/config.py (+13/-2) src/provisioningserver/rpc/cluster.py (+22/-0) src/provisioningserver/templates/dhcp/dhcpd.conf.template (+13/-0) src/provisioningserver/templates/dhcp/dhcpd6.conf.template (+12/-0) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Björn Tillenius | Approve | ||
MAAS Lander | Approve | ||
Review via email: mp+402371@code.launchpad.net |
Commit message
add ability to create DHCP snippets within a pool.
Description of the change
Adds the ability to create DHCP snippets within a pool.
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b ability_
STATUS: FAILED
LOG: http://
COMMIT: a9bcdca000d7cfc
- 2a9f1f9... by Christian Grabowski
-
use a more test-fixture-
friendly dhcp snippet
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b ability_
STATUS: FAILED
LOG: http://
COMMIT: 2a9f1f907c4a68c
- b7a04b2... by Christian Grabowski
-
use double quote in interpolated string
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b ability_
STATUS: SUCCESS
COMMIT: b7a04b201119a79
Björn Tillenius (bjornt) wrote : | # |
nice fix. I have a few comments/questions inline, but nothing major.
- 183f742... by Christian Grabowski
-
make pool dhcp_snippets param non-optional in making pools
- cbb350e... by Christian Grabowski
-
filter on instance.subnet, not instance.subnet.id in form
- 051b4ea... by Christian Grabowski
-
add units asserting invalid arguments throw validation error
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b ability_
STATUS: SUCCESS
COMMIT: 051b4eabe55599c
Christian Grabowski (cgrabowski) wrote : | # |
Replied to a few comments, otherwise, just made the suggested changes.
Björn Tillenius (bjornt) wrote : | # |
Thanks. The changes look good, but there's still an issue with the RPC protocol changes you made.
Björn Tillenius (bjornt) wrote : | # |
As discussed on MM, I was confused, and the RPC changes are indeed needed.
Preview Diff
1 | diff --git a/src/maasserver/api/dhcpsnippets.py b/src/maasserver/api/dhcpsnippets.py |
2 | index 71f3494..ed4fcb9 100644 |
3 | --- a/src/maasserver/api/dhcpsnippets.py |
4 | +++ b/src/maasserver/api/dhcpsnippets.py |
5 | @@ -262,6 +262,9 @@ class DHCPSnippetsHandler(OperationsHandler): |
6 | @param (string) "subnet" [required=false] The subnet this snippet |
7 | applies to. Cannot be used with node or global_snippet. |
8 | |
9 | + @param (string) "iprange" [required=false] The iprange within a subnet |
10 | + this snippet applies to. Must also provide a subnet value. |
11 | + |
12 | @param (boolean) "global_snippet" [required=false] Whether or not this |
13 | snippet is to be applied globally. Cannot be used with node or subnet. |
14 | |
15 | diff --git a/src/maasserver/dhcp.py b/src/maasserver/dhcp.py |
16 | index 2622632..2d61705 100644 |
17 | --- a/src/maasserver/dhcp.py |
18 | +++ b/src/maasserver/dhcp.py |
19 | @@ -396,13 +396,14 @@ def make_hosts_for_subnets(subnets, nodes_dhcp_snippets: list = None): |
20 | return hosts |
21 | |
22 | |
23 | -def make_pools_for_subnet(subnet, failover_peer=None): |
24 | +def make_pools_for_subnet(subnet, dhcp_snippets, failover_peer=None): |
25 | """Return list of pools to create in the DHCP config for `subnet`.""" |
26 | pools = [] |
27 | for ip_range in subnet.get_dynamic_ranges().order_by("id"): |
28 | pool = { |
29 | "ip_range_low": ip_range.start_ip, |
30 | "ip_range_high": ip_range.end_ip, |
31 | + "dhcp_snippets": dhcp_snippets.get(ip_range.id, []), |
32 | } |
33 | if failover_peer is not None: |
34 | pool["failover_peer"] = failover_peer |
35 | @@ -430,6 +431,7 @@ def make_subnet_config( |
36 | """ |
37 | ip_network = subnet.get_ipnetwork() |
38 | dns_servers = [] |
39 | + ipranges_dhcp_snippets = dict() |
40 | if subnet.allow_dns and default_dns_servers: |
41 | # If the MAAS DNS server is enabled make sure that is used first. |
42 | if subnet.gateway_ip: |
43 | @@ -446,6 +448,20 @@ def make_subnet_config( |
44 | dns_servers += [IPAddress(server) for server in subnet.dns_servers] |
45 | if subnets_dhcp_snippets is None: |
46 | subnets_dhcp_snippets = [] |
47 | + else: |
48 | + subnet_only_dhcp_snippets = [] |
49 | + for snippet in subnets_dhcp_snippets: |
50 | + if snippet.iprange is not None: |
51 | + iprange_dhcp_snippets = ipranges_dhcp_snippets.get( |
52 | + snippet.iprange.id, [] |
53 | + ) |
54 | + iprange_dhcp_snippets.append(make_dhcp_snippet(snippet)) |
55 | + ipranges_dhcp_snippets[ |
56 | + snippet.iprange.id |
57 | + ] = iprange_dhcp_snippets |
58 | + else: |
59 | + subnet_only_dhcp_snippets.append(snippet) |
60 | + subnets_dhcp_snippets = subnet_only_dhcp_snippets |
61 | |
62 | subnet_config = { |
63 | "subnet": str(ip_network.network), |
64 | @@ -456,7 +472,11 @@ def make_subnet_config( |
65 | "dns_servers": dns_servers, |
66 | "ntp_servers": get_ntp_servers(ntp_servers, subnet, peer_rack), |
67 | "domain_name": default_domain.name, |
68 | - "pools": make_pools_for_subnet(subnet, failover_peer), |
69 | + "pools": make_pools_for_subnet( |
70 | + subnet, |
71 | + ipranges_dhcp_snippets, |
72 | + failover_peer, |
73 | + ), |
74 | "dhcp_snippets": [ |
75 | make_dhcp_snippet(dhcp_snippet) |
76 | for dhcp_snippet in subnets_dhcp_snippets |
77 | diff --git a/src/maasserver/forms/dhcpsnippet.py b/src/maasserver/forms/dhcpsnippet.py |
78 | index fc4d74b..12f14b2 100644 |
79 | --- a/src/maasserver/forms/dhcpsnippet.py |
80 | +++ b/src/maasserver/forms/dhcpsnippet.py |
81 | @@ -14,7 +14,7 @@ from maasserver.fields import ( |
82 | VersionedTextFileField, |
83 | ) |
84 | from maasserver.forms import MAASModelForm |
85 | -from maasserver.models import DHCPSnippet, Node, Subnet |
86 | +from maasserver.models import DHCPSnippet, IPRange, Node, Subnet |
87 | from maasserver.utils.forms import set_form_error |
88 | from provisioningserver.events import EVENT_TYPES |
89 | |
90 | @@ -57,6 +57,14 @@ class DHCPSnippetForm(MAASModelForm): |
91 | help_text="The subnet which the DHCP snippet is for.", |
92 | ) |
93 | |
94 | + iprange = SpecifierOrModelChoiceField( |
95 | + label="IP Range", |
96 | + queryset=IPRange.objects.all(), |
97 | + required=False, |
98 | + initial=None, |
99 | + help_text="The iprange which the DHCP snippet is for.", |
100 | + ) |
101 | + |
102 | global_snippet = forms.BooleanField( |
103 | label="Global DHCP Snippet", |
104 | required=False, |
105 | @@ -75,6 +83,7 @@ class DHCPSnippetForm(MAASModelForm): |
106 | "enabled", |
107 | "node", |
108 | "subnet", |
109 | + "iprange", |
110 | "global_snippet", |
111 | ) |
112 | |
113 | @@ -88,6 +97,10 @@ class DHCPSnippetForm(MAASModelForm): |
114 | self.fields["value"].initial = self.instance.value |
115 | if instance is not None and instance.node is not None: |
116 | self.initial["node"] = self.instance.node.system_id |
117 | + if instance is not None and instance.subnet is not None: |
118 | + self.fields["iprange"].queryset = IPRange.objects.filter( |
119 | + subnet=instance.subnet |
120 | + ) |
121 | |
122 | def clean(self): |
123 | cleaned_data = super().clean() |
124 | diff --git a/src/maasserver/forms/tests/test_dhcpsnippet.py b/src/maasserver/forms/tests/test_dhcpsnippet.py |
125 | index 2000f04..693e104 100644 |
126 | --- a/src/maasserver/forms/tests/test_dhcpsnippet.py |
127 | +++ b/src/maasserver/forms/tests/test_dhcpsnippet.py |
128 | @@ -129,6 +129,76 @@ class TestDHCPSnippetForm(MAASServerTestCase): |
129 | self.assertEqual(enabled, dhcp_snippet.enabled) |
130 | self.assertEqual(subnet, dhcp_snippet.subnet) |
131 | |
132 | + def test_create_dhcp_snippet_with_iprange(self): |
133 | + subnet = factory.make_ipv4_Subnet_with_IPRanges() |
134 | + iprange = subnet.get_dynamic_ranges().first() |
135 | + iprange.save() |
136 | + name = factory.make_name("name") |
137 | + value = factory.make_string() |
138 | + description = factory.make_string() |
139 | + enabled = factory.pick_bool() |
140 | + form = DHCPSnippetForm( |
141 | + data={ |
142 | + "name": name, |
143 | + "value": value, |
144 | + "description": description, |
145 | + "enabled": enabled, |
146 | + "subnet": subnet.id, |
147 | + "iprange": iprange.id, |
148 | + } |
149 | + ) |
150 | + self.assertTrue(form.is_valid(), form.errors) |
151 | + endpoint = factory.pick_choice(ENDPOINT_CHOICES) |
152 | + request = HttpRequest() |
153 | + request.user = factory.make_User() |
154 | + dhcp_snippet = form.save(endpoint, request) |
155 | + self.assertEqual(name, dhcp_snippet.name) |
156 | + self.assertEqual(value, dhcp_snippet.value.data) |
157 | + self.assertEqual(description, dhcp_snippet.description) |
158 | + self.assertEqual(enabled, dhcp_snippet.enabled) |
159 | + self.assertEqual(subnet, dhcp_snippet.subnet) |
160 | + self.assertEqual(iprange, dhcp_snippet.iprange) |
161 | + |
162 | + def test_create_dhcp_snippet_with_iprange_requires_subnet(self): |
163 | + subnet = factory.make_ipv4_Subnet_with_IPRanges() |
164 | + iprange = subnet.get_dynamic_ranges().first() |
165 | + iprange.save() |
166 | + name = factory.make_name("name") |
167 | + value = factory.make_string() |
168 | + description = factory.make_string() |
169 | + enabled = factory.pick_bool() |
170 | + form = DHCPSnippetForm( |
171 | + data={ |
172 | + "name": name, |
173 | + "value": value, |
174 | + "dscription": description, |
175 | + "enabled": enabled, |
176 | + "iprange": iprange.id, |
177 | + } |
178 | + ) |
179 | + self.assertFalse(form.is_valid(), form.errors) |
180 | + |
181 | + def test_create_dhcp_snippet_with_iprange_requires_parent_subnet(self): |
182 | + subnet = factory.make_ipv4_Subnet_with_IPRanges() |
183 | + subnet2 = factory.make_Subnet() |
184 | + iprange = subnet.get_dynamic_ranges().first() |
185 | + iprange.save() |
186 | + name = factory.make_name("name") |
187 | + value = factory.make_string() |
188 | + description = factory.make_string() |
189 | + enabled = factory.pick_bool() |
190 | + form = DHCPSnippetForm( |
191 | + data={ |
192 | + "name": name, |
193 | + "value": value, |
194 | + "dscription": description, |
195 | + "enabled": enabled, |
196 | + "subnet": subnet2.id, |
197 | + "iprange": iprange.id, |
198 | + } |
199 | + ) |
200 | + self.assertFalse(form.is_valid(), form.errors) |
201 | + |
202 | def test_cannt_create_dhcp_snippet_with_node_and_subnet(self): |
203 | node = factory.make_Node() |
204 | subnet = factory.make_Subnet() |
205 | diff --git a/src/maasserver/migrations/maasserver/0239_add_iprange_specific_dhcp_snippets.py b/src/maasserver/migrations/maasserver/0239_add_iprange_specific_dhcp_snippets.py |
206 | new file mode 100644 |
207 | index 0000000..f5bba86 |
208 | --- /dev/null |
209 | +++ b/src/maasserver/migrations/maasserver/0239_add_iprange_specific_dhcp_snippets.py |
210 | @@ -0,0 +1,24 @@ |
211 | +# Generated by Django 2.2.12 on 2021-05-06 19:28 |
212 | + |
213 | +from django.db import migrations, models |
214 | +import django.db.models.deletion |
215 | + |
216 | + |
217 | +class Migration(migrations.Migration): |
218 | + |
219 | + dependencies = [ |
220 | + ("maasserver", "0238_disable_boot_architectures"), |
221 | + ] |
222 | + |
223 | + operations = [ |
224 | + migrations.AddField( |
225 | + model_name="dhcpsnippet", |
226 | + name="iprange", |
227 | + field=models.ForeignKey( |
228 | + blank=True, |
229 | + null=True, |
230 | + on_delete=django.db.models.deletion.CASCADE, |
231 | + to="maasserver.IPRange", |
232 | + ), |
233 | + ), |
234 | + ] |
235 | diff --git a/src/maasserver/models/dhcpsnippet.py b/src/maasserver/models/dhcpsnippet.py |
236 | index 61346d3..7a401d7 100644 |
237 | --- a/src/maasserver/models/dhcpsnippet.py |
238 | +++ b/src/maasserver/models/dhcpsnippet.py |
239 | @@ -14,6 +14,7 @@ from django.db.models import ( |
240 | ) |
241 | |
242 | from maasserver.models.cleansave import CleanSave |
243 | +from maasserver.models.iprange import IPRange |
244 | from maasserver.models.node import Node |
245 | from maasserver.models.subnet import Subnet |
246 | from maasserver.models.timestampedmodel import TimestampedModel |
247 | @@ -85,6 +86,8 @@ class DHCPSnippet(CleanSave, TimestampedModel): |
248 | |
249 | subnet = ForeignKey(Subnet, null=True, blank=True, on_delete=CASCADE) |
250 | |
251 | + iprange = ForeignKey(IPRange, null=True, blank=True, on_delete=CASCADE) |
252 | + |
253 | objects = DHCPSnippetManager() |
254 | |
255 | def __str__(self): |
256 | @@ -97,3 +100,15 @@ class DHCPSnippet(CleanSave, TimestampedModel): |
257 | "A DHCP snippet cannot be enabled on a node and subnet at the " |
258 | "same time." |
259 | ) |
260 | + if self.iprange is not None and self.subnet is None: |
261 | + raise ValidationError( |
262 | + "A DHCP snippet cannot be enabled on an iprange without" |
263 | + "a parent subnet" |
264 | + ) |
265 | + elif ( |
266 | + self.iprange is not None |
267 | + and self.iprange.subnet_id != self.subnet.id |
268 | + ): |
269 | + raise ValidationError( |
270 | + "A DHCP snippet's IP Range must be within the" "parent subnet" |
271 | + ) |
272 | diff --git a/src/maasserver/models/tests/test_dhcpsnippet.py b/src/maasserver/models/tests/test_dhcpsnippet.py |
273 | index 35cbb91..acefc80 100644 |
274 | --- a/src/maasserver/models/tests/test_dhcpsnippet.py |
275 | +++ b/src/maasserver/models/tests/test_dhcpsnippet.py |
276 | @@ -55,6 +55,24 @@ class TestDHCPSnippet(MAASServerTestCase): |
277 | self.assertEqual(enabled, dhcp_snippet.enabled) |
278 | self.assertEqual(subnet, dhcp_snippet.subnet) |
279 | |
280 | + def test_factory_make_DHCPSnippet_sets_iprange(self): |
281 | + name = factory.make_name("dhcp_snippet") |
282 | + value = VersionedTextFile.objects.create(data=factory.make_string()) |
283 | + description = factory.make_string() |
284 | + enabled = factory.pick_bool() |
285 | + subnet = factory.make_ipv4_Subnet_with_IPRanges() |
286 | + iprange = subnet.get_dynamic_ranges().first() |
287 | + iprange.save() |
288 | + dhcp_snippet = factory.make_DHCPSnippet( |
289 | + name, value, description, enabled, subnet=subnet, iprange=iprange |
290 | + ) |
291 | + self.assertEqual(name, dhcp_snippet.name) |
292 | + self.assertEqual(value.data, dhcp_snippet.value.data) |
293 | + self.assertEqual(description, dhcp_snippet.description) |
294 | + self.assertEqual(enabled, dhcp_snippet.enabled) |
295 | + self.assertEqual(subnet, dhcp_snippet.subnet) |
296 | + self.assertEqual(iprange, dhcp_snippet.iprange) |
297 | + |
298 | def test_can_only_set_snippet_for_node_or_subnet(self): |
299 | node = factory.make_Node() |
300 | subnet = factory.make_Subnet() |
301 | diff --git a/src/maasserver/testing/factory.py b/src/maasserver/testing/factory.py |
302 | index c463876..929c159 100644 |
303 | --- a/src/maasserver/testing/factory.py |
304 | +++ b/src/maasserver/testing/factory.py |
305 | @@ -2955,6 +2955,7 @@ class Factory(maastesting.factory.Factory): |
306 | enabled=True, |
307 | node=None, |
308 | subnet=None, |
309 | + iprange=None, |
310 | ): |
311 | if name is None: |
312 | name = self.make_name("dhcp_snippet") |
313 | @@ -2969,6 +2970,7 @@ class Factory(maastesting.factory.Factory): |
314 | enabled=enabled, |
315 | node=node, |
316 | subnet=subnet, |
317 | + iprange=iprange, |
318 | ) |
319 | |
320 | def make_default_PackageRepositories(self): |
321 | diff --git a/src/maasserver/tests/test_dhcp.py b/src/maasserver/tests/test_dhcp.py |
322 | index 6bc3db5..040bfbf 100644 |
323 | --- a/src/maasserver/tests/test_dhcp.py |
324 | +++ b/src/maasserver/tests/test_dhcp.py |
325 | @@ -1582,8 +1582,16 @@ class TestMakeSubnetConfig(MAASServerTestCase): |
326 | ) |
327 | self.assertEqual( |
328 | [ |
329 | - {"ip_range_low": "10.9.8.11", "ip_range_high": "10.9.8.20"}, |
330 | - {"ip_range_low": "10.9.8.21", "ip_range_high": "10.9.8.30"}, |
331 | + { |
332 | + "ip_range_low": "10.9.8.11", |
333 | + "ip_range_high": "10.9.8.20", |
334 | + "dhcp_snippets": [], |
335 | + }, |
336 | + { |
337 | + "ip_range_low": "10.9.8.21", |
338 | + "ip_range_high": "10.9.8.30", |
339 | + "dhcp_snippets": [], |
340 | + }, |
341 | ], |
342 | config["pools"], |
343 | ) |
344 | @@ -1615,11 +1623,13 @@ class TestMakeSubnetConfig(MAASServerTestCase): |
345 | "ip_range_low": "10.9.8.11", |
346 | "ip_range_high": "10.9.8.20", |
347 | "failover_peer": failover_peer, |
348 | + "dhcp_snippets": [], |
349 | }, |
350 | { |
351 | "ip_range_low": "10.9.8.21", |
352 | "ip_range_high": "10.9.8.30", |
353 | "failover_peer": failover_peer, |
354 | + "dhcp_snippets": [], |
355 | }, |
356 | ], |
357 | config["pools"], |
358 | @@ -1696,6 +1706,58 @@ class TestMakeSubnetConfig(MAASServerTestCase): |
359 | config["disabled_boot_architectures"], |
360 | ) |
361 | |
362 | + def test_returns_iprange_dhcp_snippets(self): |
363 | + rack_controller = factory.make_RackController(interface=False) |
364 | + vlan = factory.make_VLAN() |
365 | + subnet = factory.make_ipv4_Subnet_with_IPRanges(vlan=vlan) |
366 | + iprange = subnet.get_dynamic_ranges().first() |
367 | + iprange.save() |
368 | + factory.make_Interface( |
369 | + INTERFACE_TYPE.PHYSICAL, vlan=vlan, node=rack_controller |
370 | + ) |
371 | + default_domain = Domain.objects.get_default_domain() |
372 | + subnet_snippets = [ |
373 | + factory.make_DHCPSnippet(subnet=subnet, enabled=True) |
374 | + for _ in range(3) |
375 | + ] |
376 | + iprange_snippets = [ |
377 | + factory.make_DHCPSnippet( |
378 | + subnet=subnet, iprange=iprange, enabled=True |
379 | + ) |
380 | + for _ in range(3) |
381 | + ] |
382 | + dhcp_snippets = subnet_snippets + iprange_snippets |
383 | + config = dhcp.make_subnet_config( |
384 | + rack_controller, |
385 | + subnet, |
386 | + [factory.make_ipv4_address()], |
387 | + [factory.make_name("ntp")], |
388 | + default_domain, |
389 | + subnets_dhcp_snippets=dhcp_snippets, |
390 | + ) |
391 | + self.assertItemsEqual( |
392 | + [ |
393 | + { |
394 | + "name": dhcp_snippet.name, |
395 | + "description": dhcp_snippet.description, |
396 | + "value": dhcp_snippet.value.data, |
397 | + } |
398 | + for dhcp_snippet in subnet_snippets |
399 | + ], |
400 | + config["dhcp_snippets"], |
401 | + ) |
402 | + self.assertItemsEqual( |
403 | + [ |
404 | + { |
405 | + "name": dhcp_snippet.name, |
406 | + "description": dhcp_snippet.description, |
407 | + "value": dhcp_snippet.value.data, |
408 | + } |
409 | + for dhcp_snippet in iprange_snippets |
410 | + ], |
411 | + config["pools"][0]["dhcp_snippets"], |
412 | + ) |
413 | + |
414 | def test_subnet_without_gateway_restricts_nameservers(self): |
415 | network1 = IPNetwork("10.9.8.0/24") |
416 | network2 = IPNetwork("10.9.9.0/24") |
417 | @@ -2192,6 +2254,7 @@ class TestGetDHCPConfigureFor(MAASServerTestCase): |
418 | "ip_range_high": str(ip_range.end_ip), |
419 | "failover_peer": "failover-vlan-%d" |
420 | % ha_vlan.id, |
421 | + "dhcp_snippets": [], |
422 | } |
423 | for ip_range in ( |
424 | ha_subnet.get_dynamic_ranges().order_by("id") |
425 | @@ -2226,6 +2289,7 @@ class TestGetDHCPConfigureFor(MAASServerTestCase): |
426 | "ip_range_high": str(ip_range.end_ip), |
427 | "failover_peer": "failover-vlan-%d" |
428 | % ha_vlan.id, |
429 | + "dhcp_snippets": [], |
430 | } |
431 | for ip_range in ( |
432 | other_subnet.get_dynamic_ranges().order_by( |
433 | @@ -2350,6 +2414,7 @@ class TestGetDHCPConfigureFor(MAASServerTestCase): |
434 | "ip_range_high": str(ip_range.end_ip), |
435 | "failover_peer": "failover-vlan-%d" |
436 | % ha_vlan.id, |
437 | + "dhcp_snippets": [], |
438 | } |
439 | for ip_range in ( |
440 | ha_subnet.get_dynamic_ranges().order_by("id") |
441 | @@ -2379,6 +2444,7 @@ class TestGetDHCPConfigureFor(MAASServerTestCase): |
442 | "ip_range_high": str(ip_range.end_ip), |
443 | "failover_peer": "failover-vlan-%d" |
444 | % ha_vlan.id, |
445 | + "dhcp_snippets": [], |
446 | } |
447 | for ip_range in ( |
448 | other_subnet.get_dynamic_ranges().order_by( |
449 | @@ -2531,7 +2597,9 @@ class TestConfigureDHCP(MAASTransactionServerTestCase): |
450 | gateway_ip="fd38:c341:27da:c831::1", |
451 | dns_servers=[], |
452 | ) |
453 | - factory.make_IPRange( |
454 | + iprange_v4 = subnet_v4.get_dynamic_ranges().first() |
455 | + iprange_v4.save() |
456 | + iprange_v6 = factory.make_IPRange( |
457 | subnet_v6, |
458 | "fd38:c341:27da:c831:0:1::", |
459 | "fd38:c341:27da:c831:0:1:ffff:0", |
460 | @@ -2561,6 +2629,12 @@ class TestConfigureDHCP(MAASTransactionServerTestCase): |
461 | ) |
462 | |
463 | for _ in range(3): |
464 | + factory.make_DHCPSnippet( |
465 | + subnet=subnet_v4, iprange=iprange_v4, enabled=True |
466 | + ) |
467 | + factory.make_DHCPSnippet( |
468 | + subnet=subnet_v6, iprange=iprange_v6, enabled=True |
469 | + ) |
470 | factory.make_DHCPSnippet(subnet=subnet_v4, enabled=True) |
471 | factory.make_DHCPSnippet(subnet=subnet_v6, enabled=True) |
472 | factory.make_DHCPSnippet(enabled=True) |
473 | @@ -2831,7 +2905,9 @@ class TestValidateDHCPConfig(MAASTransactionServerTestCase): |
474 | subnet_v6 = factory.make_Subnet( |
475 | vlan=vlan, cidr="fd38:c341:27da:c831::/64" |
476 | ) |
477 | - factory.make_IPRange( |
478 | + iprange_v4 = subnet_v4.get_dynamic_ranges().first() |
479 | + iprange_v4.save() |
480 | + iprange_v6 = factory.make_IPRange( |
481 | subnet_v6, |
482 | "fd38:c341:27da:c831:0:1::", |
483 | "fd38:c341:27da:c831:0:1:ffff:0", |
484 | @@ -2859,6 +2935,12 @@ class TestValidateDHCPConfig(MAASTransactionServerTestCase): |
485 | ) |
486 | |
487 | for _ in range(3): |
488 | + factory.make_DHCPSnippet( |
489 | + subnet=subnet_v4, iprange=iprange_v4, enabled=True |
490 | + ) |
491 | + factory.make_DHCPSnippet( |
492 | + subnet=subnet_v6, iprange=iprange_v6, enabled=True |
493 | + ) |
494 | factory.make_DHCPSnippet(subnet=subnet_v4, enabled=True) |
495 | factory.make_DHCPSnippet(subnet=subnet_v6, enabled=True) |
496 | factory.make_DHCPSnippet(enabled=True) |
497 | diff --git a/src/maasserver/websockets/handlers/tests/test_dhcpsnippet.py b/src/maasserver/websockets/handlers/tests/test_dhcpsnippet.py |
498 | index c1f7b34..39567d5 100644 |
499 | --- a/src/maasserver/websockets/handlers/tests/test_dhcpsnippet.py |
500 | +++ b/src/maasserver/websockets/handlers/tests/test_dhcpsnippet.py |
501 | @@ -31,8 +31,11 @@ class TestDHCPSnippetHandler(MAASServerTestCase): |
502 | def dehydrate_dhcp_snippet(self, dhcp_snippet): |
503 | node_system_id = None |
504 | subnet_id = None |
505 | + iprange_id = None |
506 | if dhcp_snippet.subnet is not None: |
507 | subnet_id = dhcp_snippet.subnet.id |
508 | + if dhcp_snippet.iprange is not None: |
509 | + iprange_id = dhcp_snippet.iprange.id |
510 | elif dhcp_snippet.node is not None: |
511 | node_system_id = dhcp_snippet.node.system_id |
512 | return { |
513 | @@ -51,6 +54,7 @@ class TestDHCPSnippetHandler(MAASServerTestCase): |
514 | "enabled": dhcp_snippet.enabled, |
515 | "node": node_system_id, |
516 | "subnet": subnet_id, |
517 | + "iprange": iprange_id, |
518 | "updated": dehydrate_datetime(dhcp_snippet.updated), |
519 | "created": dehydrate_datetime(dhcp_snippet.created), |
520 | } |
521 | diff --git a/src/provisioningserver/dhcp/testing/config.py b/src/provisioningserver/dhcp/testing/config.py |
522 | index 0a2b82a..f44363d 100644 |
523 | --- a/src/provisioningserver/dhcp/testing/config.py |
524 | +++ b/src/provisioningserver/dhcp/testing/config.py |
525 | @@ -26,17 +26,23 @@ def fix_shared_networks_failover(shared_networks, failover_peers): |
526 | return shared_networks |
527 | |
528 | |
529 | -def make_subnet_pool(network, start_ip=None, end_ip=None, failover_peer=None): |
530 | +def make_subnet_pool( |
531 | + network, start_ip=None, end_ip=None, failover_peer=None, dhcp_snippets=None |
532 | +): |
533 | """Return a pool entry for a subnet from network.""" |
534 | if start_ip is None and end_ip is None: |
535 | start_ip, end_ip = factory.make_ip_range(network) |
536 | if failover_peer is None: |
537 | failover_peer = factory.make_name("failover") |
538 | - return { |
539 | + if dhcp_snippets is None: |
540 | + dhcp_snippets = make_pool_dhcp_snippets() |
541 | + pool = { |
542 | "ip_range_low": str(start_ip), |
543 | "ip_range_high": str(end_ip), |
544 | "failover_peer": failover_peer, |
545 | + "dhcp_snippets": dhcp_snippets, |
546 | } |
547 | + return pool |
548 | |
549 | |
550 | def _make_snippets(count, template): |
551 | @@ -66,6 +72,11 @@ def make_host_dhcp_snippets(allow_empty=True): |
552 | return _make_snippets(count, "option smtp-server %s;") |
553 | |
554 | |
555 | +def make_pool_dhcp_snippets(allow_empty=True): |
556 | + count = random.randrange((0 if allow_empty else 1), 3) |
557 | + return _make_snippets(count, "option nntp-server %s;") |
558 | + |
559 | + |
560 | def make_host( |
561 | hostname=None, |
562 | interface_name=None, |
563 | diff --git a/src/provisioningserver/rpc/cluster.py b/src/provisioningserver/rpc/cluster.py |
564 | index a3fda58..b245c61 100644 |
565 | --- a/src/provisioningserver/rpc/cluster.py |
566 | +++ b/src/provisioningserver/rpc/cluster.py |
567 | @@ -347,6 +347,28 @@ class _ConfigureDHCP(amp.Command): |
568 | b"failover_peer", |
569 | amp.Unicode(optional=True), |
570 | ), |
571 | + ( |
572 | + b"dhcp_snippets", |
573 | + AmpList( |
574 | + [ |
575 | + ( |
576 | + b"name", |
577 | + amp.Unicode(), |
578 | + ), |
579 | + ( |
580 | + b"description", |
581 | + amp.Unicode( |
582 | + optional=True |
583 | + ), |
584 | + ), |
585 | + ( |
586 | + b"value", |
587 | + amp.Unicode(), |
588 | + ), |
589 | + ], |
590 | + optional=True, |
591 | + ), |
592 | + ), |
593 | ] |
594 | ), |
595 | ), |
596 | diff --git a/src/provisioningserver/templates/dhcp/dhcpd.conf.template b/src/provisioningserver/templates/dhcp/dhcpd.conf.template |
597 | index 06c886d..a9d85ff 100644 |
598 | --- a/src/provisioningserver/templates/dhcp/dhcpd.conf.template |
599 | +++ b/src/provisioningserver/templates/dhcp/dhcpd.conf.template |
600 | @@ -115,6 +115,19 @@ shared-network {{shared_network["name"]}} { |
601 | {{if pool.get('failover_peer')}} |
602 | failover peer "{{pool['failover_peer']}}"; |
603 | {{endif}} |
604 | + |
605 | + {{if len(pool['dhcp_snippets']) == 0}} |
606 | + # No DHCP snippets for pool |
607 | + {{endif}} |
608 | + {{for dhcp_snippet in pool['dhcp_snippets']}} |
609 | + {{if dhcp_snippet['description'] != ''}} |
610 | + # Description: {{dhcp_snippet['description'] | oneline}} |
611 | + {{endif}} |
612 | + {{for line in dhcp_snippet['value'].splitlines()}} |
613 | + {{line}} |
614 | + {{endfor}} |
615 | + {{endfor}} |
616 | + |
617 | range {{pool['ip_range_low']}} {{pool['ip_range_high']}}; |
618 | } |
619 | {{endfor}} |
620 | diff --git a/src/provisioningserver/templates/dhcp/dhcpd6.conf.template b/src/provisioningserver/templates/dhcp/dhcpd6.conf.template |
621 | index 4a854d3..b33a839 100644 |
622 | --- a/src/provisioningserver/templates/dhcp/dhcpd6.conf.template |
623 | +++ b/src/provisioningserver/templates/dhcp/dhcpd6.conf.template |
624 | @@ -81,6 +81,18 @@ shared-network {{shared_network["name"]}} { |
625 | |
626 | {{for pool in dhcp_subnet['pools']}} |
627 | pool6 { |
628 | + {{if len(pool['dhcp_snippets']) == 0}} |
629 | + # No DHCP snippets for pool |
630 | + {{endif}} |
631 | + {{for dhcp_snippet in pool['dhcp_snippets']}} |
632 | + {{if dhcp_snippet['description'] != ''}} |
633 | + # Description: {{dhcp_snippet['description'] | oneline}} |
634 | + {{endif}} |
635 | + {{for line in dhcp_snippet['value'].splitlines()}} |
636 | + {{line}} |
637 | + {{endfor}} |
638 | + {{endfor}} |
639 | + |
640 | range6 {{pool['ip_range_low']}} {{pool['ip_range_high']}}; |
641 | } |
642 | {{endfor}} |
UNIT TESTS to_nest_ a_dhcp_ snippet_ in_a_pool lp:~cgrabowski/maas/+git/maas into -b master lp:~maas-committers/maas
-b ability_
STATUS: FAILED maas-ci. internal: 8080/job/ maas/job/ branch- tester/ 9977/console 2f6a345df8d7414 5019805a89
LOG: http://
COMMIT: a5f26c9496b47a5