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

Proposed by LaMont Jones on 2016-07-08
Status: Merged
Approved by: LaMont Jones on 2016-07-11
Approved revision: 5150
Merged at revision: 5150
Proposed branch: lp:~lamont/maas/bug-1599223-2.0
Merge into: lp:maas/2.0
Diff against target: 696 lines (+237/-135)
6 files modified
src/maasserver/dns/tests/test_zonegenerator.py (+11/-22)
src/maasserver/dns/zonegenerator.py (+12/-38)
src/maasserver/models/dnsresource.py (+6/-3)
src/maasserver/models/staticipaddress.py (+102/-36)
src/maasserver/models/tests/test_dnsresource.py (+2/-2)
src/maasserver/models/tests/test_staticipaddress.py (+104/-34)
To merge this branch: bzr merge lp:~lamont/maas/bug-1599223-2.0
Reviewer Review Type Date Requested Status
LaMont Jones (community) Approve on 2016-07-11
Review via email: mp+299586@code.launchpad.net

Commit message

Refactor get_hostname_ip_mapping() to also return interface-named rows.
When generating reverse DNS, we have to consider all of the forward zones, not just our subnet.

Description of the change

Refactor get_hostname_ip_mapping() to also return interface-named rows.
When generating reverse DNS, we have to consider all of the forward zones, not just our subnet.

To post a comment you must log in.
lp:~lamont/maas/bug-1599223-2.0 updated on 2016-07-08
5150. By LaMont Jones on 2016-07-08

Clean up the ugly SQL and better optimize it.

LaMont Jones (lamont) wrote :

Simple backport from trunk to 2.0.

