Skip to content

Commit 133af42

Browse files
committed
Implement get_subnets_address_scopes method
This patch implement a method to retrieve the IPv4 and IPv6 address scopes of several subnets, providing a list of fixed IP addresses that belong to a port. The goal of this patch is to decouple the address scope retrieval from the logic that determines if an OVN LSP is virtual or not. Related-Bug: #2018529 Conflicts: neutron/common/ovn/utils.py Change-Id: Id4ab755293627119455cbcd15602690c8ce7a3c1 (cherry picked from commit a22b1de)
1 parent 91e1086 commit 133af42

File tree

3 files changed

+105
-20
lines changed

3 files changed

+105
-20
lines changed

neutron/common/ovn/utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -853,3 +853,45 @@ def get_ovn_chassis_other_config(chassis):
853853
return chassis.other_config
854854
except AttributeError:
855855
return chassis.external_ids
856+
857+
858+
def get_subnets_address_scopes(context, subnets, fixed_ips, ml2_plugin):
859+
"""Returns the IPv4 and IPv6 address scopes of several subnets.
860+
861+
The subnets hosted on the same network must be allocated from the same
862+
subnet pool (from ``NetworkSubnetPoolAffinityError`` exception). That
863+
applies per IP version (it means it is possible to have two subnet pools,
864+
one for IPv4 and one for IPv6).
865+
866+
:param context: neutron api request context
867+
:param subnets: (list of dict) subnet dictionaries
868+
:param fixed_ips: (list of dict) fixed IPs of several subnets (usually
869+
belonging to a network but not mandatory)
870+
:param ml2_plugin: (``Ml2Plugin``) ML2 plugin instance
871+
:return: (tuple of 2 strings) IPv4 and IPv6 address scope IDs
872+
"""
873+
address4_scope_id, address6_scope_id = '', ''
874+
if not subnets:
875+
return address4_scope_id, address6_scope_id
876+
877+
subnets_by_id = {subnet['id']: subnet for subnet in subnets}
878+
for fixed_ip in fixed_ips:
879+
subnet_id = fixed_ip.get('subnet_id')
880+
subnet = subnets_by_id.get(subnet_id)
881+
if not subnet or not subnet['subnetpool_id']:
882+
continue
883+
884+
try:
885+
subnet_pool = ml2_plugin.get_subnetpool(context,
886+
id=subnet['subnetpool_id'])
887+
if subnet_pool['address_scope_id']:
888+
if subnet_pool['ip_version'] == const.IP_VERSION_4:
889+
address4_scope_id = subnet_pool['address_scope_id']
890+
else:
891+
address6_scope_id = subnet_pool['address_scope_id']
892+
except n_exc.SubnetPoolNotFound:
893+
# swallow the exception and just continue if the
894+
# lookup failed
895+
pass
896+
897+
return address4_scope_id, address6_scope_id

neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,9 @@ def _get_port_options(self, port):
338338
]
339339
subnets = self._plugin.get_subnets(
340340
context, filters={'id': subnet_ids})
341+
address4_scope_id, address6_scope_id = (
342+
utils.get_subnets_address_scopes(context, subnets, ip_subnets,
343+
self._plugin))
341344
if subnets:
342345
for ip in ip_subnets:
343346
ip_addr = ip['ip_address']
@@ -355,26 +358,6 @@ def _get_port_options(self, port):
355358
ip_addr)
356359
continue
357360

358-
if subnet["subnetpool_id"]:
359-
try:
360-
subnet_pool = self._plugin.get_subnetpool(
361-
context, id=subnet["subnetpool_id"]
362-
)
363-
if subnet_pool["address_scope_id"]:
364-
ip_version = subnet_pool["ip_version"]
365-
if ip_version == const.IP_VERSION_4:
366-
address4_scope_id = subnet_pool[
367-
"address_scope_id"
368-
]
369-
elif ip_version == const.IP_VERSION_6:
370-
address6_scope_id = subnet_pool[
371-
"address_scope_id"
372-
]
373-
except n_exc.SubnetPoolNotFound:
374-
# swallow the exception and just continue if the
375-
# lookup failed
376-
pass
377-
378361
cidrs += ' {}/{}'.format(ip['ip_address'],
379362
subnet['cidr'].split('/')[1])
380363

neutron/tests/unit/common/ovn/test_utils.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from neutron_lib.api.definitions import extra_dhcp_opt as edo_ext
2424
from neutron_lib.api.definitions import portbindings
2525
from neutron_lib import constants as n_const
26+
from neutron_lib import exceptions as n_exc
2627
from oslo_concurrency import processutils
2728
from oslo_config import cfg
2829
import testtools
@@ -943,3 +944,62 @@ def test_run_empty_list(self):
943944
def test_run_bad_schema(self):
944945
with testtools.ExpectedException(KeyError):
945946
self.OvsdbClientTestCommand.run(['foo'])
947+
948+
949+
class GetSubnetsAddressScopeTestCase(base.BaseTestCase):
950+
951+
def setUp(self):
952+
super().setUp()
953+
self.ml2_plugin = mock.Mock()
954+
955+
def test_no_subnets(self):
956+
subnets = []
957+
fixed_ips = mock.ANY
958+
address4, address6 = utils.get_subnets_address_scopes(
959+
mock.ANY, subnets, fixed_ips, self.ml2_plugin)
960+
self.assertEqual(('', ''), (address4, address6))
961+
962+
def test_no_subnetpool(self):
963+
subnets = [
964+
{'id': 'subnet1', 'subnetpool_id': None},
965+
{'id': 'subnet2', 'subnetpool_id': None},
966+
]
967+
fixed_ips = [
968+
{'subnet_id': 'subnet1'},
969+
{'subnet_id': 'subnet2'},
970+
]
971+
address4, address6 = utils.get_subnets_address_scopes(
972+
mock.ANY, subnets, fixed_ips, self.ml2_plugin)
973+
self.assertEqual(('', ''), (address4, address6))
974+
975+
def test_no_address_scope(self):
976+
subnets = [
977+
{'id': 'subnet1', 'subnetpool_id': 'pool_ipv4'},
978+
{'id': 'subnet2', 'subnetpool_id': 'pool_ipv6'},
979+
]
980+
fixed_ips = [
981+
{'subnet_id': 'subnet1'},
982+
{'subnet_id': 'subnet2'},
983+
]
984+
self.ml2_plugin.get_subnetpool.side_effect = n_exc.SubnetPoolNotFound(
985+
subnetpool_id='snp')
986+
address4, address6 = utils.get_subnets_address_scopes(
987+
mock.ANY, subnets, fixed_ips, self.ml2_plugin)
988+
self.assertEqual(('', ''), (address4, address6))
989+
990+
def test_address_scope(self):
991+
subnets = [
992+
{'id': 'subnet1', 'subnetpool_id': 'pool_ipv4'},
993+
{'id': 'subnet2', 'subnetpool_id': 'pool_ipv6'},
994+
]
995+
fixed_ips = [
996+
{'subnet_id': 'subnet1'},
997+
{'subnet_id': 'subnet2'},
998+
]
999+
self.ml2_plugin.get_subnetpool.side_effect = [
1000+
{'address_scope_id': 'scope4', 'ip_version': n_const.IP_VERSION_4},
1001+
{'address_scope_id': 'scope6', 'ip_version': n_const.IP_VERSION_6},
1002+
]
1003+
address4, address6 = utils.get_subnets_address_scopes(
1004+
mock.ANY, subnets, fixed_ips, self.ml2_plugin)
1005+
self.assertEqual(('scope4', 'scope6'), (address4, address6))

0 commit comments

Comments
 (0)