Merge lp:~lamont/maas/bug-1599223 into lp:~maas-committers/maas/trunk
- bug-1599223
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | LaMont Jones |
Approved revision: | no longer in the source branch. |
Merged at revision: | 5162 |
Proposed branch: | lp:~lamont/maas/bug-1599223 |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
539 lines (+164/-112) 6 files modified
src/maasserver/dns/tests/test_zonegenerator.py (+11/-22) src/maasserver/dns/zonegenerator.py (+1/-35) src/maasserver/models/dnsresource.py (+6/-3) src/maasserver/models/staticipaddress.py (+61/-16) src/maasserver/models/tests/test_dnsresource.py (+2/-2) src/maasserver/models/tests/test_staticipaddress.py (+83/-34) |
To merge this branch: | bzr merge lp:~lamont/maas/bug-1599223 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Mike Pontillo (community) | Approve | ||
Review via email:
|
Commit message
Refactor get_hostname_
Description of the change
Cleanup how IP Addresses get into the DNS, and reduce the overall number of SQL queries for that process significantly. All IP addresses wind up in the DNS, with the FQDN being what we expect it to be historically, and $IFACE.$FQDN for the other IPs, so that there is only the one PTR RR for the FQDN. ($IFACE.$FQDN may have many PTR RRs in the RRset.)
In the old world, we created forward/reverse DNS for the FQDN, and also created PTR RRs naming $IFACE.$FQDN for each address on each interface on each node that resided in the subnet that we were generating a PTR zone for.
With this change, $IFACE.$FQDN is promoted to first-class citizen, and we create forward and reverse DNS for the names. Any name properly given to the FQDN is omitted from the $IFACE.$FQDN collection, since that would cause us to generate multiple PTR RRs for the primary IPs for the domain. The admin who needs multiple PTR RRs for specific situations can use dnsresource-records to cause that to happen.