review: Approve

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 20:46:18 +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 20:46:18 +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 (
@@ -255,12 +251,16 @@
255 IPNetwork("%s/124" % network.network).network)251 IPNetwork("%s/124" % network.network).network)
256 rfc2317_glue.setdefault(basenet, set()).add(network)252 rfc2317_glue.setdefault(basenet, set()).add(network)
257253
254 # Since get_hostname_ip_mapping(Subnet) ignores Subnet.id, so we can
255 # just do it once and be happy. LP#1600259
256 if len(subnets):
257 mappings['reverse'] = mappings[Subnet.objects.first()]
258
258 # For each of the zones that we are generating (one or more per259 # For each of the zones that we are generating (one or more per
259 # subnet), compile the zone from:260 # subnet), compile the zone from:
260 # 1. Dynamic ranges on this subnet.261 # 1. Dynamic ranges on this subnet.
261 # 2. Node: ip mapping(subnet), including DNSResource records for262 # 2. Node: ip mapping(subnet), including DNSResource records for
262 # StaticIPAddresses in this subnet.263 # 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 can264 # 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 sort265 # correctly gather the rfc2317 glue that we need. Failure to sort
266 # means that we wind up grabbing (and deleting) the rfc2317 glue info266 # means that we wind up grabbing (and deleting) the rfc2317 glue info
@@ -283,38 +283,12 @@
283 for ip_range in subnet.get_dynamic_ranges()283 for ip_range in subnet.get_dynamic_ranges()
284 ]284 ]
285285
286 # 2. Start with the map of all of the nodes on this subnet,286 # 2. Start with the map of all of the nodes, including all
287 # including all DNSResource-associated addresses.287 # DNSResource-associated addresses. We will prune this to just
288 mapping = mappings[subnet]288 # entries for the subnet when we actually generate the zonefile.
289289 # If we get here, then we have subnets, so we noticed that above
290 # 3. Add all of the interface named records.290 # and created mappings['reverse']. LP#1600259
291 # 2015-12-18 lamont N.B., these are not found in the forward zone,291 mapping = mappings['reverse']
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 })
318292
319 # Use the default_domain as the name for the NS host in the reverse293 # Use the default_domain as the name for the NS host in the reverse
320 # zones. If this network is actually a parent rfc2317 glue294 # zones. If this network is actually a parent rfc2317 glue
321295
=== 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 20:46:18 +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-05-26 17:50:49 +0000
+++ src/maasserver/models/staticipaddress.py 2016-07-08 20:46:18 +0000
@@ -51,7 +51,6 @@
51from maasserver.models.domain import Domain51from maasserver.models.domain import Domain
52from maasserver.models.subnet import Subnet52from maasserver.models.subnet import Subnet
53from maasserver.models.timestampedmodel import TimestampedModel53from maasserver.models.timestampedmodel import TimestampedModel
54from maasserver.utils import strip_domain
55from maasserver.utils.dns import get_ip_based_hostname54from maasserver.utils.dns import get_ip_based_hostname
56from maasserver.utils.orm import (55from maasserver.utils.orm import (
57 make_serialization_failure,56 make_serialization_failure,
@@ -226,8 +225,10 @@
226 qs = self.filter(225 qs = self.filter(
227 Q(alloc_type=IPADDRESS_TYPE.USER_RESERVED) |226 Q(alloc_type=IPADDRESS_TYPE.USER_RESERVED) |
228 Q(dnsresource__isnull=False))227 Q(dnsresource__isnull=False))
228 # If this is a subnet, we need to ignore subnet.id, as per
229 # get_hostname_ip_mapping(). LP#1600259
229 if isinstance(domain_or_subnet, Subnet):230 if isinstance(domain_or_subnet, Subnet):
230 qs = qs.filter(subnet_id=domain_or_subnet.id)231 pass
231 elif isinstance(domain_or_subnet, Domain):232 elif isinstance(domain_or_subnet, Domain):
232 qs = qs.filter(dnsresource__domain_id=domain_or_subnet.id)233 qs = qs.filter(dnsresource__domain_id=domain_or_subnet.id)
233 qs = qs.prefetch_related("dnsresource_set")234 qs = qs.prefetch_related("dnsresource_set")
@@ -307,20 +308,6 @@
307 # return the IPs for the oldest Interface address.308 # return the IPs for the oldest Interface address.
308 #309 #
309 # For nodes that have disable_ipv4 set, leave out any IPv4 address.310 # For nodes that have disable_ipv4 set, leave out any IPv4 address.
310 if isinstance(domain_or_subnet, Subnet):
311 search_term = "staticip.subnet_id"
312 elif isinstance(domain_or_subnet, Domain):
313 # The model has nodes in the parent domain, but they actually live
314 # in the child domain. And the parent needs the glue. So we
315 # return such nodes addresses in _BOTH_ the parent and the child
316 # domains. domain2.name will be non-null if this host's fqdn is the
317 # name of a domain in MAAS.
318 # n.b. The SQL says: WHERE ... $search_term = %s ...
319 # we overload that to accomplish our objectives.
320 search_term = """domain2.name IS NOT NULL OR node.domain_id"""
321 else:
322 raise ValueError('bad object passed to get_hostname_ip_mapping')
323
324 default_ttl = "%d" % Config.objects.get_config('default_dns_ttl')311 default_ttl = "%d" % Config.objects.get_config('default_dns_ttl')
325 if raw_ttl:312 if raw_ttl:
326 ttl_clause = """node.address_ttl"""313 ttl_clause = """node.address_ttl"""
@@ -332,11 +319,11 @@
332 %s)""" % default_ttl319 %s)""" % default_ttl
333 sql_query = """320 sql_query = """
334 SELECT DISTINCT ON (node.hostname, is_boot, family(staticip.ip))321 SELECT DISTINCT ON (node.hostname, is_boot, family(staticip.ip))
335 node.hostname,322 CONCAT(node.hostname, '.', domain.name) AS fqdn,
336 node.system_id,323 node.system_id,
337 node.node_type,324 node.node_type,
338 """ + ttl_clause + """ AS ttl,325 """ + ttl_clause + """ AS ttl,
339 domain.name, staticip.ip,326 staticip.ip,
340 COALESCE(327 COALESCE(
341 node.boot_interface_id IS NOT NULL AND328 node.boot_interface_id IS NOT NULL AND
342 (329 (
@@ -359,22 +346,42 @@
359 link.interface_id = interface.id346 link.interface_id = interface.id
360 JOIN maasserver_staticipaddress AS staticip ON347 JOIN maasserver_staticipaddress AS staticip ON
361 staticip.id = link.staticipaddress_id348 staticip.id = link.staticipaddress_id
349 """
350 if isinstance(domain_or_subnet, Domain):
351 # The model has nodes in the parent domain, but they actually live
352 # in the child domain. And the parent needs the glue. So we
353 # return such nodes addresses in _BOTH_ the parent and the child
354 # domains. domain2.name will be non-null if this host's fqdn is the
355 # name of a domain in MAAS.
356 sql_query += """
362 LEFT JOIN maasserver_domain as domain2 ON357 LEFT JOIN maasserver_domain as domain2 ON
363 /* Pick up another copy of domain looking for instances of358 /* Pick up another copy of domain looking for instances of
364 * nodes a the top of a domain.359 * nodes a the top of a domain.
365 */360 */
366 domain2.name = CONCAT(node.hostname, '.', domain.name)361 domain2.name = CONCAT(node.hostname, '.', domain.name)
367 WHERE362 WHERE
363 (domain2.name IS NOT NULL OR node.domain_id = %s) AND
364 """
365 query_parms = [domain_or_subnet.id, ]
366 else:
367 # For subnets, we need ALL the names, so that we can correctly
368 # identify which ones should have the FQDN. dns/zonegenerator.py
369 # optimizes based on this, and only calls once with a subnet,
370 # expecting to get all the subnets back in one table.
371 sql_query += """
372 WHERE
373 """
374 query_parms = []
375 sql_query += """
368 staticip.ip IS NOT NULL AND376 staticip.ip IS NOT NULL AND
369 host(staticip.ip) != '' AND377 host(staticip.ip) != '' AND
370 (""" + search_term + """ = %s) AND
371 (378 (
372 node.disable_ipv4 IS FALSE OR379 node.disable_ipv4 IS FALSE OR
373 family(staticip.ip) <> 4380 family(staticip.ip) <> 4
374 )381 )
375 ORDER BY382 ORDER BY
376 node.hostname,383 node.hostname,
377 is_boot,384 is_boot DESC,
378 family(staticip.ip),385 family(staticip.ip),
379 CASE386 CASE
380 WHEN interface.type = 'bond' AND387 WHEN interface.type = 'bond' AND
@@ -407,33 +414,92 @@
407 END,414 END,
408 interface.id415 interface.id
409 """416 """
417 iface_sql_query = """
418 SELECT
419 CONCAT(node.hostname, '.', domain.name) AS fqdn,
420 node.system_id,
421 node.node_type,
422 """ + ttl_clause + """ AS ttl,
423 staticip.ip,
424 interface.name
425 FROM
426 maasserver_interface AS interface
427 JOIN maasserver_node AS node ON
428 node.id = interface.node_id
429 JOIN maasserver_domain as domain ON
430 domain.id = node.domain_id
431 JOIN maasserver_interface_ip_addresses AS link ON
432 link.interface_id = interface.id
433 JOIN maasserver_staticipaddress AS staticip ON
434 staticip.id = link.staticipaddress_id
435 """
436 if isinstance(domain_or_subnet, Domain):
437 # This logic is similar to the logic in sql_query above.
438 iface_sql_query += """
439 LEFT JOIN maasserver_domain as domain2 ON
440 /* Pick up another copy of domain looking for instances of
441 * the name as the top of a domain.
442 */
443 domain2.name = CONCAT(
444 interface.name, '.', node.hostname, '.', domain.name)
445 WHERE
446 (domain2.name IS NOT NULL OR node.domain_id = %s) AND
447 """
448 else:
449 # For subnets, we need ALL the names, so that we can correctly
450 # identify which ones should have the FQDN. dns/zonegenerator.py
451 # optimizes based on this, and only calls once with a subnet,
452 # expecting to get all the subnets back in one table.
453 iface_sql_query += """
454 WHERE
455 """
456 iface_sql_query += """
457 staticip.ip IS NOT NULL AND
458 host(staticip.ip) != '' AND
459 (
460 node.disable_ipv4 IS FALSE OR
461 family(staticip.ip) <> 4
462 )
463 ORDER BY
464 node.hostname,
465 interface.id
466 """
410 # We get user reserved et al mappings first, so that we can overwrite467 # We get user reserved et al mappings first, so that we can overwrite
411 # TTL as we process the return from the SQL horror above.468 # TTL as we process the return from the SQL horror above.
412 mapping = self._get_user_reserved_mappings(domain_or_subnet)469 mapping = self._get_user_reserved_mappings(domain_or_subnet)
470 # All of the mappings that we got mean that we will only want to add
471 # addresses for the boot interface (is_boot == True).
413 iface_is_boot = defaultdict(bool, {472 iface_is_boot = defaultdict(bool, {
414 hostname: True for hostname in mapping.keys()473 hostname: True for hostname in mapping.keys()
415 })474 })
416 cursor.execute(sql_query, (domain_or_subnet.id,))475 cursor.execute(sql_query, query_parms)
417 # The records from the query provide, for each hostname (after476 # The records from the query provide, for each hostname (after
418 # stripping domain), the boot and non-boot interface ip address in ipv4477 # stripping domain), the boot and non-boot interface ip address in ipv4
419 # and ipv6. Our task: if there are boot interace IPs, they win. If478 # and ipv6. Our task: if there are boot interace IPs, they win. If
420 # there are none, then whatever we got wins. The ORDER BY means that479 # there are none, then whatever we got wins. The ORDER BY means that
421 # we will see all of the non-boot interfaces before we see any boot480 # we will see all of the boot interfaces before we see any non-boot
422 # interface IPs. See Bug#1584850481 # interface IPs. See Bug#1584850
423 for (node_name, system_id, node_type,482 for (fqdn, system_id, node_type, ttl,
424 ttl, domain_name, ip, is_boot) in cursor.fetchall():483 ip, is_boot) in cursor.fetchall():
425 hostname = "%s.%s" % (strip_domain(node_name), domain_name)484 mapping[fqdn].node_type = node_type
426 mapping[hostname].node_type = node_type485 mapping[fqdn].system_id = system_id
427 mapping[hostname].system_id = system_id486 mapping[fqdn].ttl = ttl
428 mapping[hostname].ttl = ttl487 if is_boot:
429 # If this is a boot interface, and we have previously only found488 iface_is_boot[fqdn] = True
430 # non-boot interface IPs, discard them. See also Bug#1584850.489 # If we have an IP on the right interface type, save it.
431 if is_boot and not iface_is_boot[hostname]:490 if is_boot == iface_is_boot[fqdn]:
432 mapping[hostname].ips = set()491 mapping[fqdn].ips.add(ip)
433 iface_is_boot[hostname] = True492 # Next, get all the addresses, on all the interfaces, and add the ones
434 # At this point, if the interface type is correct, keep the IP.493 # that are not already present on the FQDN as $IFACE.$FQDN.
435 if is_boot == iface_is_boot[hostname]:494 cursor.execute(iface_sql_query, (domain_or_subnet.id,))
436 mapping[hostname].ips.add(ip)495 for (fqdn, system_id, node_type, ttl,
496 ip, iface_name) in cursor.fetchall():
497 if ip not in mapping[fqdn].ips:
498 name = "%s.%s" % (iface_name, fqdn)
499 mapping[name].node_type = node_type
500 mapping[name].system_id = system_id
501 mapping[name].ttl = ttl
502 mapping[name].ips.add(ip)
437 return mapping503 return mapping
438504
439 def filter_by_ip_family(self, family):505 def filter_by_ip_family(self, family):
440506
=== 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 20:46:18 +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-05-26 17:50:49 +0000
+++ src/maasserver/models/tests/test_staticipaddress.py 2016-07-08 20:46:18 +0000
@@ -33,6 +33,7 @@
33 HostnameIPMapping,33 HostnameIPMapping,
34 StaticIPAddress,34 StaticIPAddress,
35)35)
36from maasserver.models.subnet import Subnet
36from maasserver.testing.factory import factory37from maasserver.testing.factory import factory
37from maasserver.testing.testcase import (38from maasserver.testing.testcase import (
38 MAASServerTestCase,39 MAASServerTestCase,
@@ -357,23 +358,54 @@
357 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)358 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)
358 self.assertEqual(expected_mapping, mapping)359 self.assertEqual(expected_mapping, mapping)
359360
360 def test_get_hostname_ip_mapping_returns_fqdn(self):361 def test_get_hostname_ip_mapping_returns_all_mappings_for_subnet(self):
362 domain = Domain.objects.get_default_domain()
363 expected_mapping = {}
364 for _ in range(3):
365 node = factory.make_Node(interface=True, disable_ipv4=False)
366 boot_interface = node.get_boot_interface()
367 subnet = factory.make_Subnet()
368 staticip = factory.make_StaticIPAddress(
369 alloc_type=IPADDRESS_TYPE.STICKY,
370 ip=factory.pick_ip_in_Subnet(subnet),
371 subnet=subnet, interface=boot_interface)
372 full_hostname = "%s.%s" % (node.hostname, domain.name)
373 expected_mapping[full_hostname] = HostnameIPMapping(
374 node.system_id, 30, {staticip.ip}, node.node_type)
375 # See also LP#1600259. It doesn't matter what subnet is passed in, you
376 # get all of them.
377 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
378 Subnet.objects.first())
379 self.assertEqual(expected_mapping, mapping)
380
381 def test_get_hostname_ip_mapping_returns_fqdn_and_other(self):
361 hostname = factory.make_name('hostname')382 hostname = factory.make_name('hostname')
362 domainname = factory.make_name('domain')383 domainname = factory.make_name('domain')
363 factory.make_Domain(name=domainname)384 factory.make_Domain(name=domainname)
364 full_hostname = "%s.%s" % (hostname, domainname)385 full_hostname = "%s.%s" % (hostname, domainname)
365 subnet = factory.make_Subnet()386 subnet = factory.make_Subnet()
366 node = factory.make_Node_with_Interface_on_Subnet(387 node = factory.make_Node_with_Interface_on_Subnet(
367 interface=True, hostname=full_hostname,388 interface=True, hostname=full_hostname, interface_count=3,
368 subnet=subnet, disable_ipv4=False)389 subnet=subnet, disable_ipv4=False)
369 boot_interface = node.get_boot_interface()390 boot_interface = node.get_boot_interface()
370 staticip = factory.make_StaticIPAddress(391 staticip = factory.make_StaticIPAddress(
371 alloc_type=IPADDRESS_TYPE.STICKY,392 alloc_type=IPADDRESS_TYPE.STICKY,
372 ip=factory.pick_ip_in_Subnet(subnet),393 ip=factory.pick_ip_in_Subnet(subnet),
373 subnet=subnet, interface=boot_interface)394 subnet=subnet, interface=boot_interface)
395 ifaces = node.interface_set.exclude(interface=boot_interface)
396 sip2 = factory.make_StaticIPAddress(
397 alloc_type=IPADDRESS_TYPE.STICKY,
398 ip=factory.pick_ip_in_Subnet(subnet),
399 subnet=subnet, interface=ifaces[0])
374 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet)400 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(subnet)
375 self.assertEqual({full_hostname: HostnameIPMapping(401 self.assertEqual({
376 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)402 full_hostname:
403 HostnameIPMapping(
404 node.system_id, 30, {staticip.ip}, node.node_type),
405 "%s.%s" % (ifaces[0].name, full_hostname):
406 HostnameIPMapping(
407 node.system_id, 30, {sip2.ip}, node.node_type),
408 }, mapping)
377409
378 def make_mapping(self, node, raw_ttl=False):410 def make_mapping(self, node, raw_ttl=False):
379 if raw_ttl or node.address_ttl is not None:411 if raw_ttl or node.address_ttl is not None:
@@ -481,12 +513,16 @@
481 ip=factory.pick_ip_in_Subnet(subnet),513 ip=factory.pick_ip_in_Subnet(subnet),
482 subnet=subnet, interface=boot_interface)514 subnet=subnet, interface=boot_interface)
483 newer_nic = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)515 newer_nic = factory.make_Interface(INTERFACE_TYPE.PHYSICAL, node=node)
484 factory.make_StaticIPAddress(516 newer_ip = factory.make_StaticIPAddress(
485 alloc_type=IPADDRESS_TYPE.STICKY, interface=newer_nic)517 alloc_type=IPADDRESS_TYPE.STICKY, interface=newer_nic)
486 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(518 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
487 node.domain)519 node.domain)
488 self.assertEqual({node.fqdn: HostnameIPMapping(520 expected_mapping = {
489 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)521 node.fqdn: HostnameIPMapping(
522 node.system_id, 30, {staticip.ip}, node.node_type),
523 "%s.%s" % (newer_nic.name, node.fqdn): HostnameIPMapping(
524 node.system_id, 30, {newer_ip.ip}, node.node_type)}
525 self.assertEqual(expected_mapping, mapping)
490526
491 def test_get_hostname_ip_mapping_picks_sticky_over_auto(self):527 def test_get_hostname_ip_mapping_picks_sticky_over_auto(self):
492 subnet = factory.make_Subnet(528 subnet = factory.make_Subnet(
@@ -500,12 +536,16 @@
500 ip=factory.pick_ip_in_Subnet(subnet),536 ip=factory.pick_ip_in_Subnet(subnet),
501 subnet=subnet, interface=boot_interface)537 subnet=subnet, interface=boot_interface)
502 nic = node.get_boot_interface()538 nic = node.get_boot_interface()
503 factory.make_StaticIPAddress(539 auto_ip = factory.make_StaticIPAddress(
504 alloc_type=IPADDRESS_TYPE.AUTO, interface=nic)540 alloc_type=IPADDRESS_TYPE.AUTO, interface=nic)
505 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(541 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
506 node.domain)542 node.domain)
507 self.assertEqual({node.fqdn: HostnameIPMapping(543 expected_mapping = {
508 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)544 node.fqdn: HostnameIPMapping(
545 node.system_id, 30, {staticip.ip}, node.node_type),
546 "%s.%s" % (nic.name, node.fqdn): HostnameIPMapping(
547 node.system_id, 30, {auto_ip.ip}, node.node_type)}
548 self.assertEqual(expected_mapping, mapping)
509549
510 def test_get_hostname_ip_mapping_combines_IPv4_and_IPv6_addresses(self):550 def test_get_hostname_ip_mapping_combines_IPv4_and_IPv6_addresses(self):
511 node = factory.make_Node(interface=True, disable_ipv4=False)551 node = factory.make_Node(interface=True, disable_ipv4=False)
@@ -584,13 +624,17 @@
584 staticip = factory.make_StaticIPAddress(624 staticip = factory.make_StaticIPAddress(
585 alloc_type=IPADDRESS_TYPE.AUTO, interface=iface,625 alloc_type=IPADDRESS_TYPE.AUTO, interface=iface,
586 subnet=subnet)626 subnet=subnet)
587 factory.make_StaticIPAddress(627 discovered = factory.make_StaticIPAddress(
588 alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=iface,628 alloc_type=IPADDRESS_TYPE.DISCOVERED, interface=iface,
589 subnet=subnet)629 subnet=subnet)
590 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(630 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
591 node.domain)631 node.domain)
592 self.assertEqual({node.fqdn: HostnameIPMapping(632 expected_mapping = {
593 node.system_id, 30, {staticip.ip}, node.node_type)}, mapping)633 node.fqdn: HostnameIPMapping(
634 node.system_id, 30, {staticip.ip}, node.node_type),
635 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
636 node.system_id, 30, {discovered.ip}, node.node_type)}
637 self.assertEqual(expected_mapping, mapping)
594638
595 def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self):639 def test_get_hostname_ip_mapping_prefers_bond_with_no_boot_interface(self):
596 subnet = factory.make_Subnet(640 subnet = factory.make_Subnet(
@@ -605,7 +649,7 @@
605 iface3 = factory.make_Interface(node=node)649 iface3 = factory.make_Interface(node=node)
606 bondif = factory.make_Interface(650 bondif = factory.make_Interface(
607 INTERFACE_TYPE.BOND, node=node, parents=[iface2, iface3])651 INTERFACE_TYPE.BOND, node=node, parents=[iface2, iface3])
608 factory.make_StaticIPAddress(652 iface_ip = factory.make_StaticIPAddress(
609 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,653 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
610 subnet=subnet)654 subnet=subnet)
611 factory.make_StaticIPAddress(655 factory.make_StaticIPAddress(
@@ -619,8 +663,12 @@
619 subnet=subnet)663 subnet=subnet)
620 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(664 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
621 node.domain)665 node.domain)
622 self.assertEqual({node.fqdn: HostnameIPMapping(666 expected_mapping = {
623 node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping)667 node.fqdn: HostnameIPMapping(
668 node.system_id, 30, {bond_staticip.ip}, node.node_type),
669 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
670 node.system_id, 30, {iface_ip.ip}, node.node_type)}
671 self.assertEqual(expected_mapping, mapping)
624672
625 def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self):673 def test_get_hostname_ip_mapping_prefers_bond_with_boot_interface(self):
626 subnet = factory.make_Subnet(674 subnet = factory.make_Subnet(
@@ -643,8 +691,11 @@
643 subnet=subnet)691 subnet=subnet)
644 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(692 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
645 node.domain)693 node.domain)
646 self.assertEqual({node.fqdn: HostnameIPMapping(694 expected_mapping = {
647 node.system_id, 30, {bond_staticip.ip}, node.node_type)}, mapping)695 node.fqdn: HostnameIPMapping(
696 node.system_id, 30, {bond_staticip.ip}, node.node_type),
697 }
698 self.assertEqual(expected_mapping, mapping)
648699
649 def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self):700 def test_get_hostname_ip_mapping_ignores_bond_without_boot_interface(self):
650 subnet = factory.make_Subnet(701 subnet = factory.make_Subnet(
@@ -666,13 +717,17 @@
666 factory.make_StaticIPAddress(717 factory.make_StaticIPAddress(
667 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface3,718 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface3,
668 subnet=subnet)719 subnet=subnet)
669 factory.make_StaticIPAddress(720 bondip = factory.make_StaticIPAddress(
670 alloc_type=IPADDRESS_TYPE.STICKY, interface=bondif,721 alloc_type=IPADDRESS_TYPE.STICKY, interface=bondif,
671 subnet=subnet)722 subnet=subnet)
672 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(723 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
673 node.domain)724 node.domain)
674 self.assertEqual({node.fqdn: HostnameIPMapping(725 expected_mapping = {
675 node.system_id, 30, {boot_staticip.ip}, node.node_type)}, mapping)726 node.fqdn: HostnameIPMapping(
727 node.system_id, 30, {boot_staticip.ip}, node.node_type),
728 "%s.%s" % (bondif.name, node.fqdn): HostnameIPMapping(
729 node.system_id, 30, {bondip.ip}, node.node_type)}
730 self.assertEqual(expected_mapping, mapping)
676731
677 def test_get_hostname_ip_mapping_prefers_boot_interface(self):732 def test_get_hostname_ip_mapping_prefers_boot_interface(self):
678 subnet = factory.make_Subnet(733 subnet = factory.make_Subnet(
@@ -681,7 +736,7 @@
681 hostname=factory.make_name('host'), subnet=subnet,736 hostname=factory.make_name('host'), subnet=subnet,
682 disable_ipv4=False)737 disable_ipv4=False)
683 iface = node.get_boot_interface()738 iface = node.get_boot_interface()
684 factory.make_StaticIPAddress(739 iface_ip = factory.make_StaticIPAddress(
685 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,740 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
686 subnet=subnet)741 subnet=subnet)
687 new_boot_interface = factory.make_Interface(742 new_boot_interface = factory.make_Interface(
@@ -694,8 +749,12 @@
694 subnet=subnet)749 subnet=subnet)
695 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(750 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
696 node.domain)751 node.domain)
697 self.assertEqual({node.fqdn: HostnameIPMapping(752 expected_mapping = {
698 node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping)753 node.fqdn: HostnameIPMapping(
754 node.system_id, 30, {boot_sip.ip}, node.node_type),
755 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
756 node.system_id, 30, {iface_ip.ip}, node.node_type)}
757 self.assertEqual(expected_mapping, mapping)
699758
700 def test_get_hostname_ip_mapping_prefers_boot_interface_to_alias(self):759 def test_get_hostname_ip_mapping_prefers_boot_interface_to_alias(self):
701 subnet = factory.make_Subnet(760 subnet = factory.make_Subnet(
@@ -704,7 +763,7 @@
704 hostname=factory.make_name('host'), subnet=subnet,763 hostname=factory.make_name('host'), subnet=subnet,
705 disable_ipv4=False)764 disable_ipv4=False)
706 iface = node.get_boot_interface()765 iface = node.get_boot_interface()
707 factory.make_StaticIPAddress(766 iface_ip = factory.make_StaticIPAddress(
708 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,767 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
709 subnet=subnet)768 subnet=subnet)
710 new_boot_interface = factory.make_Interface(769 new_boot_interface = factory.make_Interface(
@@ -717,8 +776,12 @@
717 subnet=subnet)776 subnet=subnet)
718 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(777 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
719 node.domain)778 node.domain)
720 self.assertEqual({node.fqdn: HostnameIPMapping(779 expected_mapping = {
721 node.system_id, 30, {boot_sip.ip}, node.node_type)}, mapping)780 node.fqdn: HostnameIPMapping(
781 node.system_id, 30, {boot_sip.ip}, node.node_type),
782 "%s.%s" % (iface.name, node.fqdn): HostnameIPMapping(
783 node.system_id, 30, {iface_ip.ip}, node.node_type)}
784 self.assertEqual(expected_mapping, mapping)
722785
723 def test_get_hostname_ip_mapping_prefers_physical_interfaces_to_vlan(self):786 def test_get_hostname_ip_mapping_prefers_physical_interfaces_to_vlan(self):
724 subnet = factory.make_Subnet(787 subnet = factory.make_Subnet(
@@ -732,13 +795,17 @@
732 phy_staticip = factory.make_StaticIPAddress(795 phy_staticip = factory.make_StaticIPAddress(
733 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,796 alloc_type=IPADDRESS_TYPE.STICKY, interface=iface,
734 subnet=subnet)797 subnet=subnet)
735 factory.make_StaticIPAddress(798 vlanip = factory.make_StaticIPAddress(
736 alloc_type=IPADDRESS_TYPE.STICKY, interface=vlanif,799 alloc_type=IPADDRESS_TYPE.STICKY, interface=vlanif,
737 subnet=subnet)800 subnet=subnet)
738 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(801 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
739 node.domain)802 node.domain)
740 self.assertEqual({node.fqdn: HostnameIPMapping(803 expected_mapping = {
741 node.system_id, 30, {phy_staticip.ip}, node.node_type)}, mapping)804 node.fqdn: HostnameIPMapping(
805 node.system_id, 30, {phy_staticip.ip}, node.node_type),
806 "%s.%s" % (vlanif.name, node.fqdn): HostnameIPMapping(
807 node.system_id, 30, {vlanip.ip}, node.node_type)}
808 self.assertEqual(expected_mapping, mapping)
742809
743 @skip("XXX: GavinPanella 2016-04-27 bug=1556188: Fails spuriously.")810 @skip("XXX: GavinPanella 2016-04-27 bug=1556188: Fails spuriously.")
744 def test_get_hostname_ip_mapping_returns_domain_head_ips(self):811 def test_get_hostname_ip_mapping_returns_domain_head_ips(self):
@@ -791,8 +858,9 @@
791 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)858 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)
792 expected_mapping = {859 expected_mapping = {
793 node.fqdn: HostnameIPMapping(860 node.fqdn: HostnameIPMapping(
794 node.system_id, 30, {sip0.ip}, node.node_type861 node.system_id, 30, {sip0.ip}, node.node_type),
795 )}862 "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping(
863 node.system_id, 30, {sip1.ip}, node.node_type)}
796 self.assertEqual(expected_mapping, mapping)864 self.assertEqual(expected_mapping, mapping)
797865
798 def test_get_hostname_ip_mapping_returns_correct_bond_ip(self):866 def test_get_hostname_ip_mapping_returns_correct_bond_ip(self):
@@ -838,8 +906,10 @@
838 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)906 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(domain)
839 expected_mapping = {907 expected_mapping = {
840 node.fqdn: HostnameIPMapping(908 node.fqdn: HostnameIPMapping(
841 node.system_id, 30, {bond_sip.ip}, node.node_type909 node.system_id, 30, {bond_sip.ip}, node.node_type),
842 )}910 "%s.%s" % (iface1.name, node.fqdn): HostnameIPMapping(
911 node.system_id, 30, {sip1.ip}, node.node_type),
912 }
843 self.assertEqual(expected_mapping, mapping)913 self.assertEqual(expected_mapping, mapping)
844914
845915

Subscribers

People subscribed via source and target branches

to all changes: