Merge ~mpontillo/maas:default-dns-ip--bug-1776604--2.4 into maas:2.4

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: d0ad4b6d9546278eb72e03c1df8891a83a6692e1
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~mpontillo/maas:default-dns-ip--bug-1776604--2.4
Merge into: maas:2.4
Diff against target: 212 lines (+132/-17)
3 files modified
src/maasserver/models/interface.py (+2/-0)
src/maasserver/models/staticipaddress.py (+30/-17)
src/maasserver/models/tests/test_staticipaddress.py (+100/-0)
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+349443@code.launchpad.net

Commit message

LP: #1776604 - Improve hostname mapping heuristic for bonded bridges

This commit changes the default hostname to IP address mapping to
account for situations where an interface parent's parent is a
machine's boot interface.

Backports: 72aba3a90faa053905c20967604195e80fee6b05

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

Self-approve backport.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/maasserver/models/interface.py b/src/maasserver/models/interface.py
index 4624e8a..5839e73 100644
--- a/src/maasserver/models/interface.py
+++ b/src/maasserver/models/interface.py
@@ -1128,6 +1128,8 @@ class Interface(CleanSave, TimestampedModel):
1128 """Remove all the `IPAddress` link on the interface."""1128 """Remove all the `IPAddress` link on the interface."""
1129 for ip_address in self.ip_addresses.exclude(1129 for ip_address in self.ip_addresses.exclude(
1130 alloc_type=IPADDRESS_TYPE.DISCOVERED):1130 alloc_type=IPADDRESS_TYPE.DISCOVERED):
1131 maaslog.info("%s: IP address automatically unlinked: %s" % (
1132 self.get_log_string(), ip_address))
1131 self.unlink_ip_address(ip_address, clearing_config=clearing_config)1133 self.unlink_ip_address(ip_address, clearing_config=clearing_config)
11321134
1133 def claim_auto_ips(self, exclude_addresses=[]):1135 def claim_auto_ips(self, exclude_addresses=[]):
diff --git a/src/maasserver/models/staticipaddress.py b/src/maasserver/models/staticipaddress.py
index bfc9d61..68be5c5 100644
--- a/src/maasserver/models/staticipaddress.py
+++ b/src/maasserver/models/staticipaddress.py
@@ -75,6 +75,8 @@ _special_mapping_result = _mapping_base_fields + (
7575
76_mapping_query_result = _mapping_base_fields + (76_mapping_query_result = _mapping_base_fields + (
77 'is_boot',77 'is_boot',
78 'preference',
79 'family',
78)80)
7981
80_interface_mapping_result = _mapping_base_fields + (82_interface_mapping_result = _mapping_base_fields + (
@@ -504,7 +506,7 @@ class StaticIPAddressManager(Manager):
504 domain.ttl,506 domain.ttl,
505 %s)""" % default_ttl507 %s)""" % default_ttl
506 sql_query = """508 sql_query = """
507 SELECT DISTINCT ON (node.hostname, is_boot, family(staticip.ip))509 SELECT DISTINCT ON (fqdn, is_boot, family)
508 CONCAT(node.hostname, '.', domain.name) AS fqdn,510 CONCAT(node.hostname, '.', domain.name) AS fqdn,
509 node.system_id,511 node.system_id,
510 node.node_type,512 node.node_type,
@@ -515,16 +517,38 @@ class StaticIPAddressManager(Manager):
515 node.boot_interface_id IS NOT NULL AND517 node.boot_interface_id IS NOT NULL AND
516 (518 (
517 node.boot_interface_id = interface.id OR519 node.boot_interface_id = interface.id OR
518 node.boot_interface_id = parent.id520 node.boot_interface_id = parent.id OR
521 node.boot_interface_id = parent_parent.id
519 ),522 ),
520 False523 False
521 ) AS is_boot524 ) AS is_boot,
525 CASE
526 WHEN interface.type = 'bridge' AND
527 parent_parent.id = node.boot_interface_id THEN 1
528 WHEN interface.type = 'bridge' AND
529 parent.id = node.boot_interface_id THEN 2
530 WHEN interface.type = 'bond' AND
531 parent.id = node.boot_interface_id THEN 3
532 WHEN interface.type = 'physical' AND
533 interface.id = node.boot_interface_id THEN 4
534 WHEN interface.type = 'bond' THEN 5
535 WHEN interface.type = 'physical' THEN 6
536 WHEN interface.type = 'vlan' THEN 7
537 WHEN interface.type = 'alias' THEN 8
538 WHEN interface.type = 'unknown' THEN 9
539 ELSE 10
540 END AS preference,
541 family(staticip.ip) AS family
522 FROM542 FROM
523 maasserver_interface AS interface543 maasserver_interface AS interface
524 LEFT OUTER JOIN maasserver_interfacerelationship AS rel ON544 LEFT OUTER JOIN maasserver_interfacerelationship AS rel ON
525 interface.id = rel.child_id545 interface.id = rel.child_id
526 LEFT OUTER JOIN maasserver_interface AS parent ON546 LEFT OUTER JOIN maasserver_interface AS parent ON
527 rel.parent_id = parent.id547 rel.parent_id = parent.id
548 LEFT OUTER JOIN maasserver_interfacerelationship AS parent_rel ON
549 parent.id = parent_rel.child_id
550 LEFT OUTER JOIN maasserver_interface AS parent_parent ON
551 parent_rel.parent_id = parent_parent.id
528 JOIN maasserver_node AS node ON552 JOIN maasserver_node AS node ON
529 node.id = interface.node_id553 node.id = interface.node_id
530 JOIN maasserver_domain AS domain ON554 JOIN maasserver_domain AS domain ON
@@ -562,21 +586,10 @@ class StaticIPAddressManager(Manager):
562 staticip.ip IS NOT NULL AND586 staticip.ip IS NOT NULL AND
563 host(staticip.ip) != ''587 host(staticip.ip) != ''
564 ORDER BY588 ORDER BY
565 node.hostname,589 fqdn,
566 is_boot DESC,590 is_boot DESC,
567 family(staticip.ip),591 family,
568 CASE592 preference,
569 WHEN interface.type = 'bond' AND
570 parent.id = node.boot_interface_id THEN 1
571 WHEN interface.type = 'physical' AND
572 interface.id = node.boot_interface_id THEN 2
573 WHEN interface.type = 'bond' THEN 3
574 WHEN interface.type = 'physical' THEN 4
575 WHEN interface.type = 'vlan' THEN 5
576 WHEN interface.type = 'alias' THEN 6
577 WHEN interface.type = 'unknown' THEN 7
578 ELSE 8
579 END,
580 /*593 /*
581 * We want STICKY and USER_RESERVED addresses to be preferred,594 * We want STICKY and USER_RESERVED addresses to be preferred,
582 * followed by AUTO, DHCP, and finally DISCOVERED.595 * followed by AUTO, DHCP, and finally DISCOVERED.
diff --git a/src/maasserver/models/tests/test_staticipaddress.py b/src/maasserver/models/tests/test_staticipaddress.py
index 7b72861..7e1f945 100644
--- a/src/maasserver/models/tests/test_staticipaddress.py
+++ b/src/maasserver/models/tests/test_staticipaddress.py
@@ -837,6 +837,106 @@ class TestStaticIPAddressManagerMapping(MAASServerTestCase):
837 node.system_id, 30, {vlanip.ip}, node.node_type)}837 node.system_id, 30, {vlanip.ip}, node.node_type)}
838 self.assertEqual(expected_mapping, mapping)838 self.assertEqual(expected_mapping, mapping)
839839
840 def test_get_hostname_ip_mapping_prefers_bridged_bond_pxe_interface(self):
841 subnet = factory.make_Subnet(
842 cidr='10.0.0.0/24', dns_servers=[], gateway_ip='10.0.0.1')
843 node = factory.make_Node_with_Interface_on_Subnet(
844 hostname='host', subnet=subnet)
845 eth0 = node.get_boot_interface()
846 eth0.name = 'eth0'
847 eth0.save()
848 eth1 = factory.make_Interface(
849 INTERFACE_TYPE.PHYSICAL, node=node, name='eth1', vlan=subnet.vlan)
850 eth2 = factory.make_Interface(
851 INTERFACE_TYPE.PHYSICAL, node=node, name='eth2', vlan=subnet.vlan)
852 node.boot_interface = eth1
853 node.save()
854 bond0 = factory.make_Interface(
855 INTERFACE_TYPE.BOND, node=node, parents=[eth1, eth2], name='bond0')
856 br_bond0 = factory.make_Interface(
857 INTERFACE_TYPE.BRIDGE, parents=[bond0], name='br-bond0')
858 phy_staticip = factory.make_StaticIPAddress(
859 alloc_type=IPADDRESS_TYPE.STICKY, interface=eth0,
860 subnet=subnet, ip='10.0.0.2')
861 bridge_ip = factory.make_StaticIPAddress(
862 alloc_type=IPADDRESS_TYPE.STICKY, interface=br_bond0,
863 subnet=subnet, ip='10.0.0.3')
864 # Make an IP addresses that shouldn't be preferred, but is also on
865 # an interface whose parent is a boot interface. (Some caution is
866 # advised when doing this, since we have interface signals that will
867 # automatically remove these addresses in some cases.)
868 bond0_extra_ip = factory.make_StaticIPAddress(
869 alloc_type=IPADDRESS_TYPE.STICKY, interface=bond0,
870 subnet=subnet, ip='10.0.0.4')
871 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
872 node.domain)
873 expected_mapping = {
874 node.fqdn: HostnameIPMapping(
875 node.system_id, 30, {bridge_ip.ip}, node.node_type),
876 "%s.%s" % (eth0.name, node.fqdn): HostnameIPMapping(
877 node.system_id, 30, {phy_staticip.ip}, node.node_type),
878 "%s.%s" % (bond0.name, node.fqdn): HostnameIPMapping(
879 node.system_id, 30, {bond0_extra_ip.ip}, node.node_type)
880 }
881 self.assertThat(mapping, Equals(expected_mapping))
882
883 def test_get_hostname_ip_mapping_with_v4_and_v6_and_bridged_bonds(self):
884 subnet_v4 = factory.make_Subnet(
885 cidr=str(factory.make_ipv4_network().cidr))
886 subnet_v6 = factory.make_Subnet(
887 cidr='2001:db8::/64')
888 node = factory.make_Node_with_Interface_on_Subnet(
889 hostname='host', subnet=subnet_v4)
890 eth0 = node.get_boot_interface()
891 eth0.name = 'eth0'
892 eth0.save()
893 eth1 = factory.make_Interface(
894 INTERFACE_TYPE.PHYSICAL, node=node, name='eth1')
895 eth2 = factory.make_Interface(
896 INTERFACE_TYPE.PHYSICAL, node=node, name='eth2')
897 node.boot_interface = eth1
898 node.save()
899 bond0 = factory.make_Interface(
900 INTERFACE_TYPE.BOND, node=node, parents=[eth1, eth2], name='bond0')
901 br_bond0 = factory.make_Interface(
902 INTERFACE_TYPE.BRIDGE, parents=[bond0], name='br-bond0')
903 phy_staticip_v4 = factory.make_StaticIPAddress(
904 alloc_type=IPADDRESS_TYPE.STICKY, interface=eth0,
905 subnet=subnet_v4)
906 bridge_ip_v4 = factory.make_StaticIPAddress(
907 alloc_type=IPADDRESS_TYPE.STICKY, interface=br_bond0,
908 subnet=subnet_v4)
909 phy_staticip_v6 = factory.make_StaticIPAddress(
910 alloc_type=IPADDRESS_TYPE.STICKY, interface=eth0,
911 subnet=subnet_v6)
912 bridge_ip_v6 = factory.make_StaticIPAddress(
913 alloc_type=IPADDRESS_TYPE.STICKY, interface=br_bond0,
914 subnet=subnet_v6)
915 # Make a few IP addresses that shouldn't be associated with the
916 # default hosname. (They'll show up as associated with bond0, however.)
917 # Technically MAAS should remove these since they belong to a parent
918 # interface, but in this case the interface won't be re-saved.
919 extra_ipv4 = factory.make_StaticIPAddress(
920 alloc_type=IPADDRESS_TYPE.STICKY, interface=bond0,
921 subnet=subnet_v4)
922 extra_ipv6 = factory.make_StaticIPAddress(
923 alloc_type=IPADDRESS_TYPE.STICKY, interface=bond0,
924 subnet=subnet_v6)
925 mapping = StaticIPAddress.objects.get_hostname_ip_mapping(
926 node.domain)
927 expected_mapping = {
928 node.fqdn: HostnameIPMapping(
929 node.system_id, 30, {bridge_ip_v4.ip, bridge_ip_v6.ip},
930 node.node_type),
931 "%s.%s" % (eth0.name, node.fqdn): HostnameIPMapping(
932 node.system_id, 30, {phy_staticip_v4.ip, phy_staticip_v6.ip},
933 node.node_type),
934 "%s.%s" % (bond0.name, node.fqdn): HostnameIPMapping(
935 node.system_id, 30, {extra_ipv4.ip, extra_ipv6.ip},
936 node.node_type)
937 }
938 self.assertThat(mapping, Equals(expected_mapping))
939
840 def test_get_hostname_ip_mapping_returns_domain_head_ips(self):940 def test_get_hostname_ip_mapping_returns_domain_head_ips(self):
841 parent = factory.make_Domain()941 parent = factory.make_Domain()
842 name = factory.make_name()942 name = factory.make_name()

Subscribers

People subscribed via source and target branches