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

Proposed by Mike Pontillo on 2016-07-13
Status: Merged
Approved by: Mike Pontillo on 2016-07-15
Approved revision: 5177
Merged at revision: 5178
Proposed branch: lp:~mpontillo/maas/subnet-dns-override-maas-dns-for-default-gw--bug-1576116
Merge into: lp:maas/trunk
Diff against target: 343 lines (+220/-17)
4 files modified
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
Reviewer Review Type Date Requested Status
Gavin Panella (community) 2016-07-13 Approve on 2016-07-14
Review via email: mp+299896@code.launchpad.net

Commit message

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.
Mike Pontillo (mpontillo) wrote :

I think this is ready for review now. This branch implements the work we discussed the other day: making curtin's default DNS servers (for the loopback interface) consistent with the DNS servers on the default gateway's subnet. Previously, the DNS server was always the rack-facing MAAS IP address.

The code was relatively simple, but it was pretty gnarly to unit test all the different permutations.

Gavin Panella (allenap) wrote :

Looks good. A few comments but they're all minor.

review: Approve
Mike Pontillo (mpontillo) wrote :

Thanks for the review. Some replies below.

MAAS Lander (maas-lander) wrote :
Download full text (1.3 MiB)

The attempt to merge lp:~mpontillo/maas/subnet-dns-override-maas-dns-for-default-gw--bug-1576116 into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease
Hit:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease
Hit:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Get:4 http://security.ubuntu.com/ubuntu xenial-security InRelease [94.5 kB]
Fetched 94.5 kB in 0s (225 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash bind9 bind9utils build-essential bzr bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm postgresql pxelinux python3-all python3-apt python3-bson python3-convoy python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-netaddr python3-netifaces python3-novaclient python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-simplejson python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173).
pxelinux is already the newest version (3:6.03+dfsg-11ubuntu1).
python-django is already the newest version (1.8.7-1ubuntu5)...

MAAS Lander (maas-lander) wrote :
Download full text (31.5 KiB)

The attempt to merge lp:~mpontillo/maas/subnet-dns-override-maas-dns-for-default-gw--bug-1576116 into lp:maas failed. Below is the output from the failed tests.

