Merge "[OVN] A LRP in an external tunnelled network has no chassis"

This commit is contained in:
Zuul 2024-02-14 15:59:24 +00:00 committed by Gerrit Code Review
commit a8fe0cb369
3 changed files with 54 additions and 50 deletions

View File

@ -1531,11 +1531,13 @@ class OVNClient(object):
"""Return chassis for scheduling gateway router.
Criteria for selecting chassis as candidates
1) chassis from cms with proper bridge mappings
2) if no chassis is available from 1) then,
select chassis with proper bridge mappings
3) Filter the available chassis accordingly to the routers
1) Chassis from cms with proper bridge mappings only (that means these
gateway chassis with the requested physical network).
2) Filter the available chassis accordingly to the routers
availability zone hints (if present)
If the logical router port belongs to a tunnelled network, there won't
be any candidate.
"""
# TODO(lucasagomes): Simplify the logic here, the CMS option has
# been introduced long ago and by now all gateway chassis should
@ -1544,15 +1546,13 @@ class OVNClient(object):
cms = cms or self._sb_idl.get_gateway_chassis_from_cms_options()
chassis_physnets = (chassis_physnets or
self._sb_idl.get_chassis_and_physnets())
cms_bmaps = []
bmaps = []
candidates = set()
for chassis, physnets in chassis_physnets.items():
if physnet and physnet in physnets:
if chassis in cms:
cms_bmaps.append(chassis)
else:
bmaps.append(chassis)
candidates = cms_bmaps or bmaps or cms
if (physnet and
physnet in physnets and
chassis in cms):
candidates.add(chassis)
candidates = list(candidates)
# Filter for availability zones
if availability_zone_hints:
@ -1563,11 +1563,8 @@ class OVNClient(object):
if az in utils.get_chassis_availability_zones(
self._sb_idl.lookup('Chassis', ch, None))]
if not cms_bmaps:
LOG.debug("No eligible chassis with external connectivity"
" through ovn-cms-options for %s", physnet)
LOG.debug("Chassis candidates for scheduling gateway router ports: %s",
candidates)
LOG.debug('Chassis candidates for scheduling gateway router ports '
'for "%s" physical network: %s', physnet, candidates)
return candidates
def _get_physnet(self, network):

View File

