Merge ~mpontillo/maas:preseed-per-interface-routes--bug-1758919--2.3 into maas:2.3

Proposed by Mike Pontillo
Status: Merged
Approved by: Mike Pontillo
Approved revision: 434ff94a620afd73b314baa08e7e95fa36ec41ca
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~mpontillo/maas:preseed-per-interface-routes--bug-1758919--2.3
Merge into: maas:2.3
Diff against target: 381 lines (+233/-61)
2 files modified
src/maasserver/preseed_network.py (+25/-16)
src/maasserver/tests/test_preseed_network.py (+208/-45)
Reviewer Review Type Date Requested Status
Mike Pontillo (community) Approve
Review via email: mp+342243@code.launchpad.net

Commit message

LP: #1758919 - Move routes to interface context within preseed.

Backports: 9fbf7eb682c3c3ff1de08b33bb967fffdf4a61bf

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/preseed_network.py b/src/maasserver/preseed_network.py
index 1be72d6..bb56765 100644
--- a/src/maasserver/preseed_network.py
+++ b/src/maasserver/preseed_network.py
@@ -52,8 +52,6 @@ def _generate_route_operation(route, version=1):
52 """Generate route operation place in `network_config`."""52 """Generate route operation place in `network_config`."""
53 if version == 1:53 if version == 1:
54 route_operation = {54 route_operation = {
55 "id": route.id,
56 "type": "route",
57 "destination": route.destination.cidr,55 "destination": route.destination.cidr,
58 "gateway": route.gateway_ip,56 "gateway": route.gateway_ip,
59 "metric": route.metric,57 "metric": route.metric,
@@ -99,6 +97,19 @@ class InterfaceConfiguration:
99 else:97 else:
100 raise ValueError("Unknown interface type: %s" % self.type)98 raise ValueError("Unknown interface type: %s" % self.type)
10199
100 if version == 2:
101 routes = self._generate_route_operations(
102 self.matching_routes, version=version)
103 if len(routes) > 0:
104 self.config['routes'] = routes
105
106 def _generate_route_operations(self, matching_routes, version=1):
107 """Generate all route operations."""
108 routes = []
109 for route in sorted(matching_routes, key=attrgetter("id")):
110 routes.append(_generate_route_operation(route, version=version))
111 return routes
112
102 def _generate_physical_operation(self, version=1):113 def _generate_physical_operation(self, version=1):
103 """Generate physical interface operation for `interface` and place in114 """Generate physical interface operation for `interface` and place in
104 `network_config`."""115 `network_config`."""
@@ -241,7 +252,8 @@ class InterfaceConfiguration:
241 self._set_default_gateway(252 self._set_default_gateway(
242 subnet,253 subnet,
243 v1_subnet_operation if version == 1 else v2_config)254 v1_subnet_operation if version == 1 else v2_config)
244 if subnet.dns_servers is not None:255 if (subnet.dns_servers is not None and
256 len(subnet.dns_servers) > 0):
245 v1_subnet_operation["dns_nameservers"] = (257 v1_subnet_operation["dns_nameservers"] = (
246 subnet.dns_servers)258 subnet.dns_servers)
247 if "nameservers" not in v2_config:259 if "nameservers" not in v2_config:
@@ -251,8 +263,16 @@ class InterfaceConfiguration:
251 v2_nameservers["addresses"] = []263 v2_nameservers["addresses"] = []
252 v2_nameservers["addresses"].extend(264 v2_nameservers["addresses"].extend(
253 [server for server in subnet.dns_servers])265 [server for server in subnet.dns_servers])
254 self.matching_routes.update(266 matching_subnet_routes = self._get_matching_routes(subnet)
255 self._get_matching_routes(subnet))267 if len(matching_subnet_routes) > 0 and version == 1:
268 # For the v1 YAML, the list of routes is rendered
269 # within the context of each subnet.
270 routes = self._generate_route_operations(
271 matching_subnet_routes, version=version)
272 v1_subnet_operation['routes'] = routes
273 # Keep track of routes which apply to the context of this
274 # interface for rendering the v2 YAML.
275 self.matching_routes.update(matching_subnet_routes)
256 if dhcp_type:276 if dhcp_type:
257 v1_config.append(277 v1_config.append(
258 {"type": dhcp_type}278 {"type": dhcp_type}
@@ -479,7 +499,6 @@ class NodeNetworkConfiguration:
479 for name in sorted(get_dns_search_paths())499 for name in sorted(get_dns_search_paths())
480 if name != self.node.domain.name500 if name != self.node.domain.name
481 ]501 ]
482 self._generate_route_operations(version=version)
483 self.v1_config.append({502 self.v1_config.append({
484 "type": "nameserver",503 "type": "nameserver",
485 "address": default_dns_servers,504 "address": default_dns_servers,
@@ -517,16 +536,6 @@ class NodeNetworkConfiguration:
517 # v2_config.update({"nameservers": nameservers})536 # v2_config.update({"nameservers": nameservers})
518 self.config = network_config537 self.config = network_config
519538
520 def _generate_route_operations(self, version=1):
521 """Generate all route operations."""
522 routes = []
523 for route in sorted(self.matching_routes, key=attrgetter("id")):
524 routes.append(_generate_route_operation(route, version=version))
525 if version == 1:
526 self.v1_config.extend(routes)
527 elif version == 2 and len(routes) > 0:
528 self.v2_config["routes"] = routes
529
530539
531def compose_curtin_network_config(node, version=1):540def compose_curtin_network_config(node, version=1):
532 """Compose the network configuration for curtin."""541 """Compose the network configuration for curtin."""
diff --git a/src/maasserver/tests/test_preseed_network.py b/src/maasserver/tests/test_preseed_network.py
index 8a509dc..cc66f9f 100644
--- a/src/maasserver/tests/test_preseed_network.py
+++ b/src/maasserver/tests/test_preseed_network.py
@@ -5,7 +5,6 @@
55
6__all__ = []6__all__ = []
77
8from operator import attrgetter
9import random8import random
10from textwrap import dedent9from textwrap import dedent
1110
@@ -15,7 +14,6 @@ from maasserver.enum import (
15 IPADDRESS_FAMILY,14 IPADDRESS_FAMILY,
16 IPADDRESS_TYPE,15 IPADDRESS_TYPE,
17)16)
18from maasserver.models.staticroute import StaticRoute
19from maasserver.preseed_network import (17from maasserver.preseed_network import (
20 compose_curtin_network_config,18 compose_curtin_network_config,
21 NodeNetworkConfiguration,19 NodeNetworkConfiguration,
@@ -228,31 +226,6 @@ class AssertNetworkConfigMixin:
228 ret += " - type: dhcp6\n"226 ret += " - type: dhcp6\n"
229 return ret227 return ret
230228
231 def collectRoutesConfig(self, node):
232 routes = set()
233 interfaces = node.interface_set.filter(enabled=True).order_by('name')
234 for iface in interfaces:
235 addresses = iface.ip_addresses.exclude(
236 alloc_type__in=[
237 IPADDRESS_TYPE.DISCOVERED,
238 IPADDRESS_TYPE.DHCP,
239 ]).order_by('id')
240 for address in addresses:
241 subnet = address.subnet
242 if subnet is not None:
243 routes.update(
244 StaticRoute.objects.filter(
245 source=subnet).order_by('id'))
246 config = ""
247 for route in sorted(routes, key=attrgetter('id')):
248 config += self.ROUTE_CONFIG % {
249 'id': route.id,
250 'destination': route.destination.cidr,
251 'gateway': route.gateway_ip,
252 'metric': route.metric,
253 }
254 return config
255
256 def collectDNSConfig(self, node, ipv4=True, ipv6=True):229 def collectDNSConfig(self, node, ipv4=True, ipv6=True):
257 config = "- type: nameserver\n address: %s\n search:\n" % (230 config = "- type: nameserver\n address: %s\n search:\n" % (
258 repr(node.get_default_dns_servers(ipv4=ipv4, ipv6=ipv6)))231 repr(node.get_default_dns_servers(ipv4=ipv4, ipv6=ipv6)))
@@ -466,29 +439,14 @@ class TestBridgeNetworkLayout(MAASServerTestCase, AssertNetworkConfigMixin):
466 self.assertNetworkConfig(net_config, config)439 self.assertNetworkConfig(net_config, config)
467440
468441
469class TestNetworkLayoutWithRoutes(
470 MAASServerTestCase, AssertNetworkConfigMixin):
471
472 def test__renders_expected_output(self):
473 node = factory.make_Node_with_Interface_on_Subnet(
474 interface_count=2)
475 for iface in node.interface_set.filter(enabled=True):
476 subnet = iface.vlan.subnet_set.first()
477 factory.make_StaticRoute(source=subnet)
478 factory.make_StaticIPAddress(
479 interface=iface, subnet=subnet)
480 net_config = self.collect_interface_config(node)
481 net_config += self.collectRoutesConfig(node)
482 net_config += self.collectDNSConfig(node)
483 config = compose_curtin_network_config(node)
484 self.assertNetworkConfig(net_config, config)
485
486
487class TestNetplan(MAASServerTestCase):442class TestNetplan(MAASServerTestCase):
488443
489 def _render_netplan_dict(self, node):444 def _render_netplan_dict(self, node):
490 return NodeNetworkConfiguration(node, version=2).config445 return NodeNetworkConfiguration(node, version=2).config
491446
447 def _render_v1_dict(self, node):
448 return NodeNetworkConfiguration(node, version=1).config
449
492 def test__single_ethernet_interface(self):450 def test__single_ethernet_interface(self):
493 node = factory.make_Node()451 node = factory.make_Node()
494 factory.make_Interface(452 factory.make_Interface(
@@ -824,3 +782,208 @@ class TestNetplan(MAASServerTestCase):
824 }782 }
825 }783 }
826 self.expectThat(netplan, Equals(expected_netplan))784 self.expectThat(netplan, Equals(expected_netplan))
785
786 def test__multiple_ethernet_interfaces_with_routes(self):
787 node = factory.make_Node()
788 vlan = factory.make_VLAN()
789 subnet = factory.make_Subnet(
790 cidr='10.0.0.0/24', gateway_ip='10.0.0.1',
791 dns_servers=[])
792 subnet2 = factory.make_Subnet(
793 cidr='10.0.1.0/24', gateway_ip='10.0.1.1',
794 dns_servers=[])
795 dest_subnet = factory.make_Subnet(cidr='192.168.0.0/24')
796 eth0 = factory.make_Interface(
797 node=node, name='eth0', mac_address="00:01:02:03:04:05", vlan=vlan)
798 eth1 = factory.make_Interface(
799 node=node, name='eth1', mac_address="02:01:02:03:04:05")
800 node.boot_interface = eth0
801 node.save()
802 factory.make_StaticIPAddress(
803 interface=eth0, subnet=subnet, ip='10.0.0.4',
804 alloc_type=IPADDRESS_TYPE.STICKY)
805 factory.make_StaticIPAddress(
806 interface=eth1, subnet=subnet2, ip='10.0.1.4',
807 alloc_type=IPADDRESS_TYPE.STICKY)
808 factory.make_StaticRoute(
809 source=subnet, gateway_ip='10.0.0.3', destination=dest_subnet,
810 metric=42)
811 factory.make_StaticRoute(
812 source=subnet2, gateway_ip='10.0.1.3', destination=dest_subnet,
813 metric=43)
814 # Make sure we know when and where the default DNS server will be used.
815 get_default_dns_servers_mock = self.patch(
816 node, 'get_default_dns_servers')
817 get_default_dns_servers_mock.return_value = ['127.0.0.2']
818 netplan = self._render_netplan_dict(node)
819 expected_netplan = {
820 'network': {
821 'version': 2,
822 'ethernets': {
823 'eth0': {
824 'gateway': '10.0.0.1',
825 'match': {'macaddress': '00:01:02:03:04:05'},
826 'mtu': 1500,
827 'set-name': 'eth0',
828 'addresses': ['10.0.0.4/24'],
829 'routes': [{
830 'to': '192.168.0.0/24',
831 'via': '10.0.0.3',
832 'metric': 42,
833 }],
834 },
835 'eth1': {
836 'match': {'macaddress': '02:01:02:03:04:05'},
837 'mtu': 1500,
838 'set-name': 'eth1',
839 'addresses': ['10.0.1.4/24'],
840 'routes': [{
841 'to': '192.168.0.0/24',
842 'via': '10.0.1.3',
843 'metric': 43,
844 }],
845 },
846 },
847 },
848 }
849 self.expectThat(netplan, Equals(expected_netplan))
850 v1 = self._render_v1_dict(node)
851 expected_v1 = {
852 'network': {
853 'version': 1,
854 'config': [
855 {
856 'id': 'eth0',
857 'mac_address': '00:01:02:03:04:05',
858 'mtu': 1500,
859 'name': 'eth0',
860 'subnets': [{
861 'address': '10.0.0.4/24',
862 'gateway': '10.0.0.1',
863 'type': 'static',
864 'routes': [{
865 'destination': '192.168.0.0/24',
866 'gateway': '10.0.0.3',
867 'metric': 42
868 }],
869 }],
870 'type': 'physical'
871 },
872 {
873 'id': 'eth1',
874 'mac_address': '02:01:02:03:04:05',
875 'mtu': 1500,
876 'name': 'eth1',
877 'subnets': [{
878 'address': '10.0.1.4/24',
879 'type': 'static',
880 'routes': [{
881 'destination': '192.168.0.0/24',
882 'gateway': '10.0.1.3',
883 'metric': 43,
884 }],
885 }],
886 'type': 'physical'
887 },
888 {
889 'address': ['127.0.0.2'],
890 'search': ['maas'],
891 'type': 'nameserver'
892 }
893 ],
894 }
895 }
896 self.expectThat(v1, Equals(expected_v1))
897
898 def test__multiple_ethernet_interfaces_with_dns(self):
899 node = factory.make_Node()
900 vlan = factory.make_VLAN()
901 subnet = factory.make_Subnet(
902 cidr='10.0.0.0/24', gateway_ip='10.0.0.1',
903 dns_servers=['10.0.0.2'])
904 subnet2 = factory.make_Subnet(
905 cidr='10.0.1.0/24', gateway_ip='10.0.1.1',
906 dns_servers=['10.0.1.2'])
907 eth0 = factory.make_Interface(
908 node=node, name='eth0', mac_address="00:01:02:03:04:05", vlan=vlan)
909 eth1 = factory.make_Interface(
910 node=node, name='eth1', mac_address="02:01:02:03:04:05")
911 node.boot_interface = eth0
912 node.save()
913 factory.make_StaticIPAddress(
914 interface=eth0, subnet=subnet, ip='10.0.0.4',
915 alloc_type=IPADDRESS_TYPE.STICKY)
916 factory.make_StaticIPAddress(
917 interface=eth1, subnet=subnet2, ip='10.0.1.4',
918 alloc_type=IPADDRESS_TYPE.STICKY)
919 # Make sure we know when and where the default DNS server will be used.
920 get_default_dns_servers_mock = self.patch(
921 node, 'get_default_dns_servers')
922 get_default_dns_servers_mock.return_value = ['127.0.0.2']
923 netplan = self._render_netplan_dict(node)
924 expected_netplan = {
925 'network': {
926 'version': 2,
927 'ethernets': {
928 'eth0': {
929 'gateway': '10.0.0.1',
930 'nameservers': {
931 'addresses': ['10.0.0.2']
932 },
933 'match': {'macaddress': '00:01:02:03:04:05'},
934 'mtu': 1500,
935 'set-name': 'eth0',
936 'addresses': ['10.0.0.4/24'],
937 },
938 'eth1': {
939 'match': {'macaddress': '02:01:02:03:04:05'},
940 'nameservers': {
941 'addresses': ['10.0.1.2']
942 },
943 'mtu': 1500,
944 'set-name': 'eth1',
945 'addresses': ['10.0.1.4/24'],
946 },
947 },
948 },
949 }
950 self.expectThat(netplan, Equals(expected_netplan))
951 v1 = self._render_v1_dict(node)
952 expected_v1 = {
953 'network': {
954 'version': 1,
955 'config': [
956 {
957 'id': 'eth0',
958 'mac_address': '00:01:02:03:04:05',
959 'mtu': 1500,
960 'name': 'eth0',
961 'subnets': [{
962 'address': '10.0.0.4/24',
963 'dns_nameservers': ['10.0.0.2'],
964 'gateway': '10.0.0.1',
965 'type': 'static',
966 }],
967 'type': 'physical'
968 },
969 {
970 'id': 'eth1',
971 'mac_address': '02:01:02:03:04:05',
972 'mtu': 1500,
973 'name': 'eth1',
974 'subnets': [{
975 'address': '10.0.1.4/24',
976 'dns_nameservers': ['10.0.1.2'],
977 'type': 'static',
978 }],
979 'type': 'physical'
980 },
981 {
982 'address': ['127.0.0.2'],
983 'search': ['maas'],
984 'type': 'nameserver'
985 }
986 ],
987 }
988 }
989 self.expectThat(v1, Equals(expected_v1))

Subscribers

People subscribed via source and target branches