Merge lp:~lamont/maas/bug-1599223 into lp:maas/trunk

Proposed by LaMont Jones on 2016-07-08
Status: Merged
Approved by: LaMont Jones on 2016-07-08
Approved revision: 5160
Merged at revision: 5162
Proposed branch: lp:~lamont/maas/bug-1599223
Merge into: lp: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
Reviewer Review Type Date Requested Status
Mike Pontillo 2016-07-08 Approve on 2016-07-08
Review via email: mp+299491@code.launchpad.net

Commit message

Refactor get_hostname_ip_mapping() and stop generating multiple PTR RRs for node FQDNs.

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.

To post a comment you must log in.
Mike Pontillo (mpontillo) wrote :

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.

review: Approve
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/maasserver/dns/tests/test_zonegenerator.py'
--- src/maasserver/dns/tests/test_zonegenerator.py 2016-05-12 19:07:37 +0000
+++ src/maasserver/dns/tests/test_zonegenerator.py 2016-07-08 00:18:04 +0000
@@ -371,10 +371,15 @@
371 forward_zone("henry"),371 forward_zone("henry"),
372 reverse_zone(default_domain, "10/29"),372 reverse_zone(default_domain, "10/29"),
373 reverse_zone(default_domain, "10/24")))373 reverse_zone(default_domain, "10/24")))
374 self.assertEqual(374 self.assertEqual({
375 {node.hostname: HostnameIPMapping(375 node.hostname: HostnameIPMapping(
376 node.system_id, default_ttl,376 node.system_id, default_ttl,
377 {'%s' % boot_ip.ip}, node.node_type)}, zones[0]._mapping)377 {'%s' % boot_ip.ip}, node.node_type),
378 "%s.%s" % (interfaces[0].name, node.hostname):
379 HostnameIPMapping(
380 node.system_id, default_ttl,
381 {'%s' % sip.ip}, node.node_type)},
382 zones[0]._mapping)
378 self.assertEqual(383 self.assertEqual(
379 {dnsdata.dnsresource.name: HostnameRRsetMapping(384 {dnsdata.dnsresource.name: HostnameRRsetMapping(
380 None,385 None,
@@ -384,9 +389,7 @@
384 node.fqdn: HostnameIPMapping(389 node.fqdn: HostnameIPMapping(
385 node.system_id, 30, {'%s' % boot_ip.ip}, node.node_type),390 node.system_id, 30, {'%s' % boot_ip.ip}, node.node_type),
386 '%s.%s' % (interfaces[0].name, node.fqdn): HostnameIPMapping(391 '%s.%s' % (interfaces[0].name, node.fqdn): HostnameIPMapping(
387 None, default_ttl, {'%s' % sip.ip}, None),392 None, default_ttl, {'%s' % sip.ip}, None)},
388 '%s.%s' % (boot_iface.name, node.fqdn): HostnameIPMapping(
389 None, default_ttl, {'%s' % boot_ip.ip}, None)},
390 zones[1]._mapping)393 zones[1]._mapping)
391394
392 def rfc2317_network(self, network):395 def rfc2317_network(self, network):
@@ -517,8 +520,6 @@
517 node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)}520 node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)}
518 expected_reverse = {521 expected_reverse = {
519 node.fqdn: HostnameIPMapping(522 node.fqdn: HostnameIPMapping(
520 node.system_id, domain.ttl, {boot_ip.ip}, node.node_type),
521 "%s.%s" % (boot_iface.name, node.fqdn): HostnameIPMapping(
522 node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)}523 node.system_id, domain.ttl, {boot_ip.ip}, node.node_type)}
523 zones = ZoneGenerator(524 zones = ZoneGenerator(
524 domain, subnet, default_ttl=global_ttl,525 domain, subnet, default_ttl=global_ttl,
@@ -545,10 +546,6 @@
545 expected_reverse = {546 expected_reverse = {
546 node.fqdn: HostnameIPMapping(547 node.fqdn: HostnameIPMapping(
547 node.system_id, node.address_ttl, {boot_ip.ip},548 node.system_id, node.address_ttl, {boot_ip.ip},
548 node.node_type),
549 "%s.%s" % (boot_iface.name, node.fqdn):
550 HostnameIPMapping(
551 node.system_id, node.address_ttl, {boot_ip.ip},
552 node.node_type)}549 node.node_type)}
553 zones = ZoneGenerator(550 zones = ZoneGenerator(
554 domain, subnet, default_ttl=global_ttl,551 domain, subnet, default_ttl=global_ttl,
@@ -581,11 +578,7 @@
581 node.system_id, node.address_ttl, ips, node.node_type)}578 node.system_id, node.address_ttl, ips, node.node_type)}
582 expected_reverse = {579 expected_reverse = {
583 node.fqdn: HostnameIPMapping(580 node.fqdn: HostnameIPMapping(
584 node.system_id, node.address_ttl, ips, node.node_type),581 node.system_id, node.address_ttl, ips, node.node_type)}
585 "%s.%s" % (boot_iface.name, node.fqdn):
586 HostnameIPMapping(
587 node.system_id, node.address_ttl, {boot_ip.ip},
588 node.node_type)}
589 zones = ZoneGenerator(582 zones = ZoneGenerator(
590 domain, subnet, default_ttl=global_ttl,583 domain, subnet, default_ttl=global_ttl,
591 serial=random.randint(0, 65535)).as_list()584 serial=random.randint(0, 65535)).as_list()
@@ -621,11 +614,7 @@
621 node.fqdn: HostnameIPMapping(614 node.fqdn: HostnameIPMapping(
622 node.system_id, node.address_ttl, node_ips, node.node_type),615 node.system_id, node.address_ttl, node_ips, node.node_type),
623 dnsrr.fqdn: HostnameIPMapping(616 dnsrr.fqdn: HostnameIPMapping(
624 None, dnsrr.address_ttl, dnsrr_ips, None),617 None, dnsrr.address_ttl, dnsrr_ips, None)}
625 "%s.%s" % (boot_iface.name, node.fqdn):
626 HostnameIPMapping(
627 node.system_id, node.address_ttl, {boot_ip.ip},
628 node.node_type)}
629 zones = ZoneGenerator(618 zones = ZoneGenerator(
630 domain, subnet, default_ttl=global_ttl,619 domain, subnet, default_ttl=global_ttl,
631 serial=random.randint(0, 65535)).as_list()620 serial=random.randint(0, 65535)).as_list()
632621
=== modified file 'src/maasserver/dns/zonegenerator.py'
--- src/maasserver/dns/zonegenerator.py 2016-04-21 19:13:47 +0000
+++ src/maasserver/dns/zonegenerator.py 2016-07-08 00:18:04 +0000
@@ -22,11 +22,7 @@
22from maasserver.models.dnsdata import DNSData22from maasserver.models.dnsdata import DNSData
23from maasserver.models.dnsresource import separate_fqdn23from maasserver.models.dnsresource import separate_fqdn
24from maasserver.models.domain import Domain24from maasserver.models.domain import Domain
25from maasserver.models.node import Node25from maasserver.models.staticipaddress import StaticIPAddress
26from maasserver.models.staticipaddress import (
27 HostnameIPMapping,
28 StaticIPAddress,
29)
30from maasserver.models.subnet import Subnet26from maasserver.models.subnet import Subnet
31from maasserver.server_address import get_maas_facing_server_address27from maasserver.server_address import get_maas_facing_server_address
32from netaddr import (28from netaddr import (
@@ -260,7 +256,6 @@
260 # 1. Dynamic ranges on this subnet.256 # 1. Dynamic ranges on this subnet.
261 # 2. Node: ip mapping(subnet), including DNSResource records for257 # 2. Node: ip mapping(subnet), including DNSResource records for
262 # StaticIPAddresses in this subnet.258 # StaticIPAddresses in this subnet.
263 # 3. Interfaces on any node that have IP addresses in this subnet.
264 # All of this needs to be done smallest to largest so that we can259 # All of this needs to be done smallest to largest so that we can
265 # correctly gather the rfc2317 glue that we need. Failure to sort260 # correctly gather the rfc2317 glue that we need. Failure to sort
266 # means that we wind up grabbing (and deleting) the rfc2317 glue info261 # means that we wind up grabbing (and deleting) the rfc2317 glue info
@@ -287,35 +282,6 @@
287 # including all DNSResource-associated addresses.282 # including all DNSResource-associated addresses.
288 mapping = mappings[subnet]283 mapping = mappings[subnet]
289284
290 # 3. Add all of the interface named records.
291 # 2015-12-18 lamont N.B., these are not found in the forward zone,
292 # on purpose. If someone eventually calls this a bug, we can
293 # revisit the size increase this would create in the forward zone.
294 # This will also include any discovered addresses on such
295 # interfaces.
296 nodes = Node.objects.filter(
297 interface__ip_addresses__subnet_id=subnet.id,
298 interface__ip_addresses__ip__isnull=False).prefetch_related(
299 "interface_set__ip_addresses")
300 for node in nodes:
301 if node.address_ttl is not None:
302 ttl = node.address_ttl
303 elif node.domain.ttl is not None:
304 ttl = node.domain.ttl
305 else:
306 ttl = default_ttl
307 for iface in node.interface_set.all():
308 ips_in_subnet = {
309 ip.ip
310 for ip in iface.ip_addresses.all()
311 if (ip.ip is not None and ip.subnet_id == subnet.id)}
312 if len(ips_in_subnet) > 0:
313 iface_map = HostnameIPMapping(
314 node.system_id, ttl, ips_in_subnet, node.node_type)
315 mapping.update({
316 "%s.%s" % (iface.name, iface.node.fqdn): iface_map
317 })
318
319 # Use the default_domain as the name for the NS host in the reverse285 # Use the default_domain as the name for the NS host in the reverse
320 # zones. If this network is actually a parent rfc2317 glue286 # zones. If this network is actually a parent rfc2317 glue
321 # network, then we need to generate the glue records.287 # network, then we need to generate the glue records.
322288
=== modified file 'src/maasserver/models/dnsresource.py'
--- src/maasserver/models/dnsresource.py 2016-05-11 19:01:48 +0000
+++ src/maasserver/models/dnsresource.py 2016-07-08 00:18:04 +0000
@@ -75,14 +75,17 @@
75 """75 """
76 if fqdn is None or fqdn == '' or fqdn == '@':76 if fqdn is None or fqdn == '' or fqdn == '@':
77 return (fqdn, None)77 return (fqdn, None)
78 elif Domain.objects.filter(name=fqdn).exists():78 if domainname is not None:
79 if domainname == fqdn or domainname is None:79 if domainname == fqdn:
80 return ('@', fqdn)80 return ('@', fqdn)
81 else:81 else:
82 # strip off the passed in ".$domainname" from the fqdn.82 # strip off the passed in ".$domainname" from the fqdn.
83 name = fqdn[:-len(domainname) - 1]83 name = fqdn[:-len(domainname) - 1]
84 return (name, domainname)84 return (name, domainname)
85 elif rrtype == 'SRV':85 else:
86 if Domain.objects.filter(name=fqdn).exists():
87 return ('@', fqdn)
88 if rrtype == 'SRV':
86 spec = SRV_LHS89 spec = SRV_LHS
87 else:90 else:
88 spec = LABEL91 spec = LABEL
8992
=== modified file 'src/maasserver/models/staticipaddress.py'
--- src/maasserver/models/staticipaddress.py 2016-07-01 12:23:35 +0000
+++ src/maasserver/models/staticipaddress.py 2016-07-08 00:18:04 +0000
@@ -377,7 +377,7 @@
377 )377 )
378 ORDER BY378 ORDER BY
379 node.hostname,379 node.hostname,
380 is_boot,380 is_boot DESC,
381 family(staticip.ip),381 family(staticip.ip),
382 CASE382 CASE
383 WHEN interface.type = 'bond' AND383 WHEN interface.type = 'bond' AND
@@ -410,6 +410,42 @@
410 END,410 END,
411 interface.id411 interface.id
412 """412 """
413 iface_sql_query = """
414 SELECT
415 node.hostname,
416 node.system_id,
417 node.node_type,
418 """ + ttl_clause + """ AS ttl,
419 domain.name, staticip.ip,
420 interface.name
421 FROM
422 maasserver_interface AS interface
423 JOIN maasserver_node AS node ON
424 node.id = interface.node_id
425 JOIN maasserver_domain as domain ON
426 domain.id = node.domain_id
427 JOIN maasserver_interface_ip_addresses AS link ON
428 link.interface_id = interface.id
429 JOIN maasserver_staticipaddress AS staticip ON
430 staticip.id = link.staticipaddress_id
431 LEFT JOIN maasserver_domain as domain2 ON
432 /* Pick up another copy of domain looking for instances of
433 * the name as the top of a domain.
434 */
435 domain2.name = CONCAT(
436 interface.name, '.', node.hostname, '.', domain.name)
437 WHERE
438 staticip.ip IS NOT NULL AND
439 host(staticip.ip) != '' AND
440 (""" + search_term + """ = %s) AND
441 (
442 node.disable_ipv4 IS FALSE OR
443 family(staticip.ip) <> 4
444 )
445 ORDER BY
446 node.hostname,
447 interface.id
448 """
413 # We get user reserved et al mappings first, so that we can overwrite449 # We get user reserved et al mappings first, so that we can overwrite
414 # TTL as we process the return from the SQL horror above.450 # TTL as we process the return from the SQL horror above.
415 mapping = self._get_user_reserved_mappings(domain_or_subnet)451 mapping = self._get_user_reserved_mappings(domain_or_subnet)
@@ -421,22 +457,31 @@
421 # stripping domain), the boot and non-boot interface ip address in ipv4457 # stripping domain), the boot and non-boot interface ip address in ipv4
422 # and ipv6. Our task: if there are boot interace IPs, they win. If458 # and ipv6. Our task: if there are boot interace IPs, they win. If
423 # there are none, then whatever we got wins. The ORDER BY means that459 # there are none, then whatever we got wins. The ORDER BY means that
424 # we will see all of the non-boot interfaces before we see any boot460 # we will see all of the boot interfaces before we see any non-boot
425 # interface IPs. See Bug#1584850461 # interface IPs. See Bug#1584850
426 for (node_name, system_id, node_type,462 for (node_name, system_id, node_type, ttl, domain_name,
427 ttl, domain_name, ip, is_boot) in cursor.fetchall():463 ip, is_boot) in cursor.fetchall():
428 hostname = "%s.%s" % (strip_domain(node_name), domain_name)464 fqdn = "%s.%s" % (strip_domain(node_name), domain_name)
429 mapping[hostname].node_type = node_type465 mapping[fqdn].node_type = node_type
430 mapping[hostname].system_id = system_id466 mapping[fqdn].system_id = system_id
431 mapping[hostname].ttl = ttl467 mapping[fqdn].ttl = ttl
432 # If this is a boot interface, and we have previously only found468 if is_boot:
433 # non-boot interface IPs, discard them. See also Bug#1584850.469 iface_is_boot[fqdn] = True
434 if is_boot and not iface_is_boot[hostname]:470 # If we have an IP on the right interface type, save it.
435 mapping[hostname].ips = set()471 if is_boot == iface_is_boot[fqdn]:
436 iface_is_boot[hostname] = True472 mapping[fqdn].ips.add(ip)
437 # At this point, if the interface type is correct, keep the IP.473 # Next, get all the addresses, on all the interfaces, and add the ones
438 if is_boot == iface_is_boot[hostname]:474 # that are not already present on the FQDN as $IFACE.$FQDN.
439 mapping[hostname].ips.add(ip)475 cursor.execute(iface_sql_query, (domain_or_subnet.id,))
476 for (node_name, system_id, node_type, ttl,
477 domain_name, ip, iface_name) in cursor.fetchall():
478 fqdn = "%s.%s" % (strip_domain(node_name), domain_name)
479 if ip not in mapping[fqdn].ips:
480 name = "%s.%s" % (iface_name, fqdn)
481 mapping[name].node_type = node_type
482 mapping[name].system_id = system_id
483 mapping[name].ttl = ttl
484 mapping[name].ips.add(ip)
440 return mapping485 return mapping
441486
442 def filter_by_ip_family(self, family):487 def filter_by_ip_family(self, family):
443488
=== modified file 'src/maasserver/models/tests/test_dnsresource.py'
--- src/maasserver/models/tests/test_dnsresource.py 2016-04-22 01:56:28 +0000
+++ src/maasserver/models/tests/test_dnsresource.py 2016-07-08 00:18:04 +0000
@@ -131,9 +131,9 @@
131 parent = "%s.%s" % (131 parent = "%s.%s" % (
132 factory.make_name("b"),132 factory.make_name("b"),
133 factory.make_name("c"))133 factory.make_name("c"))
134 label = factory.make_name("a")134 label = "%s.%s" % (factory.make_name("a"), factory.make_name("d"))
135 name = "%s.%s" % (label, parent)135 name = "%s.%s" % (label, parent)
136 factory.make_Domain(name=name)136 factory.make_Domain(name=parent)
137 self.assertEqual(137 self.assertEqual(
138 (label, parent), separate_fqdn(name, domainname=parent))138 (label, parent), separate_fqdn(name, domainname=parent))
139139
140140
=== modified file 'src/maasserver/models/tests/test_staticipaddress.py'
--- src/maasserver/models/tests/test_staticipaddress.py 2016-07-01 12:26:51 +0000
+++ src/maasserver/models/tests/test_staticipaddress.py 2016-07-08 00:18:04 +0000
@@ -356,23 +356,34 @@
356 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)356 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)
357 self.assertEqual(expected_mapping, mapping)357 self.assertEqual(expected_mapping, mapping)
358358
359 def test_get_hostname_ip_mapping_returns_fqdn(self):359 def test_get_hostname_ip_mapping_returns_fqdn_and_other(self):
360 hostname = factory.make_name('hostname')360 hostname = factory.make_name('hostname')
361 domainname = factory.make_name('domain')361 domainname = factory.make_name('domain')
362 factory.make_Domain(name=domainname)362 factory.make_Domain(name=domainname)
363 full_hostname = "%s.%s" % (hostname, domainname)363 full_hostname = "%s.%s" % (hostname, domainname)
364 subnet = factory.make_Subnet()364 subnet = factory.make_Subnet()
365 node = factory.make_Node_with_Interface_on_Subnet(365 node = factory.make_Node_with_Interface_on_Subnet(
366 interface=True, hostname=full_hostname,366 interface=True, hostname=full_hostname, interface_count=3,
367 subnet=subnet, disable_ipv4=False)367 subnet=subnet, disable_ipv4=False)
368 boot_interface = node.get_boot_interface()368 boot_interface = node.get_boot_interface()
369 staticip = factory.make_StaticIPAddress(369 staticip = factory.make_StaticIPAddress(
370 alloc_type=IPADDRESS_TYPE.STICKY,370 alloc_type=IPADDRESS_TYPE.STICKY,
371 ip=factory.pick_ip_in_Subnet(subnet),371 ip=factory.pick_ip_in_Subnet(subnet),
372 subnet=subnet, interface=boot_interface)372 subnet=subnet, interface=boot_interface)
373 ifaces = node.interface_set.exclude(interface=boot_interface)
374 sip2 = factory.make_StaticIPAddress(
375 alloc_type=IPADDRESS_TYPE.STICKY,
376 ip=factory.pick_ip_in_Subnet(subnet),
377 subnet=subnet, interface=ifaces[0])
373 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet)378 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet)
374 self.assertEqual({full_hostname: HostnameIPMapping(379 self.assertEqual({
375 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)380 full_hostname:
381 HostnameIPMapping(
382 node.system_id, 30, {staticip.ip}, node.node_type),
383 "%s.%s" % (ifaces[0].name, full_hostname):
384 HostnameIPMapping(
385 node.system_id, 30, {sip2.ip}, node.node_type),
386 }, mapping)
376387
377 def make_mapping(self, node, raw_ttl=False):388 def make_mapping(self, node, raw_ttl=False):
378 if raw_ttl or node.address_ttl is not None:389 if raw_ttl or node.address_ttl is not None:
@@ -480,12 +491,16 @@
480 ip=factory.pick_ip_in_Subnet(subnet),491 ip=factory.pick_ip_in_Subnet(subnet),
481 subnet=subnet, interface=boot_interface)492 subnet=subnet, interface=boot_interface)
482 newer_nic = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)493 newer_nic = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
483 factory.make_StaticIPAddress(494 newer_ip = factory.make_StaticIPAddress(
484 alloc_type=IPADDRESS_TYPE.STICKY, interface=newer_nic)495 alloc_type=IPADDRESS_TYPE.STICKY, interface=newer_nic)
485 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(496 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
486 node.domain)497 node.domain)
487 self.assertEqual({node.fqdn: HostnameIPMapping(498 expected_mapping = {
488 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)499 node.fqdn: HostnameIPMapping(
500 node.system_id, 30, {staticip.ip}, node.node_type),
501 "%s.%s" % (newer_nic.name, node.fqdn): HostnameIPMapping(
502 node.system_id, 30, {newer_ip.ip}, node.node_type)}
503 self.assertEqual(expected_mapping, mapping)
489504
490 def test_get_hostname_ip_mapping_picks_sticky_over_auto(self):505 def test_get_hostname_ip_mapping_picks_sticky_over_auto(self):
491 subnet = factory.make_Subnet(506 subnet = factory.make_Subnet(
@@ -499,12 +514,16 @@
499 ip=factory.pick_ip_in_Subnet(subnet),514 ip=factory.pick_ip_in_Subnet(subnet),
500 subnet=subnet, interface=boot_interface)515 subnet=subnet, interface=boot_interface)
501 nic = node.get_boot_interface()516 nic = node.get_boot_interface()
502 factory.make_StaticIPAddress(517 auto_ip = factory.make_StaticIPAddress(
503 alloc_type=IPADDRESS_TYPE.AUTO, interface=nic)518 alloc_type=IPADDRESS_TYPE.AUTO, interface=nic)
504 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(519 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
505 node.domain)520 node.domain)
506 self.assertEqual({node.fqdn: HostnameIPMapping(521 expected_mapping = {
507 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)522 node.fqdn: HostnameIPMapping(
523 node.system_id, 30, {staticip.ip}, node.node_type),
524 "%s.%s" % (nic.name, node.fqdn): HostnameIPMapping(
525 node.system_id, 30, {auto_ip.ip}, node.node_type)}
526 self.assertEqual(expected_mapping, mapping)
508527
509 def test_get_hostname_ip_mapping_combines_IPv4_and_IPv6_addresses(self):528 def test_get_hostname_ip_mapping_combines_IPv4_and_IPv6_addresses(self):
510 node = factory.make_Node(interface=True, disable_ipv4=False)529 node = factory.make_Node(interface=True, disable_ipv4=False)
@@ -583,13 +602,17 @@
583 staticip = factory.make_StaticIPAddress(602 staticip = factory.make_StaticIPAddress(
584 alloc_type=IPADDRESS_TYPE.AUTO, interface=iface,603 alloc_type=IPADDRESS_TYPE.AUTO, interface=iface,
585 subnet=subnet)604 subnet=subnet)
586 factory.make_StaticIPAddress(605 discovered = factory.make_StaticIPAddress(
587 alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=iface,606 alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=iface,
588 subnet=subnet)607 subnet=subnet)
589 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(608 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
590 node.domain)609 node.domain)
591 self.assertEqual({node.fqdn: HostnameIPMapping(610 expected_mapping = {
592 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)611 node.fqdn: HostnameIPMapping(
612 node.system_id, 30, {staticip.ip}, node.node_type),
613 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
614 node.system_id, 30, {discovered.ip}, node.node_type)}
615 self.assertEqual(expected_mapping, mapping)
593616
594 def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self):617 def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self):
595 subnet = factory.make_Subnet(618 subnet = factory.make_Subnet(
@@ -604,7 +627,7 @@
604 iface3 = factory.make_Interface(node=node)627 iface3 = factory.make_Interface(node=node)
605 bondif = factory.make_Interface(628 bondif = factory.make_Interface(
606 INTERFACE_TYPE.BOND, node=node, parents=[iface2, iface3])629 INTERFACE_TYPE.BOND, node=node, parents=[iface2, iface3])
607 factory.make_StaticIPAddress(630 iface_ip = factory.make_StaticIPAddress(
608 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,631 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
609 subnet=subnet)632 subnet=subnet)
610 factory.make_StaticIPAddress(633 factory.make_StaticIPAddress(
@@ -618,8 +641,12 @@
618 subnet=subnet)641 subnet=subnet)
619 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(642 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
620 node.domain)643 node.domain)
621 self.assertEqual({node.fqdn: HostnameIPMapping(644 expected_mapping = {
622 node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping)645 node.fqdn: HostnameIPMapping(
646 node.system_id, 30, {bond_staticip.ip}, node.node_type),
647 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
648 node.system_id, 30, {iface_ip.ip}, node.node_type)}
649 self.assertEqual(expected_mapping, mapping)
623650
624 def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self):651 def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self):
625 subnet = factory.make_Subnet(652 subnet = factory.make_Subnet(
@@ -642,8 +669,11 @@
642 subnet=subnet)669 subnet=subnet)
643 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(670 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
644 node.domain)671 node.domain)
645 self.assertEqual({node.fqdn: HostnameIPMapping(672 expected_mapping = {
646 node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping)673 node.fqdn: HostnameIPMapping(
674 node.system_id, 30, {bond_staticip.ip}, node.node_type),
675 }
676 self.assertEqual(expected_mapping, mapping)
647677
648 def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self):678 def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self):
649 subnet = factory.make_Subnet(679 subnet = factory.make_Subnet(
@@ -665,13 +695,17 @@
665 factory.make_StaticIPAddress(695 factory.make_StaticIPAddress(
666 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface3,696 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface3,
667 subnet=subnet)697 subnet=subnet)
668 factory.make_StaticIPAddress(698 bondip = factory.make_StaticIPAddress(
669 alloc_type=IPADDRESS_TYPE.STICKY, interface=bondif,699 alloc_type=IPADDRESS_TYPE.STICKY, interface=bondif,
670 subnet=subnet)700 subnet=subnet)
671 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(701 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
672 node.domain)702 node.domain)
673 self.assertEqual({node.fqdn: HostnameIPMapping(703 expected_mapping = {
674 node.system_id, 30, {boot_staticip.ip}, node.node_type)}, mapping)704 node.fqdn: HostnameIPMapping(
705 node.system_id, 30, {boot_staticip.ip}, node.node_type),
706 "%s.%s" % (bondif.name, node.fqdn): HostnameIPMapping(
707 node.system_id, 30, {bondip.ip}, node.node_type)}
708 self.assertEqual(expected_mapping, mapping)
675709
676 def test_get_hostname_ip_mapping_prefers_boot_interface(self):710 def test_get_hostname_ip_mapping_prefers_boot_interface(self):
677 subnet = factory.make_Subnet(711 subnet = factory.make_Subnet(
@@ -680,7 +714,7 @@
680 hostname=factory.make_name('host'), subnet=subnet,714 hostname=factory.make_name('host'), subnet=subnet,
681 disable_ipv4=False)715 disable_ipv4=False)
682 iface = node.get_boot_interface()716 iface = node.get_boot_interface()
683 factory.make_StaticIPAddress(717 iface_ip = factory.make_StaticIPAddress(
684 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,718 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
685 subnet=subnet)719 subnet=subnet)
686 new_boot_interface = factory.make_Interface(720 new_boot_interface = factory.make_Interface(
@@ -693,8 +727,12 @@
693 subnet=subnet)727 subnet=subnet)
694 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(728 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
695 node.domain)729 node.domain)
696 self.assertEqual({node.fqdn: HostnameIPMapping(730 expected_mapping = {
697 node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping)731 node.fqdn: HostnameIPMapping(
732 node.system_id, 30, {boot_sip.ip}, node.node_type),
733 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
734 node.system_id, 30, {iface_ip.ip}, node.node_type)}
735 self.assertEqual(expected_mapping, mapping)
698736
699 def test_get_hostname_ip_mapping_prefers_boot_interface_to_alias(self):737 def test_get_hostname_ip_mapping_prefers_boot_interface_to_alias(self):
700 subnet = factory.make_Subnet(738 subnet = factory.make_Subnet(
@@ -703,7 +741,7 @@
703 hostname=factory.make_name('host'), subnet=subnet,741 hostname=factory.make_name('host'), subnet=subnet,
704 disable_ipv4=False)742 disable_ipv4=False)
705 iface = node.get_boot_interface()743 iface = node.get_boot_interface()
706 factory.make_StaticIPAddress(744 iface_ip = factory.make_StaticIPAddress(
707 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,745 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
708 subnet=subnet)746 subnet=subnet)
709 new_boot_interface = factory.make_Interface(747 new_boot_interface = factory.make_Interface(
@@ -716,8 +754,12 @@
716 subnet=subnet)754 subnet=subnet)
717 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(755 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
718 node.domain)756 node.domain)
719 self.assertEqual({node.fqdn: HostnameIPMapping(757 expected_mapping = {
720 node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping)758 node.fqdn: HostnameIPMapping(
759 node.system_id, 30, {boot_sip.ip}, node.node_type),
760 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
761 node.system_id, 30, {iface_ip.ip}, node.node_type)}
762 self.assertEqual(expected_mapping, mapping)
721763
722 def test_get_hostname_ip_mapping_prefers_physical_interfaces_to_vlan(self):764 def test_get_hostname_ip_mapping_prefers_physical_interfaces_to_vlan(self):
723 subnet = factory.make_Subnet(765 subnet = factory.make_Subnet(
@@ -731,13 +773,17 @@
731 phy_staticip = factory.make_StaticIPAddress(773 phy_staticip = factory.make_StaticIPAddress(
732 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,774 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
733 subnet=subnet)775 subnet=subnet)
734 factory.make_StaticIPAddress(776 vlanip = factory.make_StaticIPAddress(
735 alloc_type=IPADDRESS_TYPE.STICKY, interface=vlanif,777 alloc_type=IPADDRESS_TYPE.STICKY, interface=vlanif,
736 subnet=subnet)778 subnet=subnet)
737 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(779 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
738 node.domain)780 node.domain)
739 self.assertEqual({node.fqdn: HostnameIPMapping(781 expected_mapping = {
740 node.system_id, 30, {phy_staticip.ip}, node.node_type)}, mapping)782 node.fqdn: HostnameIPMapping(
783 node.system_id, 30, {phy_staticip.ip}, node.node_type),
784 "%s.%s" % (vlanif.name, node.fqdn): HostnameIPMapping(
785 node.system_id, 30, {vlanip.ip}, node.node_type)}
786 self.assertEqual(expected_mapping, mapping)
741787
742 @skip("XXX: GavinPanella 2016-04-27 bug=1556188: Fails spuriously.")788 @skip("XXX: GavinPanella 2016-04-27 bug=1556188: Fails spuriously.")
743 def test_get_hostname_ip_mapping_returns_domain_head_ips(self):789 def test_get_hostname_ip_mapping_returns_domain_head_ips(self):
@@ -790,8 +836,9 @@
790 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)836 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)
791 expected_mapping = {837 expected_mapping = {
792 node.fqdn: HostnameIPMapping(838 node.fqdn: HostnameIPMapping(
793 node.system_id, 30, {sip0.ip}, node.node_type839 node.system_id, 30, {sip0.ip}, node.node_type),
794 )}840 "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping(
841 node.system_id, 30, {sip1.ip}, node.node_type)}
795 self.assertEqual(expected_mapping, mapping)842 self.assertEqual(expected_mapping, mapping)
796843
797 def test_get_hostname_ip_mapping_returns_correct_bond_ip(self):844 def test_get_hostname_ip_mapping_returns_correct_bond_ip(self):
@@ -837,8 +884,10 @@
837 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)884 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)
838 expected_mapping = {885 expected_mapping = {
839 node.fqdn: HostnameIPMapping(886 node.fqdn: HostnameIPMapping(
840 node.system_id, 30, {bond_sip.ip}, node.node_type887 node.system_id, 30, {bond_sip.ip}, node.node_type),
841 )}888 "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping(
889 node.system_id, 30, {sip1.ip}, node.node_type),
890 }
842 self.assertEqual(expected_mapping, mapping)891 self.assertEqual(expected_mapping, mapping)
843892
844893