Merge ~cgrabowski/maas:per_zone_dns_forwarders into maas:master
- Git
- lp:~cgrabowski/maas
- per_zone_dns_forwarders
- Merge into master
Status: | Merged |
---|---|
Approved by: | Christian Grabowski |
Approved revision: | 4f028a6f777f468ec4ad546068a552a492ed46e6 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~cgrabowski/maas:per_zone_dns_forwarders |
Merge into: | maas:master |
Diff against target: |
549 lines (+285/-6) 15 files modified
src/maasserver/api/domains.py (+6/-0) src/maasserver/dns/config.py (+22/-1) src/maasserver/dns/tests/test_config.py (+22/-0) src/maasserver/forms/domain.py (+28/-0) src/maasserver/forms/tests/test_domain.py (+32/-0) src/maasserver/migrations/maasserver/0242_forwarddnsserver.py (+42/-0) src/maasserver/models/__init__.py (+2/-0) src/maasserver/models/domain.py (+24/-0) src/maasserver/models/forwarddnsserver.py (+33/-0) src/maasserver/models/tests/test_domain.py (+20/-0) src/maasserver/testing/factory.py (+10/-0) src/provisioningserver/dns/actions.py (+2/-2) src/provisioningserver/dns/config.py (+5/-1) src/provisioningserver/dns/tests/test_config.py (+23/-1) src/provisioningserver/templates/dns/named.conf.template (+14/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexsander de Souza | Approve | ||
MAAS Lander | Approve | ||
Review via email: mp+404251@code.launchpad.net |
Commit message
add forwarded_zones to config
fix form validation unit
add param comments
fix form validation
move forwarddnsserver into its own model to get around bad migration
add test case for no authoritative and forward servers
switch forward_dns_servers to IP type
add forward_dns_servers to domain form
add forward_dns_servers field to domain model
Description of the change
- 3590a2c... by Christian Grabowski
-
format changes
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b per_zone_
STATUS: FAILED
LOG: http://
COMMIT: 3590a2ca641f2b3
- 4f028a6... by Christian Grabowski
-
fix migration collisions
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b per_zone_
STATUS: FAILED
LOG: http://
COMMIT: 72f0180fee99986
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b per_zone_
STATUS: FAILED
LOG: http://
COMMIT: 04ffac177958949
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b per_zone_
STATUS: FAILED
LOG: http://
COMMIT: 606afce58303ed6
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b per_zone_
STATUS: FAILED
LOG: http://
COMMIT: f3d586c66f3dfce
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b per_zone_
STATUS: SUCCESS
COMMIT: 4f028a6f777f468
Alexsander de Souza (alexsander-souza) : | # |
Preview Diff
1 | diff --git a/src/maasserver/api/domains.py b/src/maasserver/api/domains.py |
2 | index 51417c6..a88fe88 100644 |
3 | --- a/src/maasserver/api/domains.py |
4 | +++ b/src/maasserver/api/domains.py |
5 | @@ -62,6 +62,9 @@ class DomainsHandler(OperationsHandler): |
6 | @param (string) "authoritative" [required=false] Class type of the |
7 | domain. |
8 | |
9 | + @param (string) "forward_dns_servers" [required=false] List of forward dns |
10 | + server IP addresses when MAAS is not authorititative. |
11 | + |
12 | @success (http-status-code) "server-success" 200 |
13 | @success (json) "success-json" A JSON object containing the new domain |
14 | object. |
15 | @@ -175,6 +178,9 @@ class DomainHandler(OperationsHandler): |
16 | |
17 | @param (string) "ttl" [required=false] The default TTL for this domain. |
18 | |
19 | + @param (string) "forward_dns_servers" [required=false] List of IP addresses for |
20 | + forward DNS servers when MAAS is not authoritative for this domain. |
21 | + |
22 | @success (http-status-code) "server-success" 200 |
23 | @success (json) "success-json" A JSON object containing |
24 | information about the updated domain. |
25 | diff --git a/src/maasserver/dns/config.py b/src/maasserver/dns/config.py |
26 | index dedfb5f..38693a2 100644 |
27 | --- a/src/maasserver/dns/config.py |
28 | +++ b/src/maasserver/dns/config.py |
29 | @@ -47,6 +47,20 @@ def dns_force_reload(): |
30 | DNSPublication(source="Force reload").save() |
31 | |
32 | |
33 | +def forward_domains_to_forwarded_zones(forward_domains): |
34 | + # converted to a list of tuple to keep model within maasserver code |
35 | + return [ |
36 | + ( |
37 | + domain.name, |
38 | + [ |
39 | + fwd_dns_srvr.ip_address |
40 | + for fwd_dns_srvr in domain.forward_dns_servers |
41 | + ], |
42 | + ) |
43 | + for domain in forward_domains |
44 | + ] |
45 | + |
46 | + |
47 | def dns_update_all_zones(reload_retry=False, reload_timeout=2): |
48 | """Update all zone files for all domains. |
49 | |
50 | @@ -61,6 +75,9 @@ def dns_update_all_zones(reload_retry=False, reload_timeout=2): |
51 | return |
52 | |
53 | domains = Domain.objects.filter(authoritative=True) |
54 | + forwarded_zones = forward_domains_to_forwarded_zones( |
55 | + Domain.objects.get_forward_domains() |
56 | + ) |
57 | subnets = Subnet.objects.exclude(rdns_mode=RDNS_MODE.DISABLED) |
58 | default_ttl = Config.objects.get_config("default_dns_ttl") |
59 | serial = current_zone_serial() |
60 | @@ -87,7 +104,11 @@ def dns_update_all_zones(reload_retry=False, reload_timeout=2): |
61 | # recursive queries to the upstream DNS servers. Again, this is legacy, |
62 | # where the "trusted" ACL ended up in the same configuration file as the |
63 | # zone stanzas, and so both need to be rewritten at the same time. |
64 | - bind_write_configuration(zones, trusted_networks=get_trusted_networks()) |
65 | + bind_write_configuration( |
66 | + zones, |
67 | + trusted_networks=get_trusted_networks(), |
68 | + forwarded_zones=forwarded_zones, |
69 | + ) |
70 | |
71 | # Reloading with retries may be a legacy from Celery days, or it may be |
72 | # necessary to recover from races during start-up. We're not sure if it is |
73 | diff --git a/src/maasserver/dns/tests/test_config.py b/src/maasserver/dns/tests/test_config.py |
74 | index c1f4e3f..7dc69c9 100644 |
75 | --- a/src/maasserver/dns/tests/test_config.py |
76 | +++ b/src/maasserver/dns/tests/test_config.py |
77 | @@ -27,6 +27,7 @@ from maasserver.dns.config import ( |
78 | current_zone_serial, |
79 | dns_force_reload, |
80 | dns_update_all_zones, |
81 | + forward_domains_to_forwarded_zones, |
82 | get_internal_domain, |
83 | get_resource_name_for_subnet, |
84 | get_trusted_acls, |
85 | @@ -78,6 +79,27 @@ class TestDNSUtilities(MAASServerTestCase): |
86 | MatchesStructure.byEquality(source="Force reload"), |
87 | ) |
88 | |
89 | + def test_forward_domains_to_forwarded_zones(self): |
90 | + name1 = factory.make_name("domain") |
91 | + name2 = factory.make_name("other") |
92 | + domain1 = factory.make_Domain(name=name1) |
93 | + domain2 = factory.make_Domain(name=name2) |
94 | + ip1 = factory.make_ip_address() |
95 | + ip2 = factory.make_ip_address() |
96 | + fwd_srvr1 = factory.make_ForwardDNSServer( |
97 | + ip_address=ip1, domains=[domain1] |
98 | + ) |
99 | + fwd_srvr2 = factory.make_ForwardDNSServer( |
100 | + ip_address=ip2, domains=[domain2] |
101 | + ) |
102 | + fwd_zones = forward_domains_to_forwarded_zones( |
103 | + Domain.objects.get_forward_domains() |
104 | + ) |
105 | + self.assertItemsEqual( |
106 | + fwd_zones, |
107 | + [(name1, [fwd_srvr1.ip_address]), (name2, [fwd_srvr2.ip_address])], |
108 | + ) |
109 | + |
110 | |
111 | class TestDNSServer(MAASServerTestCase): |
112 | """A base class to perform real-world DNS-related tests. |
113 | diff --git a/src/maasserver/forms/domain.py b/src/maasserver/forms/domain.py |
114 | index 05ab79c..9a81f9f 100644 |
115 | --- a/src/maasserver/forms/domain.py |
116 | +++ b/src/maasserver/forms/domain.py |
117 | @@ -5,9 +5,12 @@ |
118 | |
119 | |
120 | from django import forms |
121 | +from django.core.exceptions import ValidationError |
122 | |
123 | +from maasserver.fields import IPListFormField |
124 | from maasserver.forms import APIEditMixin, MAASModelForm |
125 | from maasserver.models.domain import Domain |
126 | +from maasserver.models.forwarddnsserver import ForwardDNSServer |
127 | |
128 | |
129 | class DomainForm(MAASModelForm): |
130 | @@ -21,6 +24,31 @@ class DomainForm(MAASModelForm): |
131 | |
132 | authoritative = forms.NullBooleanField(required=False) |
133 | |
134 | + forward_dns_servers = IPListFormField(required=False) |
135 | + |
136 | + def save(self): |
137 | + super(MAASModelForm, self).save() |
138 | + fwd_srvrs = self.cleaned_data.get("forward_dns_servers") |
139 | + if fwd_srvrs is not None: |
140 | + fwd_srvrs_list = fwd_srvrs.split(" ") |
141 | + for fwd_srvr_ip in fwd_srvrs_list: |
142 | + fwd_srvr = ForwardDNSServer.objects.get_or_create( |
143 | + ip_address=fwd_srvr_ip |
144 | + )[0] |
145 | + fwd_srvr.domains.add(self.instance) |
146 | + fwd_srvr.save() |
147 | + del self.cleaned_data["forward_dns_servers"] |
148 | + return self.instance |
149 | + |
150 | + def clean(self): |
151 | + if self.data.get("authoritative") and len( |
152 | + self.data.get("forward_dns_servers", "") |
153 | + ): |
154 | + raise ValidationError( |
155 | + "a domain cannot be both authoritative and have forward dns servers" |
156 | + ) |
157 | + super(DomainForm, self).clean() |
158 | + |
159 | def _post_clean(self): |
160 | # ttl=None needs to make it through. See also APIEditMixin |
161 | self.cleaned_data = { |
162 | diff --git a/src/maasserver/forms/tests/test_domain.py b/src/maasserver/forms/tests/test_domain.py |
163 | index c423600..f50f780 100644 |
164 | --- a/src/maasserver/forms/tests/test_domain.py |
165 | +++ b/src/maasserver/forms/tests/test_domain.py |
166 | @@ -84,3 +84,35 @@ class TestDomainForm(MAASServerTestCase): |
167 | self.assertEqual(name, domain.name) |
168 | self.assertEqual(authoritative, domain.authoritative) |
169 | self.assertEqual(None, domain.ttl) |
170 | + |
171 | + def test_can_create_forward_dns_server(self): |
172 | + name = factory.make_name("domain") |
173 | + forward_dns_servers = [factory.make_ip_address() for _ in range(0, 2)] |
174 | + form = DomainForm( |
175 | + { |
176 | + "name": name, |
177 | + "authoritative": False, |
178 | + "forward_dns_servers": " ".join(forward_dns_servers), |
179 | + } |
180 | + ) |
181 | + self.assertTrue(form.is_valid(), form.errors) |
182 | + domain = form.save() |
183 | + self.assertEqual( |
184 | + forward_dns_servers, |
185 | + [ |
186 | + fwd_dns_srvr.ip_address |
187 | + for fwd_dns_srvr in domain.forward_dns_servers |
188 | + ], |
189 | + ) |
190 | + |
191 | + def test_validate_authority(self): |
192 | + name = factory.make_name("domain") |
193 | + forward_dns_servers = [factory.make_ip_address() for _ in range(0, 2)] |
194 | + form = DomainForm( |
195 | + { |
196 | + "name": name, |
197 | + "authoritative": True, |
198 | + "forward_dns_servers": " ".join(forward_dns_servers), |
199 | + } |
200 | + ) |
201 | + self.assertRaises(ValueError, form.save) |
202 | diff --git a/src/maasserver/migrations/maasserver/0242_forwarddnsserver.py b/src/maasserver/migrations/maasserver/0242_forwarddnsserver.py |
203 | new file mode 100644 |
204 | index 0000000..dc5fdd9 |
205 | --- /dev/null |
206 | +++ b/src/maasserver/migrations/maasserver/0242_forwarddnsserver.py |
207 | @@ -0,0 +1,42 @@ |
208 | +# Generated by Django 2.2.12 on 2021-06-14 23:05 |
209 | + |
210 | +from django.db import migrations, models |
211 | + |
212 | +import maasserver.models.cleansave |
213 | + |
214 | + |
215 | +class Migration(migrations.Migration): |
216 | + |
217 | + dependencies = [ |
218 | + ("maasserver", "0241_physical_interface_default_node_numanode"), |
219 | + ] |
220 | + |
221 | + operations = [ |
222 | + migrations.CreateModel( |
223 | + name="ForwardDNSServer", |
224 | + fields=[ |
225 | + ( |
226 | + "id", |
227 | + models.AutoField( |
228 | + auto_created=True, |
229 | + primary_key=True, |
230 | + serialize=False, |
231 | + verbose_name="ID", |
232 | + ), |
233 | + ), |
234 | + ("created", models.DateTimeField(editable=False)), |
235 | + ("updated", models.DateTimeField(editable=False)), |
236 | + ( |
237 | + "ip_address", |
238 | + models.GenericIPAddressField( |
239 | + default=None, editable=False, unique=True |
240 | + ), |
241 | + ), |
242 | + ("domains", models.ManyToManyField(to="maasserver.Domain")), |
243 | + ], |
244 | + options={ |
245 | + "abstract": False, |
246 | + }, |
247 | + bases=(maasserver.models.cleansave.CleanSave, models.Model), |
248 | + ), |
249 | + ] |
250 | diff --git a/src/maasserver/models/__init__.py b/src/maasserver/models/__init__.py |
251 | index e3f96e6..29904d0 100644 |
252 | --- a/src/maasserver/models/__init__.py |
253 | +++ b/src/maasserver/models/__init__.py |
254 | @@ -34,6 +34,7 @@ __all__ = [ |
255 | "FileStorage", |
256 | "Filesystem", |
257 | "FilesystemGroup", |
258 | + "ForwardDNSServer", |
259 | "GlobalDefault", |
260 | "Interface", |
261 | "IPRange", |
262 | @@ -136,6 +137,7 @@ from maasserver.models.filesystemgroup import ( |
263 | VMFS, |
264 | VolumeGroup, |
265 | ) |
266 | +from maasserver.models.forwarddnsserver import ForwardDNSServer |
267 | from maasserver.models.globaldefault import GlobalDefault |
268 | from maasserver.models.interface import ( |
269 | BondInterface, |
270 | diff --git a/src/maasserver/models/domain.py b/src/maasserver/models/domain.py |
271 | index a131a5b..6e510e8 100644 |
272 | --- a/src/maasserver/models/domain.py |
273 | +++ b/src/maasserver/models/domain.py |
274 | @@ -97,6 +97,15 @@ class DomainManager(Manager, DomainQueriesMixin): |
275 | |
276 | return GlobalDefault.objects.instance().domain |
277 | |
278 | + def get_forward_domains(self): |
279 | + rows = self.raw( |
280 | + """SELECT * FROM maasserver_domain domain |
281 | + WHERE EXISTS ( |
282 | + SELECT id FROM maasserver_forwarddnsserver_domains WHERE domain_id = domain.id |
283 | + );""" |
284 | + ) |
285 | + return list(rows) |
286 | + |
287 | def get_or_create_default_domain(self): |
288 | """Return the default domain.""" |
289 | now = datetime.datetime.now() |
290 | @@ -240,6 +249,13 @@ class Domain(CleanSave, TimestampedModel): |
291 | count += len(resource.dnsdata_set.all()) |
292 | return count |
293 | |
294 | + @property |
295 | + def forward_dns_servers(self): |
296 | + # avoid circular import |
297 | + from maasserver.models.forwarddnsserver import ForwardDNSServer |
298 | + |
299 | + return ForwardDNSServer.objects.filter(domains=self) |
300 | + |
301 | def add_delegations(self, mapping, ns_host_name, dns_ip_list, default_ttl): |
302 | """Find any subdomains that need to be added to this domain, and add |
303 | them. |
304 | @@ -366,9 +382,17 @@ class Domain(CleanSave, TimestampedModel): |
305 | if self.name is not None and self.name.endswith("."): |
306 | self.name = self.name[:-1] |
307 | |
308 | + def validate_authority(self): |
309 | + if self.authoritative and len(self.forward_dns_servers) > 0: |
310 | + raise ValidationError( |
311 | + "A Domain cannot be both authoritative and have" |
312 | + "forward DNS servers" |
313 | + ) |
314 | + |
315 | def clean(self, *args, **kwargs): |
316 | super().clean(*args, **kwargs) |
317 | self.clean_name() |
318 | + self.validate_authority() |
319 | |
320 | def render_json_for_related_rrdata( |
321 | self, for_list=False, include_dnsdata=True, as_dict=False, user=None |
322 | diff --git a/src/maasserver/models/forwarddnsserver.py b/src/maasserver/models/forwarddnsserver.py |
323 | new file mode 100644 |
324 | index 0000000..9fe07be |
325 | --- /dev/null |
326 | +++ b/src/maasserver/models/forwarddnsserver.py |
327 | @@ -0,0 +1,33 @@ |
328 | +""" Forward DNS Server Objects.""" |
329 | + |
330 | +__all__ = [ |
331 | + "ForwardDNSServerManager", |
332 | + "ForwardDNSServer", |
333 | +] |
334 | + |
335 | + |
336 | +from django.db.models import GenericIPAddressField, Manager, ManyToManyField |
337 | + |
338 | +from maasserver.models.cleansave import CleanSave |
339 | +from maasserver.models.domain import Domain |
340 | +from maasserver.models.timestampedmodel import TimestampedModel |
341 | + |
342 | + |
343 | +class ForwardDNSServerManager(Manager): |
344 | + pass |
345 | + |
346 | + |
347 | +# Due to migration 0155 calling Domain's manager directly, we cannot add forward dns servers as a column to a Domain |
348 | +# so a separate table where one or more ForwardDNSServer(s) can be used in many Domains. |
349 | +class ForwardDNSServer(CleanSave, TimestampedModel): |
350 | + """A `ForwardDNSServer`. |
351 | + :ivar ip_address: The IP address of the forward DNS server to forward queries to. |
352 | + :ivar domains: A many to many reference to domains that forward to this server.""" |
353 | + |
354 | + objects = ForwardDNSServerManager() |
355 | + |
356 | + ip_address = GenericIPAddressField( |
357 | + null=False, default=None, editable=False, unique=True |
358 | + ) |
359 | + |
360 | + domains = ManyToManyField(Domain) |
361 | diff --git a/src/maasserver/models/tests/test_domain.py b/src/maasserver/models/tests/test_domain.py |
362 | index 840e792..356042d 100644 |
363 | --- a/src/maasserver/models/tests/test_domain.py |
364 | +++ b/src/maasserver/models/tests/test_domain.py |
365 | @@ -135,6 +135,15 @@ class TestDomainManager(MAASServerTestCase): |
366 | Domain.objects.filter_by_specifiers("name:%s" % name), [domain] |
367 | ) |
368 | |
369 | + def test_get_forward_domains(self): |
370 | + name1 = factory.make_name("domain") |
371 | + domain1 = factory.make_Domain(name=name1, authoritative=False) |
372 | + name2 = factory.make_name("other") |
373 | + factory.make_Domain(name=name2, authoritative=True) |
374 | + fwd_ip = factory.make_ip_address() |
375 | + factory.make_ForwardDNSServer(ip_address=fwd_ip, domains=[domain1]) |
376 | + self.assertItemsEqual(Domain.objects.get_forward_domains(), [domain1]) |
377 | + |
378 | |
379 | class DomainTest(MAASServerTestCase): |
380 | def test_creates_domain(self): |
381 | @@ -181,6 +190,17 @@ class DomainTest(MAASServerTestCase): |
382 | domain.delete() |
383 | self.assertItemsEqual([], Domain.objects.filter(name=name)) |
384 | |
385 | + def test_validate_authority_raises_exception_when_both_authoritative_and_has_forward_dns_servers( |
386 | + self, |
387 | + ): |
388 | + name = factory.make_name("name") |
389 | + forward_server = factory.make_ip_address() |
390 | + domain = factory.make_Domain(name=name, authoritative=True) |
391 | + factory.make_ForwardDNSServer( |
392 | + ip_address=forward_server, domains=[domain] |
393 | + ) |
394 | + self.assertRaises(ValidationError, domain.validate_authority) |
395 | + |
396 | def test_cant_be_deleted_if_contains_resources(self): |
397 | domain = factory.make_Domain() |
398 | factory.make_DNSResource(domain=domain) |
399 | diff --git a/src/maasserver/testing/factory.py b/src/maasserver/testing/factory.py |
400 | index 929c159..bdefa86 100644 |
401 | --- a/src/maasserver/testing/factory.py |
402 | +++ b/src/maasserver/testing/factory.py |
403 | @@ -67,6 +67,7 @@ from maasserver.models import ( |
404 | FileStorage, |
405 | Filesystem, |
406 | FilesystemGroup, |
407 | + ForwardDNSServer, |
408 | IPRange, |
409 | KeySource, |
410 | LargeFile, |
411 | @@ -703,6 +704,15 @@ class Factory(maastesting.factory.Factory): |
412 | domain.save() |
413 | return domain |
414 | |
415 | + def make_ForwardDNSServer(self, ip_address=None, domains=None): |
416 | + if ip_address is None: |
417 | + ip_address = self.make_ip_address() |
418 | + fwd_dns_srvr = ForwardDNSServer(ip_address=ip_address) |
419 | + fwd_dns_srvr.save() |
420 | + fwd_dns_srvr.domains.set(domains) |
421 | + fwd_dns_srvr.save() |
422 | + return fwd_dns_srvr |
423 | + |
424 | def pick_rrset(self, rrtype=None, rrdata=None, exclude=[]): |
425 | while rrtype is None: |
426 | rrtype = self.pick_choice( |
427 | diff --git a/src/provisioningserver/dns/actions.py b/src/provisioningserver/dns/actions.py |
428 | index eabb3e8..7b8b1ca 100644 |
429 | --- a/src/provisioningserver/dns/actions.py |
430 | +++ b/src/provisioningserver/dns/actions.py |
431 | @@ -92,7 +92,7 @@ def bind_reload_zones(zone_list): |
432 | return ret |
433 | |
434 | |
435 | -def bind_write_configuration(zones, trusted_networks): |
436 | +def bind_write_configuration(zones, trusted_networks, forwarded_zones=None): |
437 | """Write BIND's configuration. |
438 | |
439 | :param zones: Those zones to include in main config. |
440 | @@ -107,7 +107,7 @@ def bind_write_configuration(zones, trusted_networks): |
441 | assert not isinstance(trusted_networks, (bytes, str)) |
442 | assert isinstance(trusted_networks, Sequence) |
443 | |
444 | - dns_config = DNSConfig(zones=zones) |
445 | + dns_config = DNSConfig(zones=zones, forwarded_zones=forwarded_zones) |
446 | dns_config.write_config(trusted_networks=trusted_networks) |
447 | |
448 | |
449 | diff --git a/src/provisioningserver/dns/config.py b/src/provisioningserver/dns/config.py |
450 | index 904ae8d..fe4c5a2 100644 |
451 | --- a/src/provisioningserver/dns/config.py |
452 | +++ b/src/provisioningserver/dns/config.py |
453 | @@ -289,10 +289,13 @@ class DNSConfig: |
454 | template_file_name = "named.conf.template" |
455 | target_file_name = MAAS_NAMED_CONF_NAME |
456 | |
457 | - def __init__(self, zones=None): |
458 | + def __init__(self, zones=None, forwarded_zones=None): |
459 | if zones is None: |
460 | zones = () |
461 | + if forwarded_zones is None: |
462 | + forwarded_zones = () |
463 | self.zones = zones |
464 | + self.forwarded_zones = forwarded_zones |
465 | |
466 | def write_config(self, overwrite=True, **kwargs): |
467 | """Write out this DNS config file. |
468 | @@ -303,6 +306,7 @@ class DNSConfig: |
469 | trusted_networks = kwargs.pop("trusted_networks", "") |
470 | context = { |
471 | "zones": self.zones, |
472 | + "forwarded_zones": self.forwarded_zones, |
473 | "DNS_CONFIG_DIR": get_dns_config_dir(), |
474 | "named_rndc_conf_path": get_named_rndc_conf_path(), |
475 | "trusted_networks": trusted_networks, |
476 | diff --git a/src/provisioningserver/dns/tests/test_config.py b/src/provisioningserver/dns/tests/test_config.py |
477 | index 042df69..97fa15b 100644 |
478 | --- a/src/provisioningserver/dns/tests/test_config.py |
479 | +++ b/src/provisioningserver/dns/tests/test_config.py |
480 | @@ -63,7 +63,7 @@ from provisioningserver.dns.zoneconfig import ( |
481 | DNSReverseZoneConfig, |
482 | ) |
483 | from provisioningserver.utils import locate_config |
484 | -from provisioningserver.utils.isc import read_isc_file |
485 | +from provisioningserver.utils.isc import parse_isc_string, read_isc_file |
486 | |
487 | NAMED_CONF_OPTIONS_CONTENTS = dedent( |
488 | """\ |
489 | @@ -547,6 +547,28 @@ class TestDNSConfig(MAASTestCase): |
490 | ), |
491 | ) |
492 | |
493 | + def test_write_config_with_forwarded_zones(self): |
494 | + name = factory.make_name("domain") |
495 | + ip = factory.make_ip_address() |
496 | + forwarded_zones = [(name, [ip])] |
497 | + target_dir = patch_dns_config_path(self) |
498 | + DNSConfig(forwarded_zones=forwarded_zones).write_config() |
499 | + config_path = os.path.join(target_dir, MAAS_NAMED_CONF_NAME) |
500 | + expected_content = dedent( |
501 | + f""" |
502 | + zone "{name}" {{ |
503 | + type forward; |
504 | + forward only; |
505 | + forwarders {{ |
506 | + {ip}; |
507 | + }}; |
508 | + }}; |
509 | + """ |
510 | + ) |
511 | + config = read_isc_file(config_path) |
512 | + expected = parse_isc_string(expected_content) |
513 | + self.assertEqual(expected[f'zone "{name}"'], config[f'zone "{name}"']) |
514 | + |
515 | def test_write_config_makes_config_world_readable(self): |
516 | target_dir = patch_dns_config_path(self) |
517 | DNSConfig().write_config() |
518 | diff --git a/src/provisioningserver/templates/dns/named.conf.template b/src/provisioningserver/templates/dns/named.conf.template |
519 | index 938d772..f0f9841 100644 |
520 | --- a/src/provisioningserver/templates/dns/named.conf.template |
521 | +++ b/src/provisioningserver/templates/dns/named.conf.template |
522 | @@ -1,6 +1,6 @@ |
523 | include "{{named_rndc_conf_path}}"; |
524 | |
525 | -# Zone declarations. |
526 | +# Authoritative Zone declarations. |
527 | {{for zone in zones}} |
528 | {{for zoneinfo in zone.zone_info}} |
529 | zone "{{zoneinfo.zone_name}}" { |
530 | @@ -10,6 +10,19 @@ zone "{{zoneinfo.zone_name}}" { |
531 | {{endfor}} |
532 | {{endfor}} |
533 | |
534 | +# Forwarded Zone declarations. |
535 | +{{for forwarded_zone in forwarded_zones}} |
536 | +zone "{{forwarded_zone[0]}}" { |
537 | + type forward; |
538 | + forward only; |
539 | + forwarders { |
540 | + {{for forward_server in forwarded_zone[1]}} |
541 | + {{forward_server}}; |
542 | + {{endfor}} |
543 | + }; |
544 | +}; |
545 | +{{endfor}} |
546 | + |
547 | # Access control for recursive queries. See named.conf.options.inside.maas |
548 | # for the directives used on this ACL. |
549 | acl "trusted" { |
UNIT TESTS dns_forwarders lp:~cgrabowski/maas/+git/maas into -b master lp:~maas-committers/maas
-b per_zone_
STATUS: FAILED maas-ci. internal: 8080/job/ maas/job/ branch- tester/ 10253/console e53ea495d9c6e8b 83d9d75b3a
LOG: http://
COMMIT: d3077cac07a6f9c