Get:1 http://security.ubuntu.com/ubuntu xenial-security InRelease [94.5 kB]
Hit:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease
Get:3 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease [95.7 kB]
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Get:5 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates/main amd64 Packages [315 kB]
Get:6 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates/universe amd64 Packages [293 kB]
Fetched 799 kB in 0s (1,768 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash bind9 bind9utils build-essential bzr bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm postgresql pxelinux python3-all python3-apt python3-bson python3-convoy python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-netaddr python3-netifaces python3-novaclient python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-simplejson python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1u...

MAAS Lander (maas-lander) wrote :
Download full text (29.2 KiB)

The attempt to merge lp:~mpontillo/maas/subnet-dns-override-maas-dns-for-default-gw--bug-1576116 into lp:maas failed. Below is the output from the failed tests.

Hit:1 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial InRelease
Get:2 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-updates InRelease [95.7 kB]
Get:3 http://security.ubuntu.com/ubuntu xenial-security InRelease [94.5 kB]
Hit:4 http://prodstack-zone-2.clouds.archive.ubuntu.com/ubuntu xenial-backports InRelease
Fetched 190 kB in 0s (409 kB/s)
Reading package lists...
sudo DEBIAN_FRONTEND=noninteractive apt-get -y \
    --no-install-recommends install apache2 archdetect-deb authbind bash bind9 bind9utils build-essential bzr bzr-builddeb chromium-browser chromium-chromedriver curl daemontools debhelper dh-apport dh-systemd distro-info dnsutils firefox freeipmi-tools git gjs ipython isc-dhcp-common libjs-angularjs libjs-jquery libjs-jquery-hotkeys libjs-yui3-full libjs-yui3-min libpq-dev make nodejs-legacy npm postgresql pxelinux python3-all python3-apt python3-bson python3-convoy python3-crochet python3-cssselect python3-curtin python3-dev python3-distro-info python3-django python3-django-nose python3-django-piston3 python3-dnspython python3-docutils python3-formencode python3-hivex python3-httplib2 python3-jinja2 python3-jsonschema python3-lxml python3-netaddr python3-netifaces python3-novaclient python3-oauth python3-oauthlib python3-openssl python3-paramiko python3-petname python3-pexpect python3-psycopg2 python3-pyinotify python3-pyparsing python3-pyvmomi python3-requests python3-seamicroclient python3-setuptools python3-simplestreams python3-sphinx python3-tempita python3-twisted python3-txtftp python3-tz python3-yaml python3-zope.interface python-bson python-crochet python-django python-django-piston python-djorm-ext-pgarray python-formencode python-lxml python-netaddr python-netifaces python-pocket-lint python-psycopg2 python-simplejson python-tempita python-twisted python-yaml socat syslinux-common tgt ubuntu-cloudimage-keyring wget xvfb
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is already the newest version (2.4.18-2ubuntu3).
archdetect-deb is already the newest version (1.117ubuntu2).
authbind is already the newest version (2.1.1+nmu1).
build-essential is already the newest version (12.1ubuntu2).
curl is already the newest version (7.47.0-1ubuntu2).
debhelper is already the newest version (9.20160115ubuntu3).
distro-info is already the newest version (0.14build1).
freeipmi-tools is already the newest version (1.4.11-1ubuntu1).
git is already the newest version (1:2.7.4-0ubuntu1).
isc-dhcp-common is already the newest version (4.3.3-5ubuntu12).
libjs-angularjs is already the newest version (1.2.28-1ubuntu2).
libjs-jquery is already the newest version (1.11.3+dfsg-4).
libjs-yui3-full is already the newest version (3.5.1-1ubuntu3).
libjs-yui3-min is already the newest version (3.5.1-1ubuntu3).
make is already the newest version (4.1-6).
postgresql is already the newest version (9.5+173).
pxelinux is already the newest version (3:6.03+dfsg-11ubuntu1).
python-django is already the newest version (1.8.7-...

Mike Pontillo (mpontillo) wrote :

Strange error. Trying again...

https://paste.ubuntu.com/19438855/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/maasserver/models/node.py'
2--- src/maasserver/models/node.py 2016-07-06 17:22:11 +0000
3+++ src/maasserver/models/node.py 2016-07-15 00:08:33 +0000
4@@ -215,8 +215,23 @@
5
6 # Return type from `get_effective_power_info`.
7 PowerInfo = namedtuple("PowerInfo", (
8- "can_be_started", "can_be_stopped", "can_be_queried", "power_type",
9- "power_parameters"))
10+ "can_be_started",
11+ "can_be_stopped",
12+ "can_be_queried",
13+ "power_type",
14+ "power_parameters",
15+))
16+
17+DefaultGateways = namedtuple("DefaultGateways", (
18+ "ipv4",
19+ "ipv6",
20+))
21+
22+GatewayDefinition = namedtuple("GatewayDefinition", (
23+ "interface_id",
24+ "subnet_id",
25+ "gateway_ip",
26+))
27
28
29 def generate_node_system_id():
30@@ -2691,7 +2706,7 @@
31 interface.id
32 """, (self.id,))
33 return [
34- (found[0], found[1], found[2])
35+ GatewayDefinition._make((found[0], found[1], found[2]))
36 for found in cursor.fetchall()
37 ]
38
39@@ -2743,7 +2758,7 @@
40
41 # Early out if we already have both gateways.
42 if gateway_ipv4 and gateway_ipv6:
43- return (gateway_ipv4, gateway_ipv6)
44+ return DefaultGateways._make((gateway_ipv4, gateway_ipv6))
45
46 # Get the best guesses for the missing IP families.
47 found_gateways = self.get_best_guess_for_default_gateways()
48@@ -2753,7 +2768,47 @@
49 if not gateway_ipv6:
50 gateway_ipv6 = self._get_gateway_tuple_by_family(
51 found_gateways, IPADDRESS_FAMILY.IPv6)
52- return (gateway_ipv4, gateway_ipv6)
53+ return DefaultGateways._make((gateway_ipv4, gateway_ipv6))
54+
55+ def get_default_dns_servers(self):
56+ """Return the default DNS servers for this node."""
57+ # Circular imports.
58+ from maasserver.dns.zonegenerator import get_dns_server_address
59+
60+ gateways = self.get_default_gateways()
61+
62+ # Try first to use DNS servers from default gateway subnets.
63+ if gateways.ipv4 is not None:
64+ subnet = Subnet.objects.get(id=gateways.ipv4.subnet_id)
65+ if subnet.dns_servers is not None and len(subnet.dns_servers) > 0:
66+ # An IPv4 subnet is hosting the default gateway and has DNS
67+ # servers defined. IPv4 DNS servers take first-priority.
68+ return subnet.dns_servers
69+ if gateways.ipv6 is not None:
70+ subnet = Subnet.objects.get(id=gateways.ipv6.subnet_id)
71+ if subnet.dns_servers is not None and len(subnet.dns_servers) > 0:
72+ # An IPv6 subnet is hosting the default gateway and has DNS
73+ # servers defined. IPv6 DNS servers take second-priority.
74+ return subnet.dns_servers
75+
76+ # No default gateway subnet has specific DNS servers defined, so
77+ # use MAAS for the default DNS server.
78+ if gateways.ipv4 is None and gateways.ipv6 is None:
79+ # If there are no default gateways, the default is the MAAS
80+ # region IP address.
81+ maas_dns_server = get_dns_server_address(
82+ rack_controller=self.get_boot_rack_controller())
83+ else:
84+ # Choose an address consistent with the primary address-family
85+ # in use, as indicated by the presence (or not) of a gateway.
86+ # Note that this path is only taken if the MAAS URL is set to
87+ # a hostname, and the hostname resolves to both an IPv4 and an
88+ # IPv6 address.
89+ maas_dns_server = get_dns_server_address(
90+ rack_controller=self.get_boot_rack_controller(),
91+ ipv4=(gateways.ipv4 is not None),
92+ ipv6=(gateways.ipv6 is not None))
93+ return [maas_dns_server]
94
95 def get_boot_purpose(self):
96 """
97
98=== modified file 'src/maasserver/models/tests/test_node.py'
99--- src/maasserver/models/tests/test_node.py 2016-06-30 17:03:46 +0000
100+++ src/maasserver/models/tests/test_node.py 2016-07-15 00:08:33 +0000
101@@ -36,6 +36,7 @@
102 from maasserver.enum import (
103 FILESYSTEM_GROUP_TYPE,
104 FILESYSTEM_TYPE,
105+ INTERFACE_LINK_TYPE,
106 INTERFACE_TYPE,
107 IPADDRESS_TYPE,
108 NODE_PERMISSION,
109@@ -93,6 +94,7 @@
110 NODE_TRANSITIONS,
111 )
112 from maasserver.rpc.testing.fixtures import MockLiveRegionToClusterRPCFixture
113+import maasserver.server_address as server_address_module
114 from maasserver.storage_layouts import (
115 StorageLayoutError,
116 StorageLayoutMissingBootDiskError,
117@@ -136,6 +138,7 @@
118 commissioning,
119 disk_erasing,
120 )
121+from netaddr import IPAddress
122 from provisioningserver.events import (
123 EVENT_DETAILS,
124 EVENT_TYPES,
125@@ -4303,6 +4306,158 @@
126 ), node.get_default_gateways())
127
128
129+class TestGetDefaultDNSServers(MAASServerTestCase):
130+ """Tests for `Node.get_default_dns_servers`."""
131+
132+ def make_Node_with_RackController(
133+ self, ipv4=True, ipv6=True, ipv4_gateway=True, ipv6_gateway=True,
134+ ipv4_subnet_dns=None, ipv6_subnet_dns=None):
135+ ipv4_subnet_dns = [] if ipv4_subnet_dns is None else ipv4_subnet_dns
136+ ipv6_subnet_dns = [] if ipv6_subnet_dns is None else ipv6_subnet_dns
137+ rack_v4 = None
138+ rack_v6 = None
139+ fabric = factory.make_Fabric()
140+ vlan = fabric.get_default_vlan()
141+ if ipv4:
142+ gateway_ip = None if ipv4_gateway else ""
143+ v4_subnet = factory.make_Subnet(
144+ version=4, vlan=vlan, dns_servers=ipv4_subnet_dns,
145+ gateway_ip=gateway_ip)
146+ if ipv6:
147+ gateway_ip = None if ipv6_gateway else ""
148+ v6_subnet = factory.make_Subnet(
149+ version=6, vlan=vlan, dns_servers=ipv6_subnet_dns,
150+ gateway_ip=gateway_ip)
151+ rack = factory.make_RegionRackController()
152+ vlan.primary_rack = rack
153+ vlan.dhcp_on = True
154+ vlan.save()
155+ # In order to determine the correct IP address per-address-family,
156+ # a name lookup is performed on the hostname part of the URL.
157+ # We need to mock that so we can return whatever IP addresses it
158+ # resolves to.
159+ rack.url = "http://region:5240/MAAS/"
160+ if ipv4:
161+ rack_v4 = factory.pick_ip_in_Subnet(v4_subnet)
162+ if ipv6:
163+ rack_v6 = factory.pick_ip_in_Subnet(v6_subnet)
164+
165+ def get_address(hostname, ip_version=4):
166+ """Mock function to return the IP address of the rack based on the
167+ given address family.
168+ """
169+ if ip_version == 4:
170+ return {IPAddress(rack_v4)} if rack_v4 else set()
171+ elif ip_version == 6:
172+ return {IPAddress(rack_v6)} if rack_v6 else set()
173+
174+ resolve_hostname = self.patch(
175+ server_address_module, 'resolve_hostname')
176+ resolve_hostname.side_effect = get_address
177+ rack.interface_set.all().delete()
178+ rackif = factory.make_Interface(vlan=vlan, node=rack)
179+ if ipv4:
180+ rackif.link_subnet(INTERFACE_LINK_TYPE.STATIC, v4_subnet, rack_v4)
181+ if ipv6:
182+ rackif.link_subnet(INTERFACE_LINK_TYPE.STATIC, v6_subnet, rack_v6)
183+ rack.boot_interface = rackif
184+ rack.save()
185+ node = factory.make_Node(status=NODE_STATUS.READY, disable_ipv4=False)
186+ nodeif = factory.make_Interface(vlan=vlan, node=node)
187+ if ipv4:
188+ nodeif.link_subnet(INTERFACE_LINK_TYPE.AUTO, v4_subnet)
189+ if ipv6:
190+ nodeif.link_subnet(INTERFACE_LINK_TYPE.AUTO, v6_subnet)
191+ node.boot_interface = nodeif
192+ node.save()
193+ return rack_v4, rack_v6, node
194+
195+ def test__uses_rack_ipv4_if_ipv4_only_with_no_gateway(self):
196+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
197+ ipv4=True, ipv4_gateway=False, ipv6=False, ipv6_gateway=False)
198+ self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
199+
200+ def test__uses_rack_ipv4_if_ipv4_only_with_no_gateway_v4_dns(self):
201+ ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
202+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
203+ ipv4=True, ipv4_gateway=False, ipv6=False, ipv6_gateway=False,
204+ ipv4_subnet_dns=[ipv4_subnet_dns])
205+ self.assertThat(
206+ node.get_default_dns_servers(), Equals([rack_v4]))
207+
208+ def test__uses_rack_ipv6_if_ipv6_only_with_no_gateway_v6_dns(self):
209+ ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
210+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
211+ ipv4=False, ipv4_gateway=False, ipv6=True, ipv6_gateway=False,
212+ ipv6_subnet_dns=[ipv6_subnet_dns])
213+ self.assertThat(node.get_default_dns_servers(), Equals([rack_v6]))
214+
215+ def test__uses_rack_ipv4_if_dual_stack_with_no_gateway(self):
216+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
217+ ipv4=True, ipv4_gateway=False, ipv6=True, ipv6_gateway=False)
218+ self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
219+
220+ def test__uses_rack_ipv4_if_dual_stack_with_ipv4_gateway(self):
221+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
222+ ipv4=True, ipv4_gateway=True, ipv6=True, ipv6_gateway=False)
223+ self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
224+
225+ def test__uses_subnet_ipv4_if_dual_stack_with_ipv4_gateway_with_dns(self):
226+ ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
227+ ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
228+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
229+ ipv4=True, ipv4_gateway=True, ipv6=True, ipv6_gateway=False,
230+ ipv4_subnet_dns=[ipv4_subnet_dns],
231+ ipv6_subnet_dns=[ipv6_subnet_dns])
232+ self.assertThat(
233+ node.get_default_dns_servers(), Equals([ipv4_subnet_dns]))
234+
235+ def test__uses_rack_ipv6_if_dual_stack_with_ipv6_gateway(self):
236+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
237+ ipv4=True, ipv4_gateway=False, ipv6=True, ipv6_gateway=True)
238+ self.assertThat(node.get_default_dns_servers(), Equals([rack_v6]))
239+
240+ def test__uses_subnet_ipv6_if_dual_stack_with_ipv6_gateway(self):
241+ ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
242+ ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
243+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
244+ ipv4=True, ipv4_gateway=False, ipv6=True, ipv6_gateway=True,
245+ ipv4_subnet_dns=[ipv4_subnet_dns],
246+ ipv6_subnet_dns=[ipv6_subnet_dns])
247+ self.assertThat(
248+ node.get_default_dns_servers(), Equals([ipv6_subnet_dns]))
249+
250+ def test__uses_rack_ipv4_if_ipv4_with_ipv4_gateway(self):
251+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
252+ ipv4=True, ipv4_gateway=True, ipv6=False, ipv6_gateway=False)
253+ self.assertThat(node.get_default_dns_servers(), Equals([rack_v4]))
254+
255+ def test__uses_subnet_ipv4_if_ipv4_stack_with_ipv4_gateway_and_dns(self):
256+ ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
257+ ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
258+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
259+ ipv4=True, ipv4_gateway=True, ipv6=False, ipv6_gateway=False,
260+ ipv4_subnet_dns=[ipv4_subnet_dns],
261+ ipv6_subnet_dns=[ipv6_subnet_dns])
262+ self.assertThat(
263+ node.get_default_dns_servers(), Equals([ipv4_subnet_dns]))
264+
265+ def test__uses_rack_ipv6_if_ipv6_with_ipv6_gateway(self):
266+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
267+ ipv4=False, ipv4_gateway=False, ipv6=True, ipv6_gateway=True)
268+ self.assertThat(node.get_default_dns_servers(), Equals([rack_v6]))
269+
270+ def test__uses_subnet_ipv6_if_ipv6_with_ipv6_gateway_and_dns(self):
271+ ipv4_subnet_dns = factory.make_ip_address(ipv6=False)
272+ ipv6_subnet_dns = factory.make_ip_address(ipv6=True)
273+ rack_v4, rack_v6, node = self.make_Node_with_RackController(
274+ ipv4=False, ipv4_gateway=False, ipv6=True, ipv6_gateway=True,
275+ ipv4_subnet_dns=[ipv4_subnet_dns],
276+ ipv6_subnet_dns=[ipv6_subnet_dns])
277+ self.assertThat(
278+ node.get_default_dns_servers(), Equals([ipv6_subnet_dns]))
279+
280+
281 class TestNode_Start(MAASServerTestCase):
282 """Tests for Node.start()."""
283
284
285=== modified file 'src/maasserver/preseed_network.py'
286--- src/maasserver/preseed_network.py 2016-03-28 13:54:47 +0000
287+++ src/maasserver/preseed_network.py 2016-07-15 00:08:33 +0000
288@@ -6,10 +6,7 @@
289 __all__ = [
290 ]
291
292-from maasserver.dns.zonegenerator import (
293- get_dns_search_paths,
294- get_dns_server_address,
295-)
296+from maasserver.dns.zonegenerator import get_dns_search_paths
297 from maasserver.enum import (
298 INTERFACE_TYPE,
299 IPADDRESS_FAMILY,
300@@ -24,6 +21,7 @@
301 def __init__(self, node):
302 self.node = node
303 self.gateways = node.get_default_gateways()
304+ self.dns_servers = node.get_default_dns_servers()
305 self.gateway_ipv4_set = False
306 self.gateway_ipv6_set = False
307 self.operations = {
308@@ -51,8 +49,7 @@
309
310 self.network_config.append({
311 "type": "nameserver",
312- "address": get_dns_server_address(
313- rack_controller=self.node.get_boot_rack_controller()),
314+ "address": self.dns_servers,
315 "search": sorted(get_dns_search_paths()),
316 })
317
318
319=== modified file 'src/maasserver/tests/test_preseed_network.py'
320--- src/maasserver/tests/test_preseed_network.py 2016-04-15 23:38:27 +0000
321+++ src/maasserver/tests/test_preseed_network.py 2016-07-15 00:08:33 +0000
322@@ -8,10 +8,7 @@
323 import random
324 from textwrap import dedent
325
326-from maasserver.dns.zonegenerator import (
327- get_dns_search_paths,
328- get_dns_server_address,
329-)
330+from maasserver.dns.zonegenerator import get_dns_search_paths
331 from maasserver.enum import (
332 INTERFACE_TYPE,
333 IPADDRESS_FAMILY,
334@@ -203,8 +200,7 @@
335
336 def collectDNSConfig(self, node):
337 config = "- type: nameserver\n address: %s\n search:\n" % (
338- get_dns_server_address(
339- rack_controller=node.get_boot_primary_rack_controller()))
340+ repr(node.get_default_dns_servers()))
341 dns_searches = sorted(get_dns_search_paths())
342 for dns_name in dns_searches:
343 config += " - %s\n" % dns_name