Merge lp:~mpontillo/maas/subnet-dns-override-maas-dns-for-default-gw--bug-1576116--2.0 into lp:maas/2.0

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: no longer in the source branch.
Merged at revision: 5162
Proposed branch: lp:~mpontillo/maas/subnet-dns-override-maas-dns-for-default-gw--bug-1576116--2.0
Merge into: lp:maas/2.0
Diff against target: 366 lines (+225/-17)
5 files modified
docs/changelog.rst (+5/-0)
src/maasserver/models/node.py (+60/-5)
src/maasserver/models/tests/test_node.py (+155/-0)
src/maasserver/preseed_network.py (+3/-6)
src/maasserver/tests/test_preseed_network.py (+2/-6)
To merge this branch: bzr merge lp:~mpontillo/maas/subnet-dns-override-maas-dns-for-default-gw--bug-1576116--2.0
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+300148@code.launchpad.net

Commit message

Merge revision 5178 from trunk.

When deploying a node, override the MAAS DNS server with the DNS servers specified on the subnet for the default gateway, when specifying DNS servers for the loopback interface.

To post a comment you must log in.
Revision history for this message
Mike Pontillo (mpontillo) wrote :

Self-approved backport.

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

There are additional revisions which have not been approved in review. Please seek review and approval of these new revisions.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'docs/changelog.rst'
--- docs/changelog.rst 2016-07-14 23:49:58 +0000
+++ docs/changelog.rst 2016-07-15 02:30:37 +0000
@@ -15,6 +15,8 @@
1515
16LP: #1603147 Commissioning dropdown is grey and checkmarks are missing.16LP: #1603147 Commissioning dropdown is grey and checkmarks are missing.
1717
18LP: #1576116 MAAS adds regions controller as DNS resolver
19
1820
192.0.0 (rc2)212.0.0 (rc2)
20===========22===========
@@ -61,6 +63,9 @@
6163
62LP: #1596046 [2.0] maas 2.0 pxeboot fails on PowerNV64LP: #1596046 [2.0] maas 2.0 pxeboot fails on PowerNV
6365
66LP: #1600267 [1.9,2.0,UX] Can't add aliases when parent interface is set to 'DCHP'
67
68LP: #1598937 [2.0 rc1] Following fresh install maas command fails - PermissionError: [Errno 13] Permission denied: '/home/ubuntu/.maascli.db'
6469
652.0.0 (beta8)702.0.0 (beta8)
66=============71=============
6772
=== modified file 'src/maasserver/models/node.py'
--- src/maasserver/models/node.py 2016-06-17 21:26:46 +0000
+++ src/maasserver/models/node.py 2016-07-15 02:30:37 +0000
@@ -209,8 +209,23 @@
209209
210# Return type from `get_effective_power_info`.210# Return type from `get_effective_power_info`.
211PowerInfo = namedtuple("PowerInfo", (211PowerInfo = namedtuple("PowerInfo", (
212 "can_be_started", "can_be_stopped", "can_be_queried", "power_type",212 "can_be_started",
213 "power_parameters"))213 "can_be_stopped",
214 "can_be_queried",
215 "power_type",
216 "power_parameters",
217))
218
219DefaultGateways = namedtuple("DefaultGateways", (
220 "ipv4",
221 "ipv6",
222))
223
224GatewayDefinition = namedtuple("GatewayDefinition", (
225 "interface_id",
226 "subnet_id",
227 "gateway_ip",
228))
214229
215# The sequence from which the decimal form of node system IDs should be230# The sequence from which the decimal form of node system IDs should be
216# pulled. At the time of writing this should match the definition in migration231# pulled. At the time of writing this should match the definition in migration
@@ -2568,7 +2583,7 @@
2568 interface.id2583 interface.id
2569 """, (self.id,))2584 """, (self.id,))
2570 return [2585 return [
2571 (found[0], found[1], found[2])2586 GatewayDefinition._make((found[0], found[1], found[2]))
2572 for found in cursor.fetchall()2587 for found in cursor.fetchall()
2573 ]2588 ]
25742589
@@ -2620,7 +2635,7 @@
26202635
2621 # Early out if we already have both gateways.2636 # Early out if we already have both gateways.
2622 if gateway_ipv4 and gateway_ipv6:2637 if gateway_ipv4 and gateway_ipv6:
2623 return (gateway_ipv4, gateway_ipv6)2638 return DefaultGateways._make((gateway_ipv4, gateway_ipv6))
26242639
2625 # Get the best guesses for the missing IP families.2640 # Get the best guesses for the missing IP families.
2626 found_gateways = self.get_best_guess_for_default_gateways()2641 found_gateways = self.get_best_guess_for_default_gateways()
@@ -2630,7 +2645,47 @@
2630 if not gateway_ipv6:2645 if not gateway_ipv6:
2631 gateway_ipv6 = self._get_gateway_tuple_by_family(2646 gateway_ipv6 = self._get_gateway_tuple_by_family(
2632 found_gateways, IPADDRESS_FAMILY.IPv6)2647 found_gateways, IPADDRESS_FAMILY.IPv6)
2633 return (gateway_ipv4, gateway_ipv6)2648 return DefaultGateways._make((gateway_ipv4, gateway_ipv6))
2649
2650 def get_default_dns_servers(self):
2651 """Return the default DNS servers for this node."""
2652 # Circular imports.
2653 from maasserver.dns.zonegenerator import get_dns_server_address
2654
2655 gateways = self.get_default_gateways()
2656
2657 # Try first to use DNS servers from default gateway subnets.
2658 if gateways.ipv4 is not None:
2659 subnet = Subnet.objects.get(id=gateways.ipv4.subnet_id)
2660 if subnet.dns_servers is not None and len(subnet.dns_servers) > 0:
2661 # An IPv4 subnet is hosting the default gateway and has DNS
2662 # servers defined. IPv4 DNS servers take first-priority.
2663 return subnet.dns_servers
2664 if gateways.ipv6 is not None:
2665 subnet = Subnet.objects.get(id=gateways.ipv6.subnet_id)
2666 if subnet.dns_servers is not None and len(subnet.dns_servers) > 0:
2667 # An IPv6 subnet is hosting the default gateway and has DNS
2668 # servers defined. IPv6 DNS servers take second-priority.
2669 return subnet.dns_servers
2670
2671 # No default gateway subnet has specific DNS servers defined, so
2672 # use MAAS for the default DNS server.
2673 if gateways.ipv4 is None and gateways.ipv6 is None:
2674 # If there are no default gateways, the default is the MAAS
2675 # region IP address.
2676 maas_dns_server = get_dns_server_address(
2677 rack_controller=self.get_boot_rack_controller())
2678 else:
2679 # Choose an address consistent with the primary address-family
2680 # in use, as indicated by the presence (or not) of a gateway.
2681 # Note that this path is only taken if the MAAS URL is set to
2682 # a hostname, and the hostname resolves to both an IPv4 and an
2683 # IPv6 address.
2684 maas_dns_server = get_dns_server_address(
2685 rack_controller=self.get_boot_rack_controller(),
2686 ipv4=(gateways.ipv4 is not None),
2687 ipv6=(gateways.ipv6 is not None))
2688 return [maas_dns_server]
26342689
2635 def get_boot_purpose(self):2690 def get_boot_purpose(self):
2636 """2691 """
26372692
=== modified file 'src/maasserver/models/tests/test_node.py'
--- src/maasserver/models/tests/test_node.py 2016-06-28 07:12:49 +0000
+++ src/maasserver/models/tests/test_node.py 2016-07-15 02:30:37 +0000
@@ -37,6 +37,7 @@
37from maasserver.enum import (37from maasserver.enum import (
38 FILESYSTEM_GROUP_TYPE,38 FILESYSTEM_GROUP_TYPE,
39 FILESYSTEM_TYPE,39 FILESYSTEM_TYPE,
40 INTERFACE_LINK_TYPE,
40 INTERFACE_TYPE,41 INTERFACE_TYPE,
41 IPADDRESS_TYPE,42 IPADDRESS_TYPE,
42 NODE_PERMISSION,43 NODE_PERMISSION,
@@ -93,6 +94,7 @@
93 NODE_TRANSITIONS,94 NODE_TRANSITIONS,
94)95)
95from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture96from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture
97import maasserver.server_address as server_address_module
96from maasserver.storage_layouts import (98from maasserver.storage_layouts import (
97 StorageLayoutError,99 StorageLayoutError,
98 StorageLayoutMissingBootDiskError,100 StorageLayoutMissingBootDiskError,
@@ -134,6 +136,7 @@
134 commissioning,136 commissioning,
135 disk_erasing,137 disk_erasing,
136)138)
139from netaddr import IPAddress
137from provisioningserver.events import (140from provisioningserver.events import (
138 EVENT_DETAILS,141 EVENT_DETAILS,
139 EVENT_TYPES,142 EVENT_TYPES,
@@ -4101,6 +4104,158 @@
4101 ), node.get_default_gateways())4104 ), node.get_default_gateways())
41024105
41034106
4107class TestGetDefaultDNSServers(MAASServerTestCase):
4108 """Tests for `Node.get_default_dns_servers`."""
4109
4110 def make_Node_with_RackController(
4111 self, ipv4=True, ipv6=True, ipv4_gateway=True, ipv6_gateway=True,
4112 ipv4_subnet_dns=None, ipv6_subnet_dns=None):
4113 ipv4_subnet_dns = [] if ipv4_subnet_dns is None else ipv4_subnet_dns
4114 ipv6_subnet_dns = [] if ipv6_subnet_dns is None else ipv6_subnet_dns
4115 rack_v4 = None
4116 rack_v6 = None
4117 fabric = factory.make_Fabric()
4118 vlan = fabric.get_default_vlan()
4119 if ipv4:
4120 gateway_ip = None if ipv4_gateway else ""
4121 v4_subnet = factory.make_Subnet(
4122 version=4, vlan=vlan, dns_servers=ipv4_subnet_dns,
4123 gateway_ip=gateway_ip)
4124 if ipv6:
4125 gateway_ip = None if ipv6_gateway else ""
4126 v6_subnet = factory.make_Subnet(
4127 version=6, vlan=vlan, dns_servers=ipv6_subnet_dns,
4128 gateway_ip=gateway_ip)
4129 rack = factory.make_RegionRackController()
4130 vlan.primary_rack = rack
4131 vlan.dhcp_on = True
4132 vlan.save()
4133 # In order to determine the correct IP address per-address-family,
4134 # a name lookup is performed on the hostname part of the URL.
4135 # We need to mock that so we can return whatever IP addresses it
4136 # resolves to.
4137 rack.url = "http://region:5240/MAAS/"
4138 if ipv4:
4139 rack_v4 = factory.pick_ip_in_Subnet(v4_subnet)
4140 if ipv6:
4141 rack_v6 = factory.pick_ip_in_Subnet(v6_subnet)
4142
4143 def get_address(hostname, ip_version=4):
4144 """Mock function to return the IP address of the rack based on the
4145 given address family.
4146 """
4147 if ip_version == 4:
4148 return {IPAddress(rack_v4)} if rack_v4 else set()
4149 elif ip_version == 6:
4150 return {IPAddress(rack_v6)} if rack_v6 else set()
4151
4152 resolve_hostname = self.patch(
4153 server_address_module, 'resolve_hostname')
4154 resolve_hostname.side_effect = get_address
4155 rack.interface_set.all().delete()
4156 rackif = factory.make_Interface(vlan=vlan, node=rack)
4157 if ipv4:
4158 rackif.link_subnet(INTERFACE_LINK_TYPE.STATIC, v4_subnet, rack_v4)
4159 if ipv6:
4160 rackif.link_subnet(INTERFACE_LINK_TYPE.STATIC, v6_subnet, rack_v6)
4161 rack.boot_interface = rackif
4162 rack.save()
4163 node = factory.make_Node(status=NODE_STATUS.READY, disable_ipv4=False)
4164 nodeif = factory.make_Interface(vlan=vlan, node=node)
4165 if ipv4:
4166 nodeif.link_subnet(INTERFACE_LINK_TYPE.AUTO, v4_subnet)
4167 if ipv6:
4168 nodeif.link_subnet(INTERFACE_LINK_TYPE.AUTO, v6_subnet)
4169 node.boot_interface = nodeif
4170 node.save()
4171 return rack_v4, rack_v6, node
4172
4173 def test__uses_rack_ipv4_if_ipv4_only_with_no_gateway(self):
4174 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4175 ipv4=True, ipv4_gateway=False, ipv6=False, ipv6_gateway=False)
4176 self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
4177
4178 def test__uses_rack_ipv4_if_ipv4_only_with_no_gateway_v4_dns(self):
4179 ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
4180 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4181 ipv4=True, ipv4_gateway=False, ipv6=False, ipv6_gateway=False,
4182 ipv4_subnet_dns=[ipv4_subnet_dns])
4183 self.assertThat(
4184 node.get_default_dns_servers(), Equals([rack_v4]))
4185
4186 def test__uses_rack_ipv6_if_ipv6_only_with_no_gateway_v6_dns(self):
4187 ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
4188 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4189 ipv4=False, ipv4_gateway=False, ipv6=True, ipv6_gateway=False,
4190 ipv6_subnet_dns=[ipv6_subnet_dns])
4191 self.assertThat(node.get_default_dns_servers(), Equals([rack_v6]))
4192
4193 def test__uses_rack_ipv4_if_dual_stack_with_no_gateway(self):
4194 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4195 ipv4=True, ipv4_gateway=False, ipv6=True, ipv6_gateway=False)
4196 self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
4197
4198 def test__uses_rack_ipv4_if_dual_stack_with_ipv4_gateway(self):
4199 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4200 ipv4=True, ipv4_gateway=True, ipv6=True, ipv6_gateway=False)
4201 self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
4202
4203 def test__uses_subnet_ipv4_if_dual_stack_with_ipv4_gateway_with_dns(self):
4204 ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
4205 ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
4206 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4207 ipv4=True, ipv4_gateway=True, ipv6=True, ipv6_gateway=False,
4208 ipv4_subnet_dns=[ipv4_subnet_dns],
4209 ipv6_subnet_dns=[ipv6_subnet_dns])
4210 self.assertThat(
4211 node.get_default_dns_servers(), Equals([ipv4_subnet_dns]))
4212
4213 def test__uses_rack_ipv6_if_dual_stack_with_ipv6_gateway(self):
4214 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4215 ipv4=True, ipv4_gateway=False, ipv6=True, ipv6_gateway=True)
4216 self.assertThat(node.get_default_dns_servers(), Equals([rack_v6]))
4217
4218 def test__uses_subnet_ipv6_if_dual_stack_with_ipv6_gateway(self):
4219 ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
4220 ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
4221 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4222 ipv4=True, ipv4_gateway=False, ipv6=True, ipv6_gateway=True,
4223 ipv4_subnet_dns=[ipv4_subnet_dns],
4224 ipv6_subnet_dns=[ipv6_subnet_dns])
4225 self.assertThat(
4226 node.get_default_dns_servers(), Equals([ipv6_subnet_dns]))
4227
4228 def test__uses_rack_ipv4_if_ipv4_with_ipv4_gateway(self):
4229 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4230 ipv4=True, ipv4_gateway=True, ipv6=False, ipv6_gateway=False)
4231 self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
4232
4233 def test__uses_subnet_ipv4_if_ipv4_stack_with_ipv4_gateway_and_dns(self):
4234 ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
4235 ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
4236 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4237 ipv4=True, ipv4_gateway=True, ipv6=False, ipv6_gateway=False,
4238 ipv4_subnet_dns=[ipv4_subnet_dns],
4239 ipv6_subnet_dns=[ipv6_subnet_dns])
4240 self.assertThat(
4241 node.get_default_dns_servers(), Equals([ipv4_subnet_dns]))
4242
4243 def test__uses_rack_ipv6_if_ipv6_with_ipv6_gateway(self):
4244 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4245 ipv4=False, ipv4_gateway=False, ipv6=True, ipv6_gateway=True)
4246 self.assertThat(node.get_default_dns_servers(), Equals([rack_v6]))
4247
4248 def test__uses_subnet_ipv6_if_ipv6_with_ipv6_gateway_and_dns(self):
4249 ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
4250 ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
4251 rack_v4, rack_v6, node = self.make_Node_with_RackController(
4252 ipv4=False, ipv4_gateway=False, ipv6=True, ipv6_gateway=True,
4253 ipv4_subnet_dns=[ipv4_subnet_dns],
4254 ipv6_subnet_dns=[ipv6_subnet_dns])
4255 self.assertThat(
4256 node.get_default_dns_servers(), Equals([ipv6_subnet_dns]))
4257
4258
4104class TestNode_Start(MAASServerTestCase):4259class TestNode_Start(MAASServerTestCase):
4105 """Tests for Node.start()."""4260 """Tests for Node.start()."""
41064261
41074262
=== modified file 'src/maasserver/preseed_network.py'
--- src/maasserver/preseed_network.py 2016-03-28 13:54:47 +0000
+++ src/maasserver/preseed_network.py 2016-07-15 02:30:37 +0000
@@ -6,10 +6,7 @@
6__all__ = [6__all__ = [
7 ]7 ]
88
9from maasserver.dns.zonegenerator import (9from maasserver.dns.zonegenerator import get_dns_search_paths
10 get_dns_search_paths,
11 get_dns_server_address,
12)
13from maasserver.enum import (10from maasserver.enum import (
14 INTERFACE_TYPE,11 INTERFACE_TYPE,
15 IPADDRESS_FAMILY,12 IPADDRESS_FAMILY,
@@ -24,6 +21,7 @@
24 def __init__(self, node):21 def __init__(self, node):
25 self.node = node22 self.node = node
26 self.gateways = node.get_default_gateways()23 self.gateways = node.get_default_gateways()
24 self.dns_servers = node.get_default_dns_servers()
27 self.gateway_ipv4_set = False25 self.gateway_ipv4_set = False
28 self.gateway_ipv6_set = False26 self.gateway_ipv6_set = False
29 self.operations = {27 self.operations = {
@@ -51,8 +49,7 @@
5149
52 self.network_config.append({50 self.network_config.append({
53 "type": "nameserver",51 "type": "nameserver",
54 "address": get_dns_server_address(52 "address": self.dns_servers,
55 rack_controller=self.node.get_boot_rack_controller()),
56 "search": sorted(get_dns_search_paths()),53 "search": sorted(get_dns_search_paths()),
57 })54 })
5855
5956
=== modified file 'src/maasserver/tests/test_preseed_network.py'
--- src/maasserver/tests/test_preseed_network.py 2016-04-15 23:38:27 +0000
+++ src/maasserver/tests/test_preseed_network.py 2016-07-15 02:30:37 +0000
@@ -8,10 +8,7 @@
8import random8import random
9from textwrap import dedent9from textwrap import dedent
1010
11from maasserver.dns.zonegenerator import (11from maasserver.dns.zonegenerator import get_dns_search_paths
12 get_dns_search_paths,
13 get_dns_server_address,
14)
15from maasserver.enum import (12from maasserver.enum import (
16 INTERFACE_TYPE,13 INTERFACE_TYPE,
17 IPADDRESS_FAMILY,14 IPADDRESS_FAMILY,
@@ -203,8 +200,7 @@
203200
204 def collectDNSConfig(self, node):201 def collectDNSConfig(self, node):
205 config = "- type: nameserver\n address: %s\n search:\n" % (202 config = "- type: nameserver\n address: %s\n search:\n" % (
206 get_dns_server_address(203 repr(node.get_default_dns_servers()))
207 rack_controller=node.get_boot_primary_rack_controller()))
208 dns_searches = sorted(get_dns_search_paths())204 dns_searches = sorted(get_dns_search_paths())
209 for dns_name in dns_searches:205 for dns_name in dns_searches:
210 config += " - %s\n" % dns_name206 config += " - %s\n" % dns_name

Subscribers

People subscribed via source and target branches

to all changes: