Merge lp:~lamont/maas/bug-1356012 into lp:~maas-committers/maas/trunk
- bug-1356012
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | LaMont Jones |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4621 |
Proposed branch: | lp:~lamont/maas/bug-1356012 |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
661 lines (+238/-26) 14 files modified
src/maasserver/api/subnets.py (+10/-1) src/maasserver/api/tests/test_subnets.py (+7/-0) src/maasserver/dns/tests/test_config.py (+12/-6) src/maasserver/dns/tests/test_zonegenerator.py (+53/-9) src/maasserver/dns/zonegenerator.py (+38/-2) src/maasserver/enum.py (+26/-0) src/maasserver/forms_subnet.py (+5/-0) src/maasserver/migrations/builtin/maasserver/0023_add_rdns_mode.py (+22/-0) src/maasserver/models/subnet.py (+7/-0) src/maasserver/models/tests/test_subnet.py (+5/-2) src/maasserver/testing/factory.py (+3/-2) src/maasserver/websockets/handlers/tests/test_subnet.py (+1/-0) src/provisioningserver/dns/tests/test_zoneconfig.py (+4/-2) src/provisioningserver/dns/zoneconfig.py (+45/-2) |
To merge this branch: | bzr merge lp:~lamont/maas/bug-1356012 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Blake Rouse (community) | Approve | ||
Review via email: mp+284354@code.launchpad.net |
Commit message
Generate rfc2317 glue zones in the DNS by default, allow the admin to disable that if desired.
Description of the change
If a subnet is smaller than a /24 (or /124 for IPv6 networks), then reverse DNS needs to be delegated from the respective /24 (/124) DNS zone. MAAS now defaults to doing that, and allows the admin to override that.
Additionally, the admin can now turn off DNS PTR zone (reverse dns) for an individual Subnet.
LaMont Jones (lamont) wrote : | # |
some replies, everything addressed.
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~lamont/maas/bug-1356012 into lp:maas failed. Below is the output from the failed tests.
Hit:1 http://
Get:2 http://
Hit:3 http://
Hit:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Get:8 http://
Get:9 http://
Get:10 http://
Get:11 http://
Get:12 http://
Fetched 23.7 MB in 7s (3,134 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-1ubuntu1).
authbind is already the newest version (2.1.1+nmu1).
bind9 is already the newest version (1:9.9.
bind9utils is already the newest version (1:9.9.
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu1).
debhelper is already the newest ve...
Preview Diff
1 | === modified file 'src/maasserver/api/subnets.py' | |||
2 | --- src/maasserver/api/subnets.py 2015-12-01 18:12:59 +0000 | |||
3 | +++ src/maasserver/api/subnets.py 2016-01-29 18:06:17 +0000 | |||
4 | @@ -26,6 +26,7 @@ | |||
5 | 26 | 'cidr', | 26 | 'cidr', |
6 | 27 | 'gateway_ip', | 27 | 'gateway_ip', |
7 | 28 | 'dns_servers', | 28 | 'dns_servers', |
8 | 29 | 'rdns_mode', | ||
9 | 29 | ) | 30 | ) |
10 | 30 | 31 | ||
11 | 31 | 32 | ||
12 | @@ -60,7 +61,14 @@ | |||
13 | 60 | :param space: Space this subnet is in. Defaults to the default space. | 61 | :param space: Space this subnet is in. Defaults to the default space. |
14 | 61 | :param cidr: The network CIDR for this subnet. | 62 | :param cidr: The network CIDR for this subnet. |
15 | 62 | :param gateway_ip: The gateway IP address for this subnet. | 63 | :param gateway_ip: The gateway IP address for this subnet. |
17 | 63 | :param dns_servers: Comma-seperated list of DNS servers for this \ | 64 | :param rdns_mode: How reverse DNS is handled for this subnet. |
18 | 65 | One of: 0 (Disabled), 1 (Enabled), or 2 (RFC2317). Disabled means | ||
19 | 66 | no reverse zone is created; Enabled means generate the reverse | ||
20 | 67 | zone; RFC2317 extends Enabled to create the necessary parent zone | ||
21 | 68 | with the appropriate CNAME resource records for the network, if the | ||
22 | 69 | network is small enough to require the support described in | ||
23 | 70 | RFC2317. | ||
24 | 71 | :param dns_servers: Comma-seperated list of DNS servers for this | ||
25 | 64 | subnet. | 72 | subnet. |
26 | 65 | """ | 73 | """ |
27 | 66 | form = SubnetForm(data=request.data) | 74 | form = SubnetForm(data=request.data) |
28 | @@ -111,6 +119,7 @@ | |||
29 | 111 | :param space: Space this subnet is in. | 119 | :param space: Space this subnet is in. |
30 | 112 | :param cidr: The network CIDR for this subnet. | 120 | :param cidr: The network CIDR for this subnet. |
31 | 113 | :param gateway_ip: The gateway IP address for this subnet. | 121 | :param gateway_ip: The gateway IP address for this subnet. |
32 | 122 | :param rdns_mode: How reverse DNS is handled for this subnet. | ||
33 | 114 | :param dns_servers: Comma-seperated list of DNS servers for this \ | 123 | :param dns_servers: Comma-seperated list of DNS servers for this \ |
34 | 115 | subnet. | 124 | subnet. |
35 | 116 | 125 | ||
36 | 117 | 126 | ||
37 | === modified file 'src/maasserver/api/tests/test_subnets.py' | |||
38 | --- src/maasserver/api/tests/test_subnets.py 2016-01-25 14:10:29 +0000 | |||
39 | +++ src/maasserver/api/tests/test_subnets.py 2016-01-29 18:06:17 +0000 | |||
40 | @@ -15,6 +15,7 @@ | |||
41 | 15 | IPADDRESS_TYPE, | 15 | IPADDRESS_TYPE, |
42 | 16 | NODE_STATUS, | 16 | NODE_STATUS, |
43 | 17 | NODEGROUP_STATUS, | 17 | NODEGROUP_STATUS, |
44 | 18 | RDNS_MODE_CHOICES, | ||
45 | 18 | ) | 19 | ) |
46 | 19 | from maasserver.testing.api import ( | 20 | from maasserver.testing.api import ( |
47 | 20 | APITestCase, | 21 | APITestCase, |
48 | @@ -82,6 +83,7 @@ | |||
49 | 82 | space = factory.make_Space() | 83 | space = factory.make_Space() |
50 | 83 | network = factory.make_ip4_or_6_network() | 84 | network = factory.make_ip4_or_6_network() |
51 | 84 | cidr = str(network.cidr) | 85 | cidr = str(network.cidr) |
52 | 86 | rdns_mode = factory.pick_choice(RDNS_MODE_CHOICES) | ||
53 | 85 | gateway_ip = factory.pick_ip_in_network(network) | 87 | gateway_ip = factory.pick_ip_in_network(network) |
54 | 86 | dns_servers = [] | 88 | dns_servers = [] |
55 | 87 | for _ in range(2): | 89 | for _ in range(2): |
56 | @@ -96,6 +98,7 @@ | |||
57 | 96 | "cidr": cidr, | 98 | "cidr": cidr, |
58 | 97 | "gateway_ip": gateway_ip, | 99 | "gateway_ip": gateway_ip, |
59 | 98 | "dns_servers": ','.join(dns_servers), | 100 | "dns_servers": ','.join(dns_servers), |
60 | 101 | "rdns_mode": rdns_mode, | ||
61 | 99 | }) | 102 | }) |
62 | 100 | self.assertEqual( | 103 | self.assertEqual( |
63 | 101 | http.client.OK, response.status_code, response.content) | 104 | http.client.OK, response.status_code, response.content) |
64 | @@ -107,6 +110,7 @@ | |||
65 | 107 | self.assertEqual(cidr, created_subnet['cidr']) | 110 | self.assertEqual(cidr, created_subnet['cidr']) |
66 | 108 | self.assertEqual(gateway_ip, created_subnet['gateway_ip']) | 111 | self.assertEqual(gateway_ip, created_subnet['gateway_ip']) |
67 | 109 | self.assertEqual(dns_servers, created_subnet['dns_servers']) | 112 | self.assertEqual(dns_servers, created_subnet['dns_servers']) |
68 | 113 | self.assertEqual(rdns_mode, created_subnet['rdns_mode']) | ||
69 | 110 | 114 | ||
70 | 111 | def test_create_admin_only(self): | 115 | def test_create_admin_only(self): |
71 | 112 | subnet_name = factory.make_name("subnet") | 116 | subnet_name = factory.make_name("subnet") |
72 | @@ -185,9 +189,11 @@ | |||
73 | 185 | self.become_admin() | 189 | self.become_admin() |
74 | 186 | subnet = factory.make_Subnet() | 190 | subnet = factory.make_Subnet() |
75 | 187 | new_name = factory.make_name("subnet") | 191 | new_name = factory.make_name("subnet") |
76 | 192 | new_rdns_mode = factory.pick_choice(RDNS_MODE_CHOICES) | ||
77 | 188 | uri = get_subnet_uri(subnet) | 193 | uri = get_subnet_uri(subnet) |
78 | 189 | response = self.client.put(uri, { | 194 | response = self.client.put(uri, { |
79 | 190 | "name": new_name, | 195 | "name": new_name, |
80 | 196 | "rdns_mode": new_rdns_mode, | ||
81 | 191 | }) | 197 | }) |
82 | 192 | self.assertEqual( | 198 | self.assertEqual( |
83 | 193 | http.client.OK, response.status_code, response.content) | 199 | http.client.OK, response.status_code, response.content) |
84 | @@ -195,6 +201,7 @@ | |||
85 | 195 | new_name, json.loads( | 201 | new_name, json.loads( |
86 | 196 | response.content.decode(settings.DEFAULT_CHARSET))['name']) | 202 | response.content.decode(settings.DEFAULT_CHARSET))['name']) |
87 | 197 | self.assertEqual(new_name, reload_object(subnet).name) | 203 | self.assertEqual(new_name, reload_object(subnet).name) |
88 | 204 | self.assertEqual(new_rdns_mode, reload_object(subnet).rdns_mode) | ||
89 | 198 | 205 | ||
90 | 199 | def test_update_admin_only(self): | 206 | def test_update_admin_only(self): |
91 | 200 | subnet = factory.make_Subnet() | 207 | subnet = factory.make_Subnet() |
92 | 201 | 208 | ||
93 | === modified file 'src/maasserver/dns/tests/test_config.py' | |||
94 | --- src/maasserver/dns/tests/test_config.py 2016-01-25 12:08:51 +0000 | |||
95 | +++ src/maasserver/dns/tests/test_config.py 2016-01-29 18:06:17 +0000 | |||
96 | @@ -558,8 +558,10 @@ | |||
97 | 558 | commands=['-x', ip, '+short', '-%i' % version]) | 558 | commands=['-x', ip, '+short', '-%i' % version]) |
98 | 559 | return output.split('\n') | 559 | return output.split('\n') |
99 | 560 | 560 | ||
101 | 561 | def assertDNSMatches(self, hostname, domain, ip, version=4): | 561 | def assertDNSMatches(self, hostname, domain, ip, version=-1): |
102 | 562 | # A forward lookup on the hostname returns the IP address. | 562 | # A forward lookup on the hostname returns the IP address. |
103 | 563 | if version == -1: | ||
104 | 564 | version = IPAddress(ip).version | ||
105 | 563 | fqdn = "%s.%s" % (hostname, domain) | 565 | fqdn = "%s.%s" % (hostname, domain) |
106 | 564 | forward_lookup_result = self.dig_resolve(fqdn, version=version) | 566 | forward_lookup_result = self.dig_resolve(fqdn, version=version) |
107 | 565 | self.expectThat( | 567 | self.expectThat( |
108 | @@ -725,7 +727,8 @@ | |||
109 | 725 | 727 | ||
110 | 726 | def test_changing_interface_management_updates_DNS_zone(self): | 728 | def test_changing_interface_management_updates_DNS_zone(self): |
111 | 727 | self.patch(settings, "DNS_CONNECT", True) | 729 | self.patch(settings, "DNS_CONNECT", True) |
113 | 728 | network = IPNetwork('192.168.7.1/24') | 730 | network = factory.make_ip4_or_6_network( |
114 | 731 | host_bits=random.randint(3, 10)) | ||
115 | 729 | ip = factory.pick_ip_in_network(network) | 732 | ip = factory.pick_ip_in_network(network) |
116 | 730 | nodegroup = factory.make_NodeGroup( | 733 | nodegroup = factory.make_NodeGroup( |
117 | 731 | network=network, status=NODEGROUP_STATUS.ENABLED, | 734 | network=network, status=NODEGROUP_STATUS.ENABLED, |
118 | @@ -737,7 +740,8 @@ | |||
119 | 737 | 740 | ||
120 | 738 | def test_delete_nodegroup_disables_DNS_zone(self): | 741 | def test_delete_nodegroup_disables_DNS_zone(self): |
121 | 739 | self.patch(settings, "DNS_CONNECT", True) | 742 | self.patch(settings, "DNS_CONNECT", True) |
123 | 740 | network = IPNetwork('192.168.7.1/24') | 743 | network = factory.make_ip4_or_6_network( |
124 | 744 | host_bits=random.randint(3, 10)) | ||
125 | 741 | ip = factory.pick_ip_in_network(network) | 745 | ip = factory.pick_ip_in_network(network) |
126 | 742 | nodegroup = factory.make_NodeGroup( | 746 | nodegroup = factory.make_NodeGroup( |
127 | 743 | network=network, status=NODEGROUP_STATUS.ENABLED, | 747 | network=network, status=NODEGROUP_STATUS.ENABLED, |
128 | @@ -782,7 +786,8 @@ | |||
129 | 782 | 786 | ||
130 | 783 | def test_bind_configuration_includes_dynamic_ips_of_deployed_nodes(self): | 787 | def test_bind_configuration_includes_dynamic_ips_of_deployed_nodes(self): |
131 | 784 | self.patch(settings, "DNS_CONNECT", True) | 788 | self.patch(settings, "DNS_CONNECT", True) |
133 | 785 | network = IPNetwork('192.168.7.1/24') | 789 | network = factory.make_ip4_or_6_network( |
134 | 790 | host_bits=random.randint(3, 10)) | ||
135 | 786 | nodegroup = self.create_managed_nodegroup(network=network) | 791 | nodegroup = self.create_managed_nodegroup(network=network) |
136 | 787 | [interface] = nodegroup.get_managed_interfaces() | 792 | [interface] = nodegroup.get_managed_interfaces() |
137 | 788 | node = factory.make_Node( | 793 | node = factory.make_Node( |
138 | @@ -805,7 +810,8 @@ | |||
139 | 805 | 810 | ||
140 | 806 | def test_dnsresources_are_in_the_dns(self): | 811 | def test_dnsresources_are_in_the_dns(self): |
141 | 807 | self.patch(settings, "DNS_CONNECT", True) | 812 | self.patch(settings, "DNS_CONNECT", True) |
143 | 808 | network = IPNetwork('192.168.7.1/24') | 813 | network = factory.make_ip4_or_6_network( |
144 | 814 | host_bits=random.randint(3, 10)) | ||
145 | 809 | nodegroup = self.create_managed_nodegroup(network=network) | 815 | nodegroup = self.create_managed_nodegroup(network=network) |
146 | 810 | [interface] = nodegroup.get_managed_interfaces() | 816 | [interface] = nodegroup.get_managed_interfaces() |
147 | 811 | node = factory.make_Node( | 817 | node = factory.make_Node( |
148 | @@ -829,7 +835,7 @@ | |||
149 | 829 | 835 | ||
150 | 830 | def test_bind_configuration_includes_ipv6_zone(self): | 836 | def test_bind_configuration_includes_ipv6_zone(self): |
151 | 831 | self.patch(settings, "DNS_CONNECT", True) | 837 | self.patch(settings, "DNS_CONNECT", True) |
153 | 832 | network = IPNetwork('fe80::/16') | 838 | network = factory.make_ipv6_network(slash=random.randint(118, 125)) |
154 | 833 | nodegroup = self.create_managed_nodegroup(network=network) | 839 | nodegroup = self.create_managed_nodegroup(network=network) |
155 | 834 | nodegroup, node, static = self.create_nodegroup_with_static_ip( | 840 | nodegroup, node, static = self.create_nodegroup_with_static_ip( |
156 | 835 | nodegroup=nodegroup) | 841 | nodegroup=nodegroup) |
157 | 836 | 842 | ||
158 | === modified file 'src/maasserver/dns/tests/test_zonegenerator.py' | |||
159 | --- src/maasserver/dns/tests/test_zonegenerator.py 2016-01-25 14:07:49 +0000 | |||
160 | +++ src/maasserver/dns/tests/test_zonegenerator.py 2016-01-29 18:06:17 +0000 | |||
161 | @@ -27,6 +27,7 @@ | |||
162 | 27 | NODE_STATUS, | 27 | NODE_STATUS, |
163 | 28 | NODEGROUP_STATUS, | 28 | NODEGROUP_STATUS, |
164 | 29 | NODEGROUPINTERFACE_MANAGEMENT, | 29 | NODEGROUPINTERFACE_MANAGEMENT, |
165 | 30 | RDNS_MODE, | ||
166 | 30 | ) | 31 | ) |
167 | 31 | from maasserver.models import ( | 32 | from maasserver.models import ( |
168 | 32 | Config, | 33 | Config, |
169 | @@ -423,7 +424,8 @@ | |||
170 | 423 | self.assertThat( | 424 | self.assertThat( |
171 | 424 | zones, MatchesSetwise( | 425 | zones, MatchesSetwise( |
172 | 425 | forward_zone("henry"), | 426 | forward_zone("henry"), |
174 | 426 | reverse_zone(default_domain, "10/29"))) | 427 | reverse_zone(default_domain, "10/29"), |
175 | 428 | reverse_zone(default_domain, "10/24"))) | ||
176 | 427 | 429 | ||
177 | 428 | def test_with_one_nodegroup_with_node_yields_fwd_and_rev_zone(self): | 430 | def test_with_one_nodegroup_with_node_yields_fwd_and_rev_zone(self): |
178 | 429 | self.useFixture(RegionConfigurationFixture()) | 431 | self.useFixture(RegionConfigurationFixture()) |
179 | @@ -442,7 +444,8 @@ | |||
180 | 442 | self.assertThat( | 444 | self.assertThat( |
181 | 443 | zones, MatchesSetwise( | 445 | zones, MatchesSetwise( |
182 | 444 | forward_zone("henry"), | 446 | forward_zone("henry"), |
184 | 445 | reverse_zone(default_domain, "10/29"))) | 447 | reverse_zone(default_domain, "10/29"), |
185 | 448 | reverse_zone(default_domain, "10/24"))) | ||
186 | 446 | 449 | ||
187 | 447 | def test_returns_interface_ips_but_no_nulls(self): | 450 | def test_returns_interface_ips_but_no_nulls(self): |
188 | 448 | self.useFixture(RegionConfigurationFixture()) | 451 | self.useFixture(RegionConfigurationFixture()) |
189 | @@ -473,7 +476,8 @@ | |||
190 | 473 | self.assertThat( | 476 | self.assertThat( |
191 | 474 | zones, MatchesSetwise( | 477 | zones, MatchesSetwise( |
192 | 475 | forward_zone("henry"), | 478 | forward_zone("henry"), |
194 | 476 | reverse_zone(default_domain, "10/29"))) | 479 | reverse_zone(default_domain, "10/29"), |
195 | 480 | reverse_zone(default_domain, "10/24"))) | ||
196 | 477 | self.assertEqual( | 481 | self.assertEqual( |
197 | 478 | {node.hostname: (30, ['%s' % boot_ip.ip])}, zones[0]._mapping) | 482 | {node.hostname: (30, ['%s' % boot_ip.ip])}, zones[0]._mapping) |
198 | 479 | self.assertEqual( | 483 | self.assertEqual( |
199 | @@ -488,21 +492,37 @@ | |||
200 | 488 | default_ttl, ['%s' % boot_ip.ip])}, | 492 | default_ttl, ['%s' % boot_ip.ip])}, |
201 | 489 | zones[1]._mapping) | 493 | zones[1]._mapping) |
202 | 490 | 494 | ||
203 | 495 | def rfc2317_network(self, network): | ||
204 | 496 | """Returns the network that rfc2317 glue goes in, if any.""" | ||
205 | 497 | net = network | ||
206 | 498 | if net.version == 4 and net.prefixlen > 24: | ||
207 | 499 | net = IPNetwork("%s/24" % net.network) | ||
208 | 500 | net = IPNetwork("%s/24" % net.network) | ||
209 | 501 | if net.version == 6 and net.prefixlen > 124: | ||
210 | 502 | net = IPNetwork("%s/124" % net.network) | ||
211 | 503 | net = IPNetwork("%s/124" % net.network) | ||
212 | 504 | if net != network: | ||
213 | 505 | return net | ||
214 | 506 | return None | ||
215 | 507 | |||
216 | 491 | def test_two_managed_interfaces_yields_one_forward_two_reverse_zones(self): | 508 | def test_two_managed_interfaces_yields_one_forward_two_reverse_zones(self): |
217 | 492 | self.useFixture(RegionConfigurationFixture()) | 509 | self.useFixture(RegionConfigurationFixture()) |
218 | 493 | nodegroup = self.make_node_group() | 510 | nodegroup = self.make_node_group() |
219 | 494 | factory.make_NodeGroupInterface( | 511 | factory.make_NodeGroupInterface( |
220 | 495 | nodegroup=nodegroup, | 512 | nodegroup=nodegroup, |
221 | 496 | management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS) | 513 | management=NODEGROUPINTERFACE_MANAGEMENT.DHCP_AND_DNS) |
223 | 497 | [interface1, interface2] = nodegroup.get_managed_interfaces() | 514 | interfaces = nodegroup.get_managed_interfaces() |
224 | 498 | default_domain = Domain.objects.get_default_domain().name | 515 | default_domain = Domain.objects.get_default_domain().name |
225 | 499 | 516 | ||
226 | 500 | factory.make_Domain(name=nodegroup.name) | 517 | factory.make_Domain(name=nodegroup.name) |
232 | 501 | expected_zones = [ | 518 | expected_zones = [forward_zone(nodegroup.name)] + [ |
233 | 502 | forward_zone(nodegroup.name), | 519 | reverse_zone(default_domain, iface.network) |
234 | 503 | reverse_zone(default_domain, interface1.network), | 520 | for iface in interfaces |
235 | 504 | reverse_zone(default_domain, interface2.network), | 521 | ] + [ |
236 | 505 | ] | 522 | reverse_zone(default_domain, self.rfc2317_network(iface.network)) |
237 | 523 | for iface in interfaces | ||
238 | 524 | if self.rfc2317_network(iface.network) is not None | ||
239 | 525 | ] | ||
240 | 506 | domain = Domain.objects.get(name=nodegroup.name) | 526 | domain = Domain.objects.get(name=nodegroup.name) |
241 | 507 | subnet = Subnet.objects.filter( | 527 | subnet = Subnet.objects.filter( |
242 | 508 | nodegroupinterface__nodegroup=nodegroup) | 528 | nodegroupinterface__nodegroup=nodegroup) |
243 | @@ -541,6 +561,30 @@ | |||
244 | 541 | reverse_zone(default_domain.name, "11/29"), | 561 | reverse_zone(default_domain.name, "11/29"), |
245 | 542 | reverse_zone(default_domain.name, "20/29"), | 562 | reverse_zone(default_domain.name, "20/29"), |
246 | 543 | reverse_zone(default_domain.name, "21/29"), | 563 | reverse_zone(default_domain.name, "21/29"), |
247 | 564 | reverse_zone(default_domain.name, "10/24"), | ||
248 | 565 | reverse_zone(default_domain.name, "11/24"), | ||
249 | 566 | reverse_zone(default_domain.name, "20/24"), | ||
250 | 567 | reverse_zone(default_domain.name, "21/24"), | ||
251 | 568 | ) | ||
252 | 569 | self.assertThat( | ||
253 | 570 | ZoneGenerator(domains, subnets, serial_generator=Mock()).as_list(), | ||
254 | 571 | MatchesSetwise(*expected_zones)) | ||
255 | 572 | |||
256 | 573 | def test_zone_generator_handles_rdns_mode_equal_enabled(self): | ||
257 | 574 | self.useFixture(RegionConfigurationFixture()) | ||
258 | 575 | Domain.objects.get_or_create(name="one") | ||
259 | 576 | nodegroup = self.make_node_group( | ||
260 | 577 | name="one", network=IPNetwork("10/29")) | ||
261 | 578 | subnet = Subnet.objects.get(cidr="10.0.0.0/29") | ||
262 | 579 | subnet.rdns_mode = RDNS_MODE.ENABLED | ||
263 | 580 | subnet.save() | ||
264 | 581 | default_domain = Domain.objects.get_default_domain() | ||
265 | 582 | domains = Domain.objects.filter(name="one") | ||
266 | 583 | subnets = Subnet.objects.filter( | ||
267 | 584 | nodegroupinterface__nodegroup_id=nodegroup.id) | ||
268 | 585 | expected_zones = ( | ||
269 | 586 | forward_zone("one"), | ||
270 | 587 | reverse_zone(default_domain.name, "10/29"), | ||
271 | 544 | ) | 588 | ) |
272 | 545 | self.assertThat( | 589 | self.assertThat( |
273 | 546 | ZoneGenerator(domains, subnets, serial_generator=Mock()).as_list(), | 590 | ZoneGenerator(domains, subnets, serial_generator=Mock()).as_list(), |
274 | 547 | 591 | ||
275 | === modified file 'src/maasserver/dns/zonegenerator.py' | |||
276 | --- src/maasserver/dns/zonegenerator.py 2016-01-25 14:07:49 +0000 | |||
277 | +++ src/maasserver/dns/zonegenerator.py 2016-01-29 18:06:17 +0000 | |||
278 | @@ -16,7 +16,10 @@ | |||
279 | 16 | import socket | 16 | import socket |
280 | 17 | 17 | ||
281 | 18 | from maasserver import logger | 18 | from maasserver import logger |
283 | 19 | from maasserver.enum import NODEGROUP_STATUS | 19 | from maasserver.enum import ( |
284 | 20 | NODEGROUP_STATUS, | ||
285 | 21 | RDNS_MODE, | ||
286 | 22 | ) | ||
287 | 20 | from maasserver.exceptions import MAASException | 23 | from maasserver.exceptions import MAASException |
288 | 21 | from maasserver.models.config import Config | 24 | from maasserver.models.config import Config |
289 | 22 | from maasserver.models.dnsresource import DNSResource | 25 | from maasserver.models.dnsresource import DNSResource |
290 | @@ -287,6 +290,24 @@ | |||
291 | 287 | # 2. node: ip mapping(subnet), including DNSResource records for | 290 | # 2. node: ip mapping(subnet), including DNSResource records for |
292 | 288 | # StaticIPAddresses in this subnet | 291 | # StaticIPAddresses in this subnet |
293 | 289 | # 3. interfaces on any node that have IP addresses in this subnet | 292 | # 3. interfaces on any node that have IP addresses in this subnet |
294 | 293 | rfc2317_glue = {} | ||
295 | 294 | for subnet in subnets: | ||
296 | 295 | network = IPNetwork(subnet.cidr) | ||
297 | 296 | # If this is a small subnet and we are doing RFC2317 glue for it, | ||
298 | 297 | # then we need to combine that with any other such subnets | ||
299 | 298 | # We need to know this before we start creating reverse DNS zones. | ||
300 | 299 | if subnet.rdns_mode == RDNS_MODE.RFC2317: | ||
301 | 300 | if network.version == 4 and network.prefixlen > 24: | ||
302 | 301 | # Turn 192.168.99.32/29 into 192.168.99.0/24 | ||
303 | 302 | basenet = IPNetwork( | ||
304 | 303 | "%s/24" % | ||
305 | 304 | IPNetwork("%s/24" % network.network).network) | ||
306 | 305 | rfc2317_glue.setdefault(basenet, set()).add(network) | ||
307 | 306 | elif network.version == 6 and network.prefixlen > 124: | ||
308 | 307 | basenet = IPNetwork( | ||
309 | 308 | "%s/124" % | ||
310 | 309 | IPNetwork("%s/124" % network.network).network) | ||
311 | 310 | rfc2317_glue.setdefault(basenet, set()).add(network) | ||
312 | 290 | for subnet in subnets: | 311 | for subnet in subnets: |
313 | 291 | network = IPNetwork(subnet.cidr) | 312 | network = IPNetwork(subnet.cidr) |
314 | 292 | nodegroups = set( | 313 | nodegroups = set( |
315 | @@ -333,12 +354,27 @@ | |||
316 | 333 | }) | 354 | }) |
317 | 334 | 355 | ||
318 | 335 | # Use the default_domain as the name for the NS host in the reverse | 356 | # Use the default_domain as the name for the NS host in the reverse |
320 | 336 | # zones. | 357 | # zones. If this network is actually a parent rfc2317 glue |
321 | 358 | # network, then we need to generate the glue records. | ||
322 | 359 | if network in rfc2317_glue: | ||
323 | 360 | glue = rfc2317_glue[network] | ||
324 | 361 | del(rfc2317_glue[network]) | ||
325 | 362 | else: | ||
326 | 363 | glue = set() | ||
327 | 337 | yield DNSReverseZoneConfig( | 364 | yield DNSReverseZoneConfig( |
328 | 338 | Domain.objects.get_default_domain().name, serial=serial, | 365 | Domain.objects.get_default_domain().name, serial=serial, |
329 | 339 | default_ttl=default_ttl, | 366 | default_ttl=default_ttl, |
330 | 340 | mapping=mapping, network=network, | 367 | mapping=mapping, network=network, |
331 | 341 | dynamic_ranges=dynamic_ranges, | 368 | dynamic_ranges=dynamic_ranges, |
332 | 369 | rfc2317_ranges=glue, | ||
333 | 370 | ) | ||
334 | 371 | # Now provide any remaining rfc2317 glue networks. | ||
335 | 372 | for network, ranges in rfc2317_glue.items(): | ||
336 | 373 | yield DNSReverseZoneConfig( | ||
337 | 374 | Domain.objects.get_default_domain().name, serial=serial, | ||
338 | 375 | default_ttl=default_ttl, | ||
339 | 376 | network=network, | ||
340 | 377 | rfc2317_ranges=ranges, | ||
341 | 342 | ) | 378 | ) |
342 | 343 | 379 | ||
343 | 344 | def __iter__(self): | 380 | def __iter__(self): |
344 | 345 | 381 | ||
345 | === modified file 'src/maasserver/enum.py' | |||
346 | --- src/maasserver/enum.py 2016-01-20 00:07:16 +0000 | |||
347 | +++ src/maasserver/enum.py 2016-01-29 18:06:17 +0000 | |||
348 | @@ -27,6 +27,9 @@ | |||
349 | 27 | 'PARTITION_TABLE_TYPE', | 27 | 'PARTITION_TABLE_TYPE', |
350 | 28 | 'PARTITION_TABLE_TYPE_CHOICES', | 28 | 'PARTITION_TABLE_TYPE_CHOICES', |
351 | 29 | 'PRESEED_TYPE', | 29 | 'PRESEED_TYPE', |
352 | 30 | 'RDNS_MODE', | ||
353 | 31 | 'RDNS_MODE_CHOICES', | ||
354 | 32 | 'RDNS_MODE_CHOICES_DICT', | ||
355 | 30 | 'USERDATA_TYPE', | 33 | 'USERDATA_TYPE', |
356 | 31 | ] | 34 | ] |
357 | 32 | 35 | ||
358 | @@ -221,6 +224,29 @@ | |||
359 | 221 | OrderedDict(NODEGROUPINTERFACE_MANAGEMENT_CHOICES)) | 224 | OrderedDict(NODEGROUPINTERFACE_MANAGEMENT_CHOICES)) |
360 | 222 | 225 | ||
361 | 223 | 226 | ||
362 | 227 | class RDNS_MODE: | ||
363 | 228 | """The vocabulary of a `Subnet`'s possible reverse DNS modes.""" | ||
364 | 229 | # By default, we do what we've always done: assume we rule the DNS world. | ||
365 | 230 | DEFAULT = 2 | ||
366 | 231 | #: Do not generate reverse DNS for this Subnet. | ||
367 | 232 | DISABLED = 0 | ||
368 | 233 | #: Generate reverse DNS only for the CIDR. | ||
369 | 234 | ENABLED = 1 | ||
370 | 235 | #: Generate RFC2317 glue if needed (Subnet is too small for its own zone.) | ||
371 | 236 | RFC2317 = 2 | ||
372 | 237 | |||
373 | 238 | |||
374 | 239 | # Django choices for RDNS_MODE: sequence of tuples (key, UI representation.) | ||
375 | 240 | RDNS_MODE_CHOICES = ( | ||
376 | 241 | (RDNS_MODE.DISABLED, "Disabled"), | ||
377 | 242 | (RDNS_MODE.ENABLED, "Enabled"), | ||
378 | 243 | (RDNS_MODE.RFC2317, "Enabled, with rfc2317 glue zone."), | ||
379 | 244 | ) | ||
380 | 245 | |||
381 | 246 | |||
382 | 247 | RDNS_MODE_CHOICES_DICT = OrderedDict(RDNS_MODE_CHOICES) | ||
383 | 248 | |||
384 | 249 | |||
385 | 224 | class IPADDRESS_FAMILY: | 250 | class IPADDRESS_FAMILY: |
386 | 225 | """The vocabulary of possible IP family for `StaticIPAddress`.""" | 251 | """The vocabulary of possible IP family for `StaticIPAddress`.""" |
387 | 226 | IPv4 = 4 | 252 | IPv4 = 4 |
388 | 227 | 253 | ||
389 | === modified file 'src/maasserver/forms_subnet.py' | |||
390 | --- src/maasserver/forms_subnet.py 2015-12-23 17:21:20 +0000 | |||
391 | +++ src/maasserver/forms_subnet.py 2016-01-29 18:06:17 +0000 | |||
392 | @@ -8,6 +8,7 @@ | |||
393 | 8 | ] | 8 | ] |
394 | 9 | 9 | ||
395 | 10 | from django import forms | 10 | from django import forms |
396 | 11 | from maasserver.enum import RDNS_MODE_CHOICES | ||
397 | 11 | from maasserver.fields import IPListFormField | 12 | from maasserver.fields import IPListFormField |
398 | 12 | from maasserver.forms import MAASModelForm | 13 | from maasserver.forms import MAASModelForm |
399 | 13 | from maasserver.models.fabric import Fabric | 14 | from maasserver.models.fabric import Fabric |
400 | @@ -33,6 +34,9 @@ | |||
401 | 33 | space = forms.ModelChoiceField( | 34 | space = forms.ModelChoiceField( |
402 | 34 | queryset=Space.objects.all(), required=False) | 35 | queryset=Space.objects.all(), required=False) |
403 | 35 | 36 | ||
404 | 37 | rdns_mode = forms.ChoiceField( | ||
405 | 38 | choices=RDNS_MODE_CHOICES, required=False) | ||
406 | 39 | |||
407 | 36 | class Meta: | 40 | class Meta: |
408 | 37 | model = Subnet | 41 | model = Subnet |
409 | 38 | fields = ( | 42 | fields = ( |
410 | @@ -42,6 +46,7 @@ | |||
411 | 42 | 'cidr', | 46 | 'cidr', |
412 | 43 | 'gateway_ip', | 47 | 'gateway_ip', |
413 | 44 | 'dns_servers', | 48 | 'dns_servers', |
414 | 49 | 'rdns_mode', | ||
415 | 45 | ) | 50 | ) |
416 | 46 | 51 | ||
417 | 47 | def __init__(self, *args, **kwargs): | 52 | def __init__(self, *args, **kwargs): |
418 | 48 | 53 | ||
419 | === added file 'src/maasserver/migrations/builtin/maasserver/0023_add_rdns_mode.py' | |||
420 | --- src/maasserver/migrations/builtin/maasserver/0023_add_rdns_mode.py 1970-01-01 00:00:00 +0000 | |||
421 | +++ src/maasserver/migrations/builtin/maasserver/0023_add_rdns_mode.py 2016-01-29 18:06:17 +0000 | |||
422 | @@ -0,0 +1,22 @@ | |||
423 | 1 | # -*- coding: utf-8 -*- | ||
424 | 2 | from __future__ import unicode_literals | ||
425 | 3 | |||
426 | 4 | from django.db import ( | ||
427 | 5 | migrations, | ||
428 | 6 | models, | ||
429 | 7 | ) | ||
430 | 8 | |||
431 | 9 | |||
432 | 10 | class Migration(migrations.Migration): | ||
433 | 11 | |||
434 | 12 | dependencies = [ | ||
435 | 13 | ('maasserver', '0022_create_zone_serial_sequence'), | ||
436 | 14 | ] | ||
437 | 15 | |||
438 | 16 | operations = [ | ||
439 | 17 | migrations.AddField( | ||
440 | 18 | model_name='subnet', | ||
441 | 19 | name='rdns_mode', | ||
442 | 20 | field=models.IntegerField(choices=[(0, 'Disabled'), (1, 'Enabled'), (2, 'Enabled, with rfc2317 glue zone.')], default=2), | ||
443 | 21 | ), | ||
444 | 22 | ] | ||
445 | 0 | 23 | ||
446 | === modified file 'src/maasserver/models/subnet.py' | |||
447 | --- src/maasserver/models/subnet.py 2015-12-23 17:21:20 +0000 | |||
448 | +++ src/maasserver/models/subnet.py 2016-01-29 18:06:17 +0000 | |||
449 | @@ -18,6 +18,7 @@ | |||
450 | 18 | from django.db.models import ( | 18 | from django.db.models import ( |
451 | 19 | CharField, | 19 | CharField, |
452 | 20 | ForeignKey, | 20 | ForeignKey, |
453 | 21 | IntegerField, | ||
454 | 21 | Manager, | 22 | Manager, |
455 | 22 | PROTECT, | 23 | PROTECT, |
456 | 23 | Q, | 24 | Q, |
457 | @@ -28,6 +29,8 @@ | |||
458 | 28 | from maasserver.enum import ( | 29 | from maasserver.enum import ( |
459 | 29 | NODEGROUP_STATUS, | 30 | NODEGROUP_STATUS, |
460 | 30 | NODEGROUPINTERFACE_MANAGEMENT, | 31 | NODEGROUPINTERFACE_MANAGEMENT, |
461 | 32 | RDNS_MODE, | ||
462 | 33 | RDNS_MODE_CHOICES, | ||
463 | 31 | ) | 34 | ) |
464 | 32 | from maasserver.fields import ( | 35 | from maasserver.fields import ( |
465 | 33 | CIDRField, | 36 | CIDRField, |
466 | @@ -347,6 +350,10 @@ | |||
467 | 347 | cidr = CIDRField( | 350 | cidr = CIDRField( |
468 | 348 | blank=False, unique=True, editable=True, null=False) | 351 | blank=False, unique=True, editable=True, null=False) |
469 | 349 | 352 | ||
470 | 353 | rdns_mode = IntegerField( | ||
471 | 354 | choices=RDNS_MODE_CHOICES, editable=True, | ||
472 | 355 | default=RDNS_MODE.DEFAULT) | ||
473 | 356 | |||
474 | 350 | gateway_ip = MAASIPAddressField(blank=True, editable=True, null=True) | 357 | gateway_ip = MAASIPAddressField(blank=True, editable=True, null=True) |
475 | 351 | 358 | ||
476 | 352 | dns_servers = ArrayField( | 359 | dns_servers = ArrayField( |
477 | 353 | 360 | ||
478 | === modified file 'src/maasserver/models/tests/test_subnet.py' | |||
479 | --- src/maasserver/models/tests/test_subnet.py 2015-12-01 18:12:59 +0000 | |||
480 | +++ src/maasserver/models/tests/test_subnet.py 2016-01-29 18:06:17 +0000 | |||
481 | @@ -17,6 +17,7 @@ | |||
482 | 17 | NODE_PERMISSION, | 17 | NODE_PERMISSION, |
483 | 18 | NODEGROUP_STATUS, | 18 | NODEGROUP_STATUS, |
484 | 19 | NODEGROUPINTERFACE_MANAGEMENT, | 19 | NODEGROUPINTERFACE_MANAGEMENT, |
485 | 20 | RDNS_MODE_CHOICES, | ||
486 | 20 | ) | 21 | ) |
487 | 21 | from maasserver.models.subnet import ( | 22 | from maasserver.models.subnet import ( |
488 | 22 | create_cidr, | 23 | create_cidr, |
489 | @@ -417,14 +418,16 @@ | |||
490 | 417 | dns_servers = [ | 418 | dns_servers = [ |
491 | 418 | factory.make_ip_address() | 419 | factory.make_ip_address() |
492 | 419 | for _ in range(random.randint(1, 3))] | 420 | for _ in range(random.randint(1, 3))] |
493 | 421 | rdns_mode = factory.pick_choice(RDNS_MODE_CHOICES) | ||
494 | 420 | subnet = Subnet( | 422 | subnet = Subnet( |
495 | 421 | name=name, vlan=vlan, cidr=cidr, gateway_ip=gateway_ip, | 423 | name=name, vlan=vlan, cidr=cidr, gateway_ip=gateway_ip, |
497 | 422 | space=space, dns_servers=dns_servers) | 424 | space=space, dns_servers=dns_servers, rdns_mode=rdns_mode) |
498 | 423 | subnet.save() | 425 | subnet.save() |
499 | 424 | subnet_from_db = Subnet.objects.get(name=name) | 426 | subnet_from_db = Subnet.objects.get(name=name) |
500 | 425 | self.assertThat(subnet_from_db, MatchesStructure.byEquality( | 427 | self.assertThat(subnet_from_db, MatchesStructure.byEquality( |
501 | 426 | name=name, vlan=vlan, cidr=cidr, space=space, | 428 | name=name, vlan=vlan, cidr=cidr, space=space, |
503 | 427 | gateway_ip=gateway_ip, dns_servers=dns_servers)) | 429 | gateway_ip=gateway_ip, dns_servers=dns_servers, |
504 | 430 | rdns_mode=rdns_mode)) | ||
505 | 428 | 431 | ||
506 | 429 | def test_validates_gateway_ip(self): | 432 | def test_validates_gateway_ip(self): |
507 | 430 | error = self.assertRaises( | 433 | error = self.assertRaises( |
508 | 431 | 434 | ||
509 | === modified file 'src/maasserver/testing/factory.py' | |||
510 | --- src/maasserver/testing/factory.py 2016-01-27 17:44:54 +0000 | |||
511 | +++ src/maasserver/testing/factory.py 2016-01-29 18:06:17 +0000 | |||
512 | @@ -37,6 +37,7 @@ | |||
513 | 37 | NODEGROUPINTERFACE_MANAGEMENT, | 37 | NODEGROUPINTERFACE_MANAGEMENT, |
514 | 38 | PARTITION_TABLE_TYPE, | 38 | PARTITION_TABLE_TYPE, |
515 | 39 | POWER_STATE, | 39 | POWER_STATE, |
516 | 40 | RDNS_MODE, | ||
517 | 40 | ) | 41 | ) |
518 | 41 | from maasserver.fields import ( | 42 | from maasserver.fields import ( |
519 | 42 | LargeObjectFile, | 43 | LargeObjectFile, |
520 | @@ -832,7 +833,7 @@ | |||
521 | 832 | 833 | ||
522 | 833 | def make_Subnet(self, name=None, vlan=None, space=None, cidr=None, | 834 | def make_Subnet(self, name=None, vlan=None, space=None, cidr=None, |
523 | 834 | gateway_ip=None, dns_servers=None, host_bits=None, | 835 | gateway_ip=None, dns_servers=None, host_bits=None, |
525 | 835 | fabric=None, vid=None): | 836 | fabric=None, vid=None, rdns_mode=RDNS_MODE.DEFAULT): |
526 | 836 | if name is None: | 837 | if name is None: |
527 | 837 | name = factory.make_name('name') | 838 | name = factory.make_name('name') |
528 | 838 | if vlan is None: | 839 | if vlan is None: |
529 | @@ -849,7 +850,7 @@ | |||
530 | 849 | self.make_ip_address() for _ in range(random.randint(1, 3))] | 850 | self.make_ip_address() for _ in range(random.randint(1, 3))] |
531 | 850 | subnet = Subnet( | 851 | subnet = Subnet( |
532 | 851 | name=name, vlan=vlan, cidr=cidr, gateway_ip=gateway_ip, | 852 | name=name, vlan=vlan, cidr=cidr, gateway_ip=gateway_ip, |
534 | 852 | space=space, dns_servers=dns_servers) | 853 | space=space, dns_servers=dns_servers, rdns_mode=rdns_mode) |
535 | 853 | subnet.save() | 854 | subnet.save() |
536 | 854 | return subnet | 855 | return subnet |
537 | 855 | 856 | ||
538 | 856 | 857 | ||
539 | === modified file 'src/maasserver/websockets/handlers/tests/test_subnet.py' | |||
540 | --- src/maasserver/websockets/handlers/tests/test_subnet.py 2015-12-01 18:12:59 +0000 | |||
541 | +++ src/maasserver/websockets/handlers/tests/test_subnet.py 2016-01-29 18:06:17 +0000 | |||
542 | @@ -25,6 +25,7 @@ | |||
543 | 25 | "dns_servers": [server for server in subnet.dns_servers], | 25 | "dns_servers": [server for server in subnet.dns_servers], |
544 | 26 | "vlan": subnet.vlan_id, | 26 | "vlan": subnet.vlan_id, |
545 | 27 | "space": subnet.space_id, | 27 | "space": subnet.space_id, |
546 | 28 | "rdns_mode": subnet.rdns_mode, | ||
547 | 28 | "cidr": subnet.cidr, | 29 | "cidr": subnet.cidr, |
548 | 29 | "gateway_ip": subnet.gateway_ip, | 30 | "gateway_ip": subnet.gateway_ip, |
549 | 30 | } | 31 | } |
550 | 31 | 32 | ||
551 | === modified file 'src/provisioningserver/dns/tests/test_zoneconfig.py' | |||
552 | --- src/provisioningserver/dns/tests/test_zoneconfig.py 2016-01-19 22:34:22 +0000 | |||
553 | +++ src/provisioningserver/dns/tests/test_zoneconfig.py 2016-01-29 18:06:17 +0000 | |||
554 | @@ -359,7 +359,8 @@ | |||
555 | 359 | factory.make_string(): factory.pick_ip_in_network(network), | 359 | factory.make_string(): factory.pick_ip_in_network(network), |
556 | 360 | } | 360 | } |
557 | 361 | expected = [ | 361 | expected = [ |
559 | 362 | (IPAddress(ip).reverse_dns, 30, '%s.%s.' % (hostname, name)) | 362 | (IPAddress(ip).reverse_dns.split('.')[0], 30, |
560 | 363 | '%s.%s.' % (hostname, name)) | ||
561 | 363 | for hostname, ip in hosts.items() | 364 | for hostname, ip in hosts.items() |
562 | 364 | ] | 365 | ] |
563 | 365 | mapping = { | 366 | mapping = { |
564 | @@ -378,7 +379,8 @@ | |||
565 | 378 | factory.make_string(): factory.pick_ip_in_network(network), | 379 | factory.make_string(): factory.pick_ip_in_network(network), |
566 | 379 | } | 380 | } |
567 | 380 | expected = [ | 381 | expected = [ |
569 | 381 | (IPAddress(ip).reverse_dns, 30, '%s.%s.' % (hostname, name)) | 382 | (IPAddress(ip).reverse_dns.split('.')[0], 30, |
570 | 383 | '%s.%s.' % (hostname, name)) | ||
571 | 382 | for hostname, ip in in_network_mapping.items() | 384 | for hostname, ip in in_network_mapping.items() |
572 | 383 | ] | 385 | ] |
573 | 384 | mapping = { | 386 | mapping = { |
574 | 385 | 387 | ||
575 | === modified file 'src/provisioningserver/dns/zoneconfig.py' | |||
576 | --- src/provisioningserver/dns/zoneconfig.py 2016-01-19 22:34:22 +0000 | |||
577 | +++ src/provisioningserver/dns/zoneconfig.py 2016-01-29 18:06:17 +0000 | |||
578 | @@ -327,10 +327,13 @@ | |||
579 | 327 | :param default_ttl: The default TTL for the zone. | 327 | :param default_ttl: The default TTL for the zone. |
580 | 328 | :param network: The network that the mapping exists within. | 328 | :param network: The network that the mapping exists within. |
581 | 329 | :type network: :class:`netaddr.IPNetwork` | 329 | :type network: :class:`netaddr.IPNetwork` |
582 | 330 | :param rfc2317_ranges: List of ranges to generate RFC2317 CNAMEs for | ||
583 | 331 | :type rfc2317_ranges: [:class:`netaddr.IPNetwork`] | ||
584 | 330 | """ | 332 | """ |
585 | 331 | self._mapping = kwargs.pop('mapping', {}) | 333 | self._mapping = kwargs.pop('mapping', {}) |
586 | 332 | self._network = kwargs.pop("network", None) | 334 | self._network = kwargs.pop("network", None) |
587 | 333 | self._dynamic_ranges = kwargs.pop('dynamic_ranges', []) | 335 | self._dynamic_ranges = kwargs.pop('dynamic_ranges', []) |
588 | 336 | self._rfc2317_ranges = kwargs.pop('rfc2317_ranges', []) | ||
589 | 334 | zone_info = self.compose_zone_info(self._network) | 337 | zone_info = self.compose_zone_info(self._network) |
590 | 335 | super(DNSReverseZoneConfig, self).__init__( | 338 | super(DNSReverseZoneConfig, self).__init__( |
591 | 336 | domain, zone_info=zone_info, **kwargs) | 339 | domain, zone_info=zone_info, **kwargs) |
592 | @@ -428,13 +431,23 @@ | |||
593 | 428 | :param mapping: A hostname: (ttl, [ip-addresseses]) mapping for all | 431 | :param mapping: A hostname: (ttl, [ip-addresseses]) mapping for all |
594 | 429 | known hosts in the reverse zone, to their FQDN (without trailing | 432 | known hosts in the reverse zone, to their FQDN (without trailing |
595 | 430 | dot). | 433 | dot). |
597 | 431 | :param network: DNS Zone's network. | 434 | :param network: DNS Zone's network. (Not a supernet.) |
598 | 432 | :type network: :class:`netaddr.IPNetwork` | 435 | :type network: :class:`netaddr.IPNetwork` |
599 | 433 | """ | 436 | """ |
600 | 437 | def short_name(ip, network): | ||
601 | 438 | long_name = IPAddress(ip).reverse_dns | ||
602 | 439 | if network.version == 4: | ||
603 | 440 | short_name = ".".join(long_name.split('.')[ | ||
604 | 441 | :(31 - network.prefixlen) // 8 + 1]) | ||
605 | 442 | else: | ||
606 | 443 | short_name = ".".join(long_name.split('.')[ | ||
607 | 444 | :(127 - network.prefixlen) // 4 + 1]) | ||
608 | 445 | return short_name | ||
609 | 446 | |||
610 | 434 | if mapping is None: | 447 | if mapping is None: |
611 | 435 | return () | 448 | return () |
612 | 436 | return ( | 449 | return ( |
614 | 437 | (IPAddress(ip).reverse_dns, ttl, '%s.' % (hostname)) | 450 | (short_name(ip, network), ttl, '%s.' % (hostname)) |
615 | 438 | for hostname, ttl, ip in enumerate_mapping(mapping) | 451 | for hostname, ttl, ip in enumerate_mapping(mapping) |
616 | 439 | # Filter out the IP addresses that are not in `network`. | 452 | # Filter out the IP addresses that are not in `network`. |
617 | 440 | if IPAddress(ip) in network | 453 | if IPAddress(ip) in network |
618 | @@ -479,6 +492,32 @@ | |||
619 | 479 | return sorted( | 492 | return sorted( |
620 | 480 | generate_directives, key=lambda directive: directive[2]) | 493 | generate_directives, key=lambda directive: directive[2]) |
621 | 481 | 494 | ||
622 | 495 | @classmethod | ||
623 | 496 | def get_rfc2317_GENERATE_directives(cls, network, rfc2317_ranges, domain): | ||
624 | 497 | """Return the GENERATE directives for any needed rfc2317 glue.""" | ||
625 | 498 | # A non-empty rfc2317_ranges means that the network is the most | ||
626 | 499 | # specific that it can be (IPv4/24 or IPv6/124), so we can make some | ||
627 | 500 | # simplifications in the GENERATE directives. | ||
628 | 501 | generate_directives = set() | ||
629 | 502 | for subnet in rfc2317_ranges: | ||
630 | 503 | if network.version == 4: | ||
631 | 504 | iterator = "%d-%d" % ( | ||
632 | 505 | (subnet.first & 0x000000ff), | ||
633 | 506 | (subnet.last & 0x000000ff)) | ||
634 | 507 | hostname = "$.%d-%d" % ( | ||
635 | 508 | (subnet.first & 0x000000ff), | ||
636 | 509 | subnet.prefixlen) | ||
637 | 510 | generate_directives.add((iterator, '$', hostname)) | ||
638 | 511 | else: | ||
639 | 512 | iterator = "%x-%d" % ( | ||
640 | 513 | (subnet.first & 0x0000000f), | ||
641 | 514 | (subnet.last & 0x0000000f)) | ||
642 | 515 | hostname = "${0,1,x}.%x-%d" % ( | ||
643 | 516 | (subnet.first & 0x0000000f), | ||
644 | 517 | subnet.prefixlen) | ||
645 | 518 | generate_directives.add((iterator, '${0,1,x}', hostname)) | ||
646 | 519 | return sorted(generate_directives) | ||
647 | 520 | |||
648 | 482 | def write_config(self): | 521 | def write_config(self): |
649 | 483 | """Write the zone file.""" | 522 | """Write the zone file.""" |
650 | 484 | # Create GENERATE directives for IPv4 ranges. | 523 | # Create GENERATE directives for IPv4 ranges. |
651 | @@ -502,6 +541,10 @@ | |||
652 | 502 | 'other_mapping': [], | 541 | 'other_mapping': [], |
653 | 503 | 'generate_directives': { | 542 | 'generate_directives': { |
654 | 504 | 'PTR': generate_directives, | 543 | 'PTR': generate_directives, |
655 | 544 | 'CNAME': self.get_rfc2317_GENERATE_directives( | ||
656 | 545 | zi.subnetwork, | ||
657 | 546 | self._rfc2317_ranges, | ||
658 | 547 | self.domain), | ||
659 | 505 | } | 548 | } |
660 | 506 | } | 549 | } |
661 | 507 | ) | 550 | ) |
Overall looks good. Not going to block you on it. Would really like the see the missing documentation be done on the API calls before this lands.