Merge ~ack/maas:1930227-full-netmask-fix-3.0 into maas:3.0

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: e6940524bb31754a370cb2f9e4ae27fdfc209cc6
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:1930227-full-netmask-fix-3.0
Merge into: maas:3.0
Diff against target: 221 lines (+105/-31)
4 files modified
src/metadataserver/builtin_scripts/network.py (+11/-6)
src/metadataserver/builtin_scripts/tests/test_network.py (+26/-0)
src/provisioningserver/utils/network.py (+28/-21)
src/provisioningserver/utils/tests/test_network.py (+40/-4)
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
Review via email: mp+403524@code.launchpad.net

Commit message

LP:1930227 - fix netmask for /32 and /128 to match actual subnet

To post a comment you must log in.
Revision history for this message
Alberto Donato (ack) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/src/metadataserver/builtin_scripts/network.py b/src/metadataserver/builtin_scripts/network.py
index 44d8691..ed1e0f1 100644
--- a/src/metadataserver/builtin_scripts/network.py
+++ b/src/metadataserver/builtin_scripts/network.py
@@ -16,6 +16,7 @@ from maasserver.models.vlan import VLAN
16from maasserver.utils.orm import transactional16from maasserver.utils.orm import transactional
17from provisioningserver.logger import get_maas_logger17from provisioningserver.logger import get_maas_logger
18from provisioningserver.utils import flatten, sorttop18from provisioningserver.utils import flatten, sorttop
19from provisioningserver.utils.network import fix_link_addresses
19from provisioningserver.utils.twisted import synchronous20from provisioningserver.utils.twisted import synchronous
2021
21maaslog = get_maas_logger("metadataserver.network")22maaslog = get_maas_logger("metadataserver.network")
@@ -160,13 +161,17 @@ def update_interface(node, name, data, address_extra, hints=None):
160 # same, hard-coded OpenBMC MAC address.161 # same, hard-coded OpenBMC MAC address.
161 return None162 return None
162163
163 links = [164 links = []
164 address.copy()165 for address in network["addresses"]:
165 for address in network["addresses"]166 if address["scope"] != "global":
166 if address["scope"] == "global"167 continue
167 ]168 link = address.copy()
168 for link in links:
169 link.update(address_extra.get(link["address"], {}))169 link.update(address_extra.get(link["address"], {}))
170 links.append(link)
171 # fix networks that have a /32 or /128 netmask to the closest wider known
172 # subnet
173 fix_link_addresses(links)
174
170 if network["vlan"]:175 if network["vlan"]:
171 return update_vlan_interface(node, name, network, links)176 return update_vlan_interface(node, name, network, links)
172 elif network["bond"] or network["bridge"]:177 elif network["bond"] or network["bridge"]:
diff --git a/src/metadataserver/builtin_scripts/tests/test_network.py b/src/metadataserver/builtin_scripts/tests/test_network.py
index c8667d5..1162ba4 100644
--- a/src/metadataserver/builtin_scripts/tests/test_network.py
+++ b/src/metadataserver/builtin_scripts/tests/test_network.py
@@ -2419,6 +2419,32 @@ class TestUpdateInterfaces(MAASServerTestCase, UpdateInterfacesMixin):
2419 self.assertTrue(eth0.enabled)2419 self.assertTrue(eth0.enabled)
2420 self.assertEqual(data.networks["eth0"].hwaddr, eth0.mac_address)2420 self.assertEqual(data.networks["eth0"].hwaddr, eth0.mac_address)
24212421
2422 def test_fixes_link_netmasks(self):
2423 controller = self.create_empty_controller(with_empty_script_sets=True)
2424 data = FakeCommissioningData()
2425 card = data.create_network_card()
2426 card.vendor = factory.make_name("vendor")
2427 card.product = factory.make_name("product")
2428 card.firmware_version = factory.make_name("firmware_version")
2429 network = data.create_physical_network("eth0", card=card)
2430 network.addresses = [
2431 LXDAddress("192.168.0.1", 24),
2432 LXDAddress("192.168.0.2", 32),
2433 LXDAddress("2001::aaaa:1", 112),
2434 LXDAddress("2001::aaaa:2", 128),
2435 ]
2436 self.update_interfaces(controller, data)
2437 eth0 = Interface.objects.get(name="eth0", node=controller)
2438 self.assertItemsEqual(
2439 eth0.ip_addresses.values_list("ip", "subnet__cidr"),
2440 [
2441 ("192.168.0.1", "192.168.0.0/24"),
2442 ("192.168.0.2", "192.168.0.0/24"),
2443 ("2001::aaaa:1", "2001::aaaa:0/112"),
2444 ("2001::aaaa:2", "2001::aaaa:0/112"),
2445 ],
2446 )
2447
24222448
2423class TestUpdateInterfacesWithHints(2449class TestUpdateInterfacesWithHints(
2424 MAASTransactionServerTestCase, UpdateInterfacesMixin2450 MAASTransactionServerTestCase, UpdateInterfacesMixin
diff --git a/src/provisioningserver/utils/network.py b/src/provisioningserver/utils/network.py
index 794521b..d8c781e 100644
--- a/src/provisioningserver/utils/network.py
+++ b/src/provisioningserver/utils/network.py
@@ -939,6 +939,11 @@ def get_mac_organization(mac):
939def fix_link_addresses(links):939def fix_link_addresses(links):
940 """Fix the addresses defined in `links`.940 """Fix the addresses defined in `links`.
941941
942 Each link entry can contain addresses in the form:
943 {"address": "1.2.3.4/24", ...}
944 or
945 {"address": "1.2.3.4", "netmask": 24, ...}
946
942 Some address will have a prefixlen of 32 or 128 depending if IPv4 or IPv6.947 Some address will have a prefixlen of 32 or 128 depending if IPv4 or IPv6.
943 Fix those address to fall within a subnet that is already defined in948 Fix those address to fall within a subnet that is already defined in
944 another link. The addresses that get fixed will be placed into the smallest949 another link. The addresses that get fixed will be placed into the smallest
@@ -952,7 +957,10 @@ def fix_link_addresses(links):
952 # Loop through and build a list of subnets where the prefixlen is not957 # Loop through and build a list of subnets where the prefixlen is not
953 # 32 or 128 for IPv4 and IPv6 respectively.958 # 32 or 128 for IPv4 and IPv6 respectively.
954 for link in links:959 for link in links:
955 ip_addr = IPNetwork(link["address"])960 if "netmask" in link:
961 ip_addr = IPNetwork(f"{link['address']}/{link['netmask']}")
962 else:
963 ip_addr = IPNetwork(link["address"])
956 if ip_addr.version == 4:964 if ip_addr.version == 4:
957 if ip_addr.prefixlen == 32:965 if ip_addr.prefixlen == 32:
958 links_v4.append(link)966 links_v4.append(link)
@@ -964,26 +972,25 @@ def fix_link_addresses(links):
964 else:972 else:
965 subnets_v6.append(ip_addr.cidr)973 subnets_v6.append(ip_addr.cidr)
966974
967 # Sort the subnets so the smallest prefixlen is first.975 for links, subnets in ((links_v4, subnets_v4), (links_v6, subnets_v6)):
968 subnets_v4 = sorted(subnets_v4, key=attrgetter("prefixlen"), reverse=True)976 subnets = sorted(subnets, key=attrgetter("prefixlen"), reverse=True)
969 subnets_v6 = sorted(subnets_v6, key=attrgetter("prefixlen"), reverse=True)977 # Fix all addresses that have prefixlen of 32 or 128 that fit in inside
970978 # one of the already defined subnets.
971 # Fix all addresses that have prefixlen of 32 or 128 that fit in inside979 for link in links:
972 # one of the already defined subnets.980 has_separate_netmask = "netmask" in link
973 for link in links_v4:981 if has_separate_netmask:
974 ip_addr = IPNetwork(link["address"])982 ip_addr = IPNetwork(f"{link['address']}/{link['netmask']}")
975 for subnet in subnets_v4:983 else:
976 if ip_addr.ip in subnet:984 ip_addr = IPNetwork(link["address"])
977 ip_addr.prefixlen = subnet.prefixlen985
978 link["address"] = str(ip_addr)986 for subnet in subnets:
979 break987 if ip_addr.ip in subnet:
980 for link in links_v6:988 if has_separate_netmask:
981 ip_addr = IPNetwork(link["address"])989 link["netmask"] = subnet.prefixlen
982 for subnet in subnets_v6:990 else:
983 if ip_addr.ip in subnet:991 ip_addr.prefixlen = subnet.prefixlen
984 ip_addr.prefixlen = subnet.prefixlen992 link["address"] = str(ip_addr)
985 link["address"] = str(ip_addr)993 break
986 break
987994
988995
989def fix_link_gateways(links, iproute_info):996def fix_link_gateways(links, iproute_info):
diff --git a/src/provisioningserver/utils/tests/test_network.py b/src/provisioningserver/utils/tests/test_network.py
index 6a0d438..3b96869 100644
--- a/src/provisioningserver/utils/tests/test_network.py
+++ b/src/provisioningserver/utils/tests/test_network.py
@@ -1,15 +1,12 @@
1# Copyright 2014-2016 Canonical Ltd. This software is licensed under the1# Copyright 2014-2016 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Tests for network helpers."""
5
6
7import itertools4import itertools
8import json5import json
9import socket6import socket
10from socket import EAI_BADFLAGS, EAI_NODATA, EAI_NONAME, gaierror, IPPROTO_TCP7from socket import EAI_BADFLAGS, EAI_NODATA, EAI_NONAME, gaierror, IPPROTO_TCP
11from typing import List8from typing import List
12from unittest import mock9from unittest import mock, TestCase
13from unittest.mock import Mock10from unittest.mock import Mock
1411
15from netaddr import EUI, IPAddress, IPNetwork, IPRange12from netaddr import EUI, IPAddress, IPNetwork, IPRange
@@ -57,6 +54,7 @@ from provisioningserver.utils.network import (
57 enumerate_ipv4_addresses,54 enumerate_ipv4_addresses,
58 find_ip_via_arp,55 find_ip_via_arp,
59 find_mac_via_arp,56 find_mac_via_arp,
57 fix_link_addresses,
60 format_eui,58 format_eui,
61 generate_mac_address,59 generate_mac_address,
62 get_all_addresses_for_interface,60 get_all_addresses_for_interface,
@@ -94,6 +92,44 @@ from provisioningserver.utils.network import (
94from provisioningserver.utils.shell import get_env_with_locale92from provisioningserver.utils.shell import get_env_with_locale
9593
9694
95class TestFixLinkAddresses(TestCase):
96 def test_fixes_with_cidr(self):
97 links = [
98 {"address": "1.2.3.1/24", "mode": "static"},
99 {"address": "1.2.3.4/32", "mode": "static"},
100 {"address": "2001::1/96", "mode": "static"},
101 {"address": "2001::123/128", "mode": "static"},
102 ]
103 fix_link_addresses(links)
104 self.assertEqual(
105 links,
106 [
107 {"address": "1.2.3.1/24", "mode": "static"},
108 {"address": "1.2.3.4/24", "mode": "static"},
109 {"address": "2001::1/96", "mode": "static"},
110 {"address": "2001::123/96", "mode": "static"},
111 ],
112 )
113
114 def test_fixes_with_netmask(self):
115 links = [
116 {"address": "1.2.3.1", "netmask": 24, "mode": "static"},
117 {"address": "1.2.3.4", "netmask": 32, "mode": "static"},
118 {"address": "2001::1", "netmask": 96, "mode": "static"},
119 {"address": "2001::123", "netmask": 128, "mode": "static"},
120 ]
121 fix_link_addresses(links)
122 self.assertEqual(
123 links,
124 [
125 {"address": "1.2.3.1", "netmask": 24, "mode": "static"},
126 {"address": "1.2.3.4", "netmask": 24, "mode": "static"},
127 {"address": "2001::1", "netmask": 96, "mode": "static"},
128 {"address": "2001::123", "netmask": 96, "mode": "static"},
129 ],
130 )
131
132
97class TestMakeNetwork(MAASTestCase):133class TestMakeNetwork(MAASTestCase):
98 def test_constructs_IPNetwork(self):134 def test_constructs_IPNetwork(self):
99 network = make_network("10.22.82.0", 24)135 network = make_network("10.22.82.0", 24)

Subscribers

People subscribed via source and target branches