LaMont Jones (lamont) wrote : | # |
Currently, there can be some disparity between forward and reverse zones (which predates this branch) wherein we might choose to return the FQDN for a PTR where there is a IFACE.FQDN A/AAAA RR. I'll file a separate bug report for that.
Preview Diff
1 | === modified file 'src/maasserver/dns/tests/test_zonegenerator.py' |
2 | --- src/maasserver/dns/tests/test_zonegenerator.py 2016-05-12 19:07:37 +0000 |
3 | +++ src/maasserver/dns/tests/test_zonegenerator.py 2016-07-08 00:18:04 +0000 |
4 | @@ -371,10 +371,15 @@ |
5 | forward_zone("henry"), |
6 | reverse_zone(default_domain, "10/29"), |
7 | reverse_zone(default_domain, "10/24"))) |
8 | - self.assertEqual( |
9 | - {node.hostname: HostnameIPMapping( |
10 | + self.assertEqual({ |
11 | + node.hostname: HostnameIPMapping( |
12 | node.system_id, default_ttl, |
13 | - {'%s' % boot_ip.ip}, node.node_type)}, zones[0]._mapping) |
14 | + {'%s' % boot_ip.ip}, node.node_type), |
15 | + "%s.%s" % (interfaces[0].name, node.hostname): |
16 | + HostnameIPMapping( |
17 | + node.system_id, default_ttl, |
18 | + {'%s' % sip.ip}, node.node_type)}, |
19 | + zones[0]._mapping) |
20 | self.assertEqual( |
21 | {dnsdata.dnsresource.name: HostnameRRsetMapping( |
22 | None, |
23 | @@ -384,9 +389,7 @@ |
24 | node.fqdn: HostnameIPMapping( |
25 | node.system_id, 30, {'%s' % boot_ip.ip}, node.node_type), |
26 | '%s.%s' % (interfaces[0].name, node.fqdn): HostnameIPMapping( |
27 | - None, default_ttl, {'%s' % sip.ip}, None), |
28 | - '%s.%s' % (boot_iface.name, node.fqdn): HostnameIPMapping( |
29 | - None, default_ttl, {'%s' % boot_ip.ip}, None)}, |
30 | + None, default_ttl, {'%s' % sip.ip}, None)}, |
31 | zones[1]._mapping) |
32 | |
33 | def rfc2317_network(self, network): |
34 | @@ -517,8 +520,6 @@ |
35 | node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)} |
36 | expected_reverse = { |
37 | node.fqdn: HostnameIPMapping( |
38 | - node.system_id, domain.ttl, {boot_ip.ip}, node.node_type), |
39 | - "%s.%s" % (boot_iface.name, node.fqdn): HostnameIPMapping( |
40 | node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)} |
41 | zones = ZoneGenerator( |
42 | domain, subnet, default_ttl=global_ttl, |
43 | @@ -545,10 +546,6 @@ |
44 | expected_reverse = { |
45 | node.fqdn: HostnameIPMapping( |
46 | node.system_id, node.address_ttl, {boot_ip.ip}, |
47 | - node.node_type), |
48 | - "%s.%s" % (boot_iface.name, node.fqdn): |
49 | - HostnameIPMapping( |
50 | - node.system_id, node.address_ttl, {boot_ip.ip}, |
51 | node.node_type)} |
52 | zones = ZoneGenerator( |
53 | domain, subnet, default_ttl=global_ttl, |
54 | @@ -581,11 +578,7 @@ |
55 | node.system_id, node.address_ttl, ips, node.node_type)} |
56 | expected_reverse = { |
57 | node.fqdn: HostnameIPMapping( |
58 | - node.system_id, node.address_ttl, ips, node.node_type), |
59 | - "%s.%s" % (boot_iface.name, node.fqdn): |
60 | - HostnameIPMapping( |
61 | - node.system_id, node.address_ttl, {boot_ip.ip}, |
62 | - node.node_type)} |
63 | + node.system_id, node.address_ttl, ips, node.node_type)} |
64 | zones = ZoneGenerator( |
65 | domain, subnet, default_ttl=global_ttl, |
66 | serial=random.randint(0, 65535)).as_list() |
67 | @@ -621,11 +614,7 @@ |
68 | node.fqdn: HostnameIPMapping( |
69 | node.system_id, node.address_ttl, node_ips, node.node_type), |
70 | dnsrr.fqdn: HostnameIPMapping( |
71 | - None, dnsrr.address_ttl, dnsrr_ips, None), |
72 | - "%s.%s" % (boot_iface.name, node.fqdn): |
73 | - HostnameIPMapping( |
74 | - node.system_id, node.address_ttl, {boot_ip.ip}, |
75 | - node.node_type)} |
76 | + None, dnsrr.address_ttl, dnsrr_ips, None)} |
77 | zones = ZoneGenerator( |
78 | domain, subnet, default_ttl=global_ttl, |
79 | serial=random.randint(0, 65535)).as_list() |
80 | |
81 | === modified file 'src/maasserver/dns/zonegenerator.py' |
82 | --- src/maasserver/dns/zonegenerator.py 2016-04-21 19:13:47 +0000 |
83 | +++ src/maasserver/dns/zonegenerator.py 2016-07-08 00:18:04 +0000 |
84 | @@ -22,11 +22,7 @@ |
85 | from maasserver.models.dnsdata import DNSData |
86 | from maasserver.models.dnsresource import separate_fqdn |
87 | from maasserver.models.domain import Domain |
88 | -from maasserver.models.node import Node |
89 | -from maasserver.models.staticipaddress import ( |
90 | - HostnameIPMapping, |
91 | - StaticIPAddress, |
92 | -) |
93 | +from maasserver.models.staticipaddress import StaticIPAddress |
94 | from maasserver.models.subnet import Subnet |
95 | from maasserver.server_address import get_maas_facing_server_address |
96 | from netaddr import ( |
97 | @@ -260,7 +256,6 @@ |
98 | # 1. Dynamic ranges on this subnet. |
99 | # 2. Node: ip mapping(subnet), including DNSResource records for |
100 | # StaticIPAddresses in this subnet. |
101 | - # 3. Interfaces on any node that have IP addresses in this subnet. |
102 | # All of this needs to be done smallest to largest so that we can |
103 | # correctly gather the rfc2317 glue that we need. Failure to sort |
104 | # means that we wind up grabbing (and deleting) the rfc2317 glue info |
105 | @@ -287,35 +282,6 @@ |
106 | # including all DNSResource-associated addresses. |
107 | mapping = mappings[subnet] |
108 | |
109 | - # 3. Add all of the interface named records. |
110 | - # 2015-12-18 lamont N.B., these are not found in the forward zone, |
111 | - # on purpose. If someone eventually calls this a bug, we can |
112 | - # revisit the size increase this would create in the forward zone. |
113 | - # This will also include any discovered addresses on such |
114 | - # interfaces. |
115 | - nodes = Node.objects.filter( |
116 | - interface__ip_addresses__subnet_id=subnet.id, |
117 | - interface__ip_addresses__ip__isnull=False).prefetch_related( |
118 | - "interface_set__ip_addresses") |
119 | - for node in nodes: |
120 | - if node.address_ttl is not None: |
121 | - ttl = node.address_ttl |
122 | - elif node.domain.ttl is not None: |
123 | - ttl = node.domain.ttl |
124 | - else: |
125 | - ttl = default_ttl |
126 | - for iface in node.interface_set.all(): |
127 | - ips_in_subnet = { |
128 | - ip.ip |
129 | - for ip in iface.ip_addresses.all() |
130 | - if (ip.ip is not None and ip.subnet_id == subnet.id)} |
131 | - if len(ips_in_subnet) > 0: |
132 | - iface_map = HostnameIPMapping( |
133 | - node.system_id, ttl, ips_in_subnet, node.node_type) |
134 | - mapping.update({ |
135 | - "%s.%s" % (iface.name, iface.node.fqdn): iface_map |
136 | - }) |
137 | - |
138 | # Use the default_domain as the name for the NS host in the reverse |
139 | # zones. If this network is actually a parent rfc2317 glue |
140 | # network, then we need to generate the glue records. |
141 | |
142 | === modified file 'src/maasserver/models/dnsresource.py' |
143 | --- src/maasserver/models/dnsresource.py 2016-05-11 19:01:48 +0000 |
144 | +++ src/maasserver/models/dnsresource.py 2016-07-08 00:18:04 +0000 |
145 | @@ -75,14 +75,17 @@ |
146 | """ |
147 | if fqdn is None or fqdn == '' or fqdn == '@': |
148 | return (fqdn, None) |
149 | - elif Domain.objects.filter(name=fqdn).exists(): |
150 | - if domainname == fqdn or domainname is None: |
151 | + if domainname is not None: |
152 | + if domainname == fqdn: |
153 | return ('@', fqdn) |
154 | else: |
155 | # strip off the passed in ".$domainname" from the fqdn. |
156 | name = fqdn[:-len(domainname) - 1] |
157 | return (name, domainname) |
158 | - elif rrtype == 'SRV': |
159 | + else: |
160 | + if Domain.objects.filter(name=fqdn).exists(): |
161 | + return ('@', fqdn) |
162 | + if rrtype == 'SRV': |
163 | spec = SRV_LHS |
164 | else: |
165 | spec = LABEL |
166 | |
167 | === modified file 'src/maasserver/models/staticipaddress.py' |
168 | --- src/maasserver/models/staticipaddress.py 2016-07-01 12:23:35 +0000 |
169 | +++ src/maasserver/models/staticipaddress.py 2016-07-08 00:18:04 +0000 |
170 | @@ -377,7 +377,7 @@ |
171 | ) |
172 | ORDER BY |
173 | node.hostname, |
174 | - is_boot, |
175 | + is_boot DESC, |
176 | family(staticip.ip), |
177 | CASE |
178 | WHEN interface.type = 'bond' AND |
179 | @@ -410,6 +410,42 @@ |
180 | END, |
181 | interface.id |
182 | """ |
183 | + iface_sql_query = """ |
184 | + SELECT |
185 | + node.hostname, |
186 | + node.system_id, |
187 | + node.node_type, |
188 | + """ + ttl_clause + """ AS ttl, |
189 | + domain.name, staticip.ip, |
190 | + interface.name |
191 | + FROM |
192 | + maasserver_interface AS interface |
193 | + JOIN maasserver_node AS node ON |
194 | + node.id = interface.node_id |
195 | + JOIN maasserver_domain as domain ON |
196 | + domain.id = node.domain_id |
197 | + JOIN maasserver_interface_ip_addresses AS link ON |
198 | + link.interface_id = interface.id |
199 | + JOIN maasserver_staticipaddress AS staticip ON |
200 | + staticip.id = link.staticipaddress_id |
201 | + LEFT JOIN maasserver_domain as domain2 ON |
202 | + /* Pick up another copy of domain looking for instances of |
203 | + * the name as the top of a domain. |
204 | + */ |
205 | + domain2.name = CONCAT( |
206 | + interface.name, '.', node.hostname, '.', domain.name) |
207 | + WHERE |
208 | + staticip.ip IS NOT NULL AND |
209 | + host(staticip.ip) != '' AND |
210 | + (""" + search_term + """ = %s) AND |
211 | + ( |
212 | + node.disable_ipv4 IS FALSE OR |
213 | + family(staticip.ip) <> 4 |
214 | + ) |
215 | + ORDER BY |
216 | + node.hostname, |
217 | + interface.id |
218 | + """ |
219 | # We get user reserved et al mappings first, so that we can overwrite |
220 | # TTL as we process the return from the SQL horror above. |
221 | mapping = self._get_user_reserved_mappings(domain_or_subnet) |
222 | @@ -421,22 +457,31 @@ |
223 | # stripping domain), the boot and non-boot interface ip address in ipv4 |
224 | # and ipv6. Our task: if there are boot interace IPs, they win. If |
225 | # there are none, then whatever we got wins. The ORDER BY means that |
226 | - # we will see all of the non-boot interfaces before we see any boot |
227 | + # we will see all of the boot interfaces before we see any non-boot |
228 | # interface IPs. See Bug#1584850 |
229 | - for (node_name, system_id, node_type, |
230 | - ttl, domain_name, ip, is_boot) in cursor.fetchall(): |
231 | - hostname = "%s.%s" % (strip_domain(node_name), domain_name) |
232 | - mapping[hostname].node_type = node_type |
233 | - mapping[hostname].system_id = system_id |
234 | - mapping[hostname].ttl = ttl |
235 | - # If this is a boot interface, and we have previously only found |
236 | - # non-boot interface IPs, discard them. See also Bug#1584850. |
237 | - if is_boot and not iface_is_boot[hostname]: |
238 | - mapping[hostname].ips = set() |
239 | - iface_is_boot[hostname] = True |
240 | - # At this point, if the interface type is correct, keep the IP. |
241 | - if is_boot == iface_is_boot[hostname]: |
242 | - mapping[hostname].ips.add(ip) |
243 | + for (node_name, system_id, node_type, ttl, domain_name, |
244 | + ip, is_boot) in cursor.fetchall(): |
245 | + fqdn = "%s.%s" % (strip_domain(node_name), domain_name) |
246 | + mapping[fqdn].node_type = node_type |
247 | + mapping[fqdn].system_id = system_id |
248 | + mapping[fqdn].ttl = ttl |
249 | + if is_boot: |
250 | + iface_is_boot[fqdn] = True |
251 | + # If we have an IP on the right interface type, save it. |
252 | + if is_boot == iface_is_boot[fqdn]: |
253 | + mapping[fqdn].ips.add(ip) |
254 | + # Next, get all the addresses, on all the interfaces, and add the ones |
255 | + # that are not already present on the FQDN as $IFACE.$FQDN. |
256 | + cursor.execute(iface_sql_query, (domain_or_subnet.id,)) |
257 | + for (node_name, system_id, node_type, ttl, |
258 | + domain_name, ip, iface_name) in cursor.fetchall(): |
259 | + fqdn = "%s.%s" % (strip_domain(node_name), domain_name) |
260 | + if ip not in mapping[fqdn].ips: |
261 | + name = "%s.%s" % (iface_name, fqdn) |
262 | + mapping[name].node_type = node_type |
263 | + mapping[name].system_id = system_id |
264 | + mapping[name].ttl = ttl |
265 | + mapping[name].ips.add(ip) |
266 | return mapping |
267 | |
268 | def filter_by_ip_family(self, family): |
269 | |
270 | === modified file 'src/maasserver/models/tests/test_dnsresource.py' |
271 | --- src/maasserver/models/tests/test_dnsresource.py 2016-04-22 01:56:28 +0000 |
272 | +++ src/maasserver/models/tests/test_dnsresource.py 2016-07-08 00:18:04 +0000 |
273 | @@ -131,9 +131,9 @@ |
274 | parent = "%s.%s" % ( |
275 | factory.make_name("b"), |
276 | factory.make_name("c")) |
277 | - label = factory.make_name("a") |
278 | + label = "%s.%s" % (factory.make_name("a"), factory.make_name("d")) |
279 | name = "%s.%s" % (label, parent) |
280 | - factory.make_Domain(name=name) |
281 | + factory.make_Domain(name=parent) |
282 | self.assertEqual( |
283 | (label, parent), separate_fqdn(name, domainname=parent)) |
284 | |
285 | |
286 | === modified file 'src/maasserver/models/tests/test_staticipaddress.py' |
287 | --- src/maasserver/models/tests/test_staticipaddress.py 2016-07-01 12:26:51 +0000 |
288 | +++ src/maasserver/models/tests/test_staticipaddress.py 2016-07-08 00:18:04 +0000 |
289 | @@ -356,23 +356,34 @@ |
290 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) |
291 | self.assertEqual(expected_mapping, mapping) |
292 | |
293 | - def test_get_hostname_ip_mapping_returns_fqdn(self): |
294 | + def test_get_hostname_ip_mapping_returns_fqdn_and_other(self): |
295 | hostname = factory.make_name('hostname') |
296 | domainname = factory.make_name('domain') |
297 | factory.make_Domain(name=domainname) |
298 | full_hostname = "%s.%s" % (hostname, domainname) |
299 | subnet = factory.make_Subnet() |
300 | node = factory.make_Node_with_Interface_on_Subnet( |
301 | - interface=True, hostname=full_hostname, |
302 | + interface=True, hostname=full_hostname, interface_count=3, |
303 | subnet=subnet, disable_ipv4=False) |
304 | boot_interface = node.get_boot_interface() |
305 | staticip = factory.make_StaticIPAddress( |
306 | alloc_type=IPADDRESS_TYPE.STICKY, |
307 | ip=factory.pick_ip_in_Subnet(subnet), |
308 | subnet=subnet, interface=boot_interface) |
309 | + ifaces = node.interface_set.exclude(interface=boot_interface) |
310 | + sip2 = factory.make_StaticIPAddress( |
311 | + alloc_type=IPADDRESS_TYPE.STICKY, |
312 | + ip=factory.pick_ip_in_Subnet(subnet), |
313 | + subnet=subnet, interface=ifaces[0]) |
314 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet) |
315 | - self.assertEqual({full_hostname: HostnameIPMapping( |
316 | - node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) |
317 | + self.assertEqual({ |
318 | + full_hostname: |
319 | + HostnameIPMapping( |
320 | + node.system_id, 30, {staticip.ip}, node.node_type), |
321 | + "%s.%s" % (ifaces[0].name, full_hostname): |
322 | + HostnameIPMapping( |
323 | + node.system_id, 30, {sip2.ip}, node.node_type), |
324 | + }, mapping) |
325 | |
326 | def make_mapping(self, node, raw_ttl=False): |
327 | if raw_ttl or node.address_ttl is not None: |
328 | @@ -480,12 +491,16 @@ |
329 | ip=factory.pick_ip_in_Subnet(subnet), |
330 | subnet=subnet, interface=boot_interface) |
331 | newer_nic = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node) |
332 | - factory.make_StaticIPAddress( |
333 | + newer_ip = factory.make_StaticIPAddress( |
334 | alloc_type=IPADDRESS_TYPE.STICKY, interface=newer_nic) |
335 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
336 | node.domain) |
337 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
338 | - node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) |
339 | + expected_mapping = { |
340 | + node.fqdn: HostnameIPMapping( |
341 | + node.system_id, 30, {staticip.ip}, node.node_type), |
342 | + "%s.%s" % (newer_nic.name, node.fqdn): HostnameIPMapping( |
343 | + node.system_id, 30, {newer_ip.ip}, node.node_type)} |
344 | + self.assertEqual(expected_mapping, mapping) |
345 | |
346 | def test_get_hostname_ip_mapping_picks_sticky_over_auto(self): |
347 | subnet = factory.make_Subnet( |
348 | @@ -499,12 +514,16 @@ |
349 | ip=factory.pick_ip_in_Subnet(subnet), |
350 | subnet=subnet, interface=boot_interface) |
351 | nic = node.get_boot_interface() |
352 | - factory.make_StaticIPAddress( |
353 | + auto_ip = factory.make_StaticIPAddress( |
354 | alloc_type=IPADDRESS_TYPE.AUTO, interface=nic) |
355 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
356 | node.domain) |
357 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
358 | - node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) |
359 | + expected_mapping = { |
360 | + node.fqdn: HostnameIPMapping( |
361 | + node.system_id, 30, {staticip.ip}, node.node_type), |
362 | + "%s.%s" % (nic.name, node.fqdn): HostnameIPMapping( |
363 | + node.system_id, 30, {auto_ip.ip}, node.node_type)} |
364 | + self.assertEqual(expected_mapping, mapping) |
365 | |
366 | def test_get_hostname_ip_mapping_combines_IPv4_and_IPv6_addresses(self): |
367 | node = factory.make_Node(interface=True, disable_ipv4=False) |
368 | @@ -583,13 +602,17 @@ |
369 | staticip = factory.make_StaticIPAddress( |
370 | alloc_type=IPADDRESS_TYPE.AUTO, interface=iface, |
371 | subnet=subnet) |
372 | - factory.make_StaticIPAddress( |
373 | + discovered = factory.make_StaticIPAddress( |
374 | alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=iface, |
375 | subnet=subnet) |
376 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
377 | node.domain) |
378 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
379 | - node.system_id, 30, {staticip.ip}, node.node_type)}, mapping) |
380 | + expected_mapping = { |
381 | + node.fqdn: HostnameIPMapping( |
382 | + node.system_id, 30, {staticip.ip}, node.node_type), |
383 | + "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( |
384 | + node.system_id, 30, {discovered.ip}, node.node_type)} |
385 | + self.assertEqual(expected_mapping, mapping) |
386 | |
387 | def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self): |
388 | subnet = factory.make_Subnet( |
389 | @@ -604,7 +627,7 @@ |
390 | iface3 = factory.make_Interface(node=node) |
391 | bondif = factory.make_Interface( |
392 | INTERFACE_TYPE.BOND, node=node, parents=[iface2, iface3]) |
393 | - factory.make_StaticIPAddress( |
394 | + iface_ip = factory.make_StaticIPAddress( |
395 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
396 | subnet=subnet) |
397 | factory.make_StaticIPAddress( |
398 | @@ -618,8 +641,12 @@ |
399 | subnet=subnet) |
400 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
401 | node.domain) |
402 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
403 | - node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping) |
404 | + expected_mapping = { |
405 | + node.fqdn: HostnameIPMapping( |
406 | + node.system_id, 30, {bond_staticip.ip}, node.node_type), |
407 | + "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( |
408 | + node.system_id, 30, {iface_ip.ip}, node.node_type)} |
409 | + self.assertEqual(expected_mapping, mapping) |
410 | |
411 | def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self): |
412 | subnet = factory.make_Subnet( |
413 | @@ -642,8 +669,11 @@ |
414 | subnet=subnet) |
415 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
416 | node.domain) |
417 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
418 | - node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping) |
419 | + expected_mapping = { |
420 | + node.fqdn: HostnameIPMapping( |
421 | + node.system_id, 30, {bond_staticip.ip}, node.node_type), |
422 | + } |
423 | + self.assertEqual(expected_mapping, mapping) |
424 | |
425 | def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self): |
426 | subnet = factory.make_Subnet( |
427 | @@ -665,13 +695,17 @@ |
428 | factory.make_StaticIPAddress( |
429 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface3, |
430 | subnet=subnet) |
431 | - factory.make_StaticIPAddress( |
432 | + bondip = factory.make_StaticIPAddress( |
433 | alloc_type=IPADDRESS_TYPE.STICKY, interface=bondif, |
434 | subnet=subnet) |
435 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
436 | node.domain) |
437 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
438 | - node.system_id, 30, {boot_staticip.ip}, node.node_type)}, mapping) |
439 | + expected_mapping = { |
440 | + node.fqdn: HostnameIPMapping( |
441 | + node.system_id, 30, {boot_staticip.ip}, node.node_type), |
442 | + "%s.%s" % (bondif.name, node.fqdn): HostnameIPMapping( |
443 | + node.system_id, 30, {bondip.ip}, node.node_type)} |
444 | + self.assertEqual(expected_mapping, mapping) |
445 | |
446 | def test_get_hostname_ip_mapping_prefers_boot_interface(self): |
447 | subnet = factory.make_Subnet( |
448 | @@ -680,7 +714,7 @@ |
449 | hostname=factory.make_name('host'), subnet=subnet, |
450 | disable_ipv4=False) |
451 | iface = node.get_boot_interface() |
452 | - factory.make_StaticIPAddress( |
453 | + iface_ip = factory.make_StaticIPAddress( |
454 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
455 | subnet=subnet) |
456 | new_boot_interface = factory.make_Interface( |
457 | @@ -693,8 +727,12 @@ |
458 | subnet=subnet) |
459 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
460 | node.domain) |
461 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
462 | - node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping) |
463 | + expected_mapping = { |
464 | + node.fqdn: HostnameIPMapping( |
465 | + node.system_id, 30, {boot_sip.ip}, node.node_type), |
466 | + "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( |
467 | + node.system_id, 30, {iface_ip.ip}, node.node_type)} |
468 | + self.assertEqual(expected_mapping, mapping) |
469 | |
470 | def test_get_hostname_ip_mapping_prefers_boot_interface_to_alias(self): |
471 | subnet = factory.make_Subnet( |
472 | @@ -703,7 +741,7 @@ |
473 | hostname=factory.make_name('host'), subnet=subnet, |
474 | disable_ipv4=False) |
475 | iface = node.get_boot_interface() |
476 | - factory.make_StaticIPAddress( |
477 | + iface_ip = factory.make_StaticIPAddress( |
478 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
479 | subnet=subnet) |
480 | new_boot_interface = factory.make_Interface( |
481 | @@ -716,8 +754,12 @@ |
482 | subnet=subnet) |
483 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
484 | node.domain) |
485 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
486 | - node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping) |
487 | + expected_mapping = { |
488 | + node.fqdn: HostnameIPMapping( |
489 | + node.system_id, 30, {boot_sip.ip}, node.node_type), |
490 | + "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping( |
491 | + node.system_id, 30, {iface_ip.ip}, node.node_type)} |
492 | + self.assertEqual(expected_mapping, mapping) |
493 | |
494 | def test_get_hostname_ip_mapping_prefers_physical_interfaces_to_vlan(self): |
495 | subnet = factory.make_Subnet( |
496 | @@ -731,13 +773,17 @@ |
497 | phy_staticip = factory.make_StaticIPAddress( |
498 | alloc_type=IPADDRESS_TYPE.STICKY, interface=iface, |
499 | subnet=subnet) |
500 | - factory.make_StaticIPAddress( |
501 | + vlanip = factory.make_StaticIPAddress( |
502 | alloc_type=IPADDRESS_TYPE.STICKY, interface=vlanif, |
503 | subnet=subnet) |
504 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping( |
505 | node.domain) |
506 | - self.assertEqual({node.fqdn: HostnameIPMapping( |
507 | - node.system_id, 30, {phy_staticip.ip}, node.node_type)}, mapping) |
508 | + expected_mapping = { |
509 | + node.fqdn: HostnameIPMapping( |
510 | + node.system_id, 30, {phy_staticip.ip}, node.node_type), |
511 | + "%s.%s" % (vlanif.name, node.fqdn): HostnameIPMapping( |
512 | + node.system_id, 30, {vlanip.ip}, node.node_type)} |
513 | + self.assertEqual(expected_mapping, mapping) |
514 | |
515 | @skip("XXX: GavinPanella 2016-04-27 bug=1556188: Fails spuriously.") |
516 | def test_get_hostname_ip_mapping_returns_domain_head_ips(self): |
517 | @@ -790,8 +836,9 @@ |
518 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) |
519 | expected_mapping = { |
520 | node.fqdn: HostnameIPMapping( |
521 | - node.system_id, 30, {sip0.ip}, node.node_type |
522 | - )} |
523 | + node.system_id, 30, {sip0.ip}, node.node_type), |
524 | + "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping( |
525 | + node.system_id, 30, {sip1.ip}, node.node_type)} |
526 | self.assertEqual(expected_mapping, mapping) |
527 | |
528 | def test_get_hostname_ip_mapping_returns_correct_bond_ip(self): |
529 | @@ -837,8 +884,10 @@ |
530 | mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain) |
531 | expected_mapping = { |
532 | node.fqdn: HostnameIPMapping( |
533 | - node.system_id, 30, {bond_sip.ip}, node.node_type |
534 | - )} |
535 | + node.system_id, 30, {bond_sip.ip}, node.node_type), |
536 | + "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping( |
537 | + node.system_id, 30, {sip1.ip}, node.node_type), |
538 | + } |
539 | self.assertEqual(expected_mapping, mapping) |
540 | |
541 |
Looks good to me. Was this tested end-to-end on nodes deployed with more than one interface? We need to make sure to do that before backporting to 2.0. Let me know if I can help.