@ -33,12 +33,14 @@ from neutron.tests.functional.resources.ovsdb import events
class TestRouter(base.TestOVNFunctionalBase):
def setUp(self):
super(TestRouter, self).setUp()
def setUp(self, **kwargs):
super().setUp(**kwargs)
self.chassis1 = self.add_fake_chassis(
'ovs-host1', physical_nets=['physnet1', 'physnet3'])
'ovs-host1', physical_nets=['physnet1', 'physnet3'],
enable_chassis_as_gw=True, azs=[])
self.chassis2 = self.add_fake_chassis(
'ovs-host2', physical_nets=['physnet2', 'physnet3'])
'ovs-host2', physical_nets=['physnet2', 'physnet3'],
enable_chassis_as_gw=True, azs=[])
self.cr_lrp_pb_event = events.WaitForCrLrpPortBindingEvent()
self.sb_api.idl.notify_handler.watch_event(self.cr_lrp_pb_event)
@ -105,12 +107,14 @@ class TestRouter(base.TestOVNFunctionalBase):
self.assertCountEqual(expected, chassis)
def _check_gateway_chassis_candidates(self, candidates,
router_az_hints=None):
router_az_hints=None,
physnet='physnet1'):
# In this test, fake_select() is called once from _create_router()
# and later from schedule_unhosted_gateways()
ovn_client = self.l3_plugin._ovn_client
net_type = 'vlan' if physnet else 'geneve'
ext1 = self._create_ext_network(
'ext1', 'vlan', 'physnet1', 1, "10.0.0.1", "10.0.0.0/24")
'ext1', net_type, physnet, 1, "10.0.0.1", "10.0.0.0/24")
# mock select function and check if it is called with expected
# candidates.
@ -141,12 +145,11 @@ class TestRouter(base.TestOVNFunctionalBase):
def test_gateway_chassis_with_cms_and_bridge_mappings(self):
# Both chassis1 and chassis3 are having proper bridge mappings,
# but only chassis3 is having enable-chassis-as-gw.
# Test if chassis3 is selected as candidate or not.
# but only chassis1 is having enable-chassis-as-gw.
# Test if chassis1 is selected as candidate or not.
self.chassis3 = self.add_fake_chassis(
'ovs-host3', physical_nets=['physnet1'],
other_config={'ovn-cms-options': 'enable-chassis-as-gw'})
self._check_gateway_chassis_candidates([self.chassis3])
'ovs-host3', physical_nets=['physnet1'], azs=[])
self._check_gateway_chassis_candidates([self.chassis1])
def test_gateway_chassis_with_cms_and_no_bridge_mappings(self):
# chassis1 is having proper bridge mappings.
@ -180,12 +183,10 @@ class TestRouter(base.TestOVNFunctionalBase):
# Test if chassis3 is selected as candidate or not.
self.chassis3 = self.add_fake_chassis(
'ovs-host3', physical_nets=['physnet1'],
other_config={'ovn-cms-options': 'enable-chassis-as-gw'},
azs=['ovn'])
azs=['ovn'], enable_chassis_as_gw=True)
self.chassis4 = self.add_fake_chassis(
'ovs-host4', physical_nets=['physnet1'],
other_config={'ovn-cms-options': 'enable-chassis-as-gw'},
azs=['ovn2'])
azs=['ovn2'], enable_chassis_as_gw=True)
self._check_gateway_chassis_candidates([self.chassis3],
router_az_hints=['ovn'])
@ -195,11 +196,9 @@ class TestRouter(base.TestOVNFunctionalBase):
# AvailabilityZoneNotFound. after create will delete if.
# add chassis4 is having azs [ovn2], not match routers az_hints [ovn]
self.chassis3 = self.add_fake_chassis(
'ovs-host3', physical_nets=['physnet1'],
other_config={'ovn-cms-options': 'enable-chassis-as-gw'})
'ovs-host3', physical_nets=['physnet1'], enable_chassis_as_gw=True)
self.chassis4 = self.add_fake_chassis(
'ovs-host4', physical_nets=['physnet1'],
other_config={'ovn-cms-options': 'enable-chassis-as-gw'},
'ovs-host4', physical_nets=['physnet1'], enable_chassis_as_gw=True,
azs=['ovn2'])
ovn_client = self.l3_plugin._ovn_client
ext1 = self._create_ext_network(
@ -227,6 +226,11 @@ class TestRouter(base.TestOVNFunctionalBase):
# Test if chassis1 is selected as candidate or not.
self._check_gateway_chassis_candidates([self.chassis1])
def test_gateway_chassis_no_physnet_tunnelled_network(self):
# The GW network is tunnelled, no physnet defined --> no possible
# candidates.
self._check_gateway_chassis_candidates([], physnet=None)
def test_gateway_chassis_least_loaded_scheduler(self):
# This test will create 4 routers each with its own gateway.
# Using the least loaded policy for scheduling gateway ports, we

View File

@ -2643,24 +2643,27 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
return ch
ovn_client._sb_idl.lookup = fake_lookup
# The target physnet and availability zones
physnet = 'public'
az_hints = ['az0', 'az2']
# List of chassis and chassis:physnet mappings.
physnet_name = 'public'
cms = [ch0.name, ch1.name, ch2.name, ch3.name, ch4.name, ch5.name]
ch_physnet = {ch0.name: [physnet], ch1.name: [physnet],
ch2.name: [physnet], ch3.name: [physnet],
ch_physnet = {ch0.name: [physnet_name], ch1.name: [physnet_name],
ch2.name: [physnet_name], ch3.name: [physnet_name],
ch4.name: ['another-physnet'],
ch5.name: ['yet-another-physnet']}
candidates = ovn_client.get_candidates_for_scheduling(
physnet, cms=cms, chassis_physnets=ch_physnet,
availability_zone_hints=az_hints)
# Only chassis ch0 and ch2 should match the availability zones
# hints and physnet we passed to get_candidates_for_scheduling()
expected_candidates = [ch0.name, ch2.name]
self.assertEqual(sorted(expected_candidates), sorted(candidates))
# The target physnets, the availability zones and the expected
# candidates.
results = [{'physnet': physnet_name, 'az_hints': ['az0', 'az2'],
'expected_candidates': [ch0.name, ch2.name]},
{'physnet': None, 'az_hints': ['az0', 'az2'],
'expected_candidates': []},
]
for result in results:
candidates = ovn_client.get_candidates_for_scheduling(
result['physnet'], cms=cms, chassis_physnets=ch_physnet,
availability_zone_hints=result['az_hints'])
self.assertEqual(sorted(result['expected_candidates']),
sorted(candidates))
def test__get_info_for_ha_chassis_group_as_extport(self):
net_attrs = {az_def.AZ_HINTS: ['az0', 'az1', 'az2']}