Skip to content

Commit ee607d8

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[OVN] The L3 scheduler does not use all chassis by default" into stable/2023.1
2 parents ec6595e + 0a42111 commit ee607d8

File tree

6 files changed

+56
-43
lines changed

6 files changed

+56
-43
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1651,8 +1651,7 @@ def _create_lrouter_port(self, context, router, port, txn=None):
16511651
physnet, availability_zone_hints=common_utils.get_az_hints(
16521652
router))
16531653
selected_chassis = self._ovn_scheduler.select(
1654-
self._nb_idl, self._sb_idl, lrouter_port_name,
1655-
candidates=candidates)
1654+
self._nb_idl, lrouter_port_name, candidates=candidates)
16561655
if selected_chassis:
16571656
columns['gateway_chassis'] = selected_chassis
16581657

neutron/scheduler/l3_ovn_scheduler.py

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self):
3535
pass
3636

3737
@abc.abstractmethod
38-
def select(self, nb_idl, sb_idl, gateway_name, candidates=None):
38+
def select(self, nb_idl, gateway_name, candidates=None):
3939
"""Schedule the gateway port of a router to an OVN chassis.
4040
4141
Schedule the gateway router port only if it is not already
@@ -57,10 +57,10 @@ def filter_existing_chassis(self, nb_idl, gw_chassis,
5757
chassis_list.remove(chassis_name)
5858
return chassis_list
5959

60-
def _schedule_gateway(self, nb_idl, sb_idl, gateway_name, candidates,
60+
def _schedule_gateway(self, nb_idl, gateway_name, candidates,
6161
existing_chassis):
6262
existing_chassis = existing_chassis or []
63-
candidates = candidates or self._get_chassis_candidates(sb_idl)
63+
candidates = candidates or []
6464
candidates = list(set(candidates) - set(existing_chassis))
6565
# If no candidates, or gateway scheduled on MAX_GATEWAY_CHASSIS nodes
6666
# or all candidates in existing_chassis, return existing_chassis.
@@ -70,6 +70,8 @@ def _schedule_gateway(self, nb_idl, sb_idl, gateway_name, candidates,
7070
len(existing_chassis) == ovn_const.MAX_GW_CHASSIS):
7171
return existing_chassis
7272
if not candidates:
73+
LOG.warning('Gateway %s was not scheduled on any chassis, no '
74+
'candidates are available', gateway_name)
7375
return [ovn_const.OVN_GATEWAY_INVALID_CHASSIS]
7476
chassis_count = ovn_const.MAX_GW_CHASSIS - len(existing_chassis)
7577
# The actual binding of the gateway to a chassis via the options
@@ -88,20 +90,13 @@ def _schedule_gateway(self, nb_idl, sb_idl, gateway_name, candidates,
8890
def _select_gateway_chassis(self, nb_idl, candidates):
8991
"""Choose a chassis from candidates based on a specific policy."""
9092

91-
def _get_chassis_candidates(self, sb_idl):
92-
# TODO(azbiswas): Allow selection of a specific type of chassis when
93-
# the upstream code merges.
94-
# return (sb_idl.get_all_chassis('gateway_router') or
95-
# sb_idl.get_all_chassis())
96-
return sb_idl.get_all_chassis()
97-
9893

9994
class OVNGatewayChanceScheduler(OVNGatewayScheduler):
10095
"""Randomly select an chassis for a gateway port of a router"""
10196

102-
def select(self, nb_idl, sb_idl, gateway_name, candidates=None,
97+
def select(self, nb_idl, gateway_name, candidates=None,
10398
existing_chassis=None):
104-
return self._schedule_gateway(nb_idl, sb_idl, gateway_name,
99+
return self._schedule_gateway(nb_idl, gateway_name,
105100
candidates, existing_chassis)
106101

107102
def _select_gateway_chassis(self, nb_idl, candidates):
@@ -113,9 +108,9 @@ def _select_gateway_chassis(self, nb_idl, candidates):
113108
class OVNGatewayLeastLoadedScheduler(OVNGatewayScheduler):
114109
"""Select the least loaded chassis for a gateway port of a router"""
115110

116-
def select(self, nb_idl, sb_idl, gateway_name, candidates=None,
111+
def select(self, nb_idl, gateway_name, candidates=None,
117112
existing_chassis=None):
118-
return self._schedule_gateway(nb_idl, sb_idl, gateway_name,
113+
return self._schedule_gateway(nb_idl, gateway_name,
119114
candidates, existing_chassis)
120115

121116
@staticmethod

neutron/services/ovn_l3/plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ def schedule_unhosted_gateways(self, event_from_chassis=None):
434434
chassis_physnets=chassis_with_physnets,
435435
availability_zone_hints=az_hints)
436436
chassis = self.scheduler.select(
437-
self._nb_ovn, self._sb_ovn, g_name, candidates=candidates,
437+
self._nb_ovn, g_name, candidates=candidates,
438438
existing_chassis=existing_chassis)
439439
if primary and primary != chassis[0]:
440440
if primary not in chassis:

neutron/tests/unit/scheduler/test_l3_ovn_scheduler.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,11 @@ def __init__(self, chassis_gateway_mapping, gateway):
3030
None))
3131

3232

33-
class FakeOVNGatewaySchedulerSbOvnIdl(object):
34-
def __init__(self, chassis_gateway_mapping):
35-
self.get_all_chassis = mock.Mock(
36-
return_value=chassis_gateway_mapping['Chassis'])
37-
38-
3933
class TestOVNGatewayScheduler(base.BaseTestCase):
4034

4135
def setUp(self):
4236
super(TestOVNGatewayScheduler, self).setUp()
37+
self.mock_log = mock.patch.object(l3_ovn_scheduler, 'LOG').start()
4338

4439
# Overwritten by derived classes
4540
self.l3_scheduler = None
@@ -82,12 +77,11 @@ def setUp(self):
8277
if chassis in details['Chassis_Bindings']:
8378
details['Chassis_Bindings'][chassis].append((gw, 0))
8479

85-
def select(self, chassis_gateway_mapping, gateway_name):
80+
def select(self, chassis_gateway_mapping, gateway_name, candidates=None):
8681
nb_idl = FakeOVNGatewaySchedulerNbOvnIdl(chassis_gateway_mapping,
8782
gateway_name)
88-
sb_idl = FakeOVNGatewaySchedulerSbOvnIdl(chassis_gateway_mapping)
89-
return self.l3_scheduler.select(nb_idl, sb_idl,
90-
gateway_name)
83+
return self.l3_scheduler.select(nb_idl, gateway_name,
84+
candidates=candidates)
9185

9286
def filter_existing_chassis(self, *args, **kwargs):
9387
return self.l3_scheduler.filter_existing_chassis(
@@ -108,21 +102,33 @@ def setUp(self):
108102
def test_no_chassis_available_for_existing_gateway(self):
109103
mapping = self.fake_chassis_gateway_mappings['None']
110104
gateway_name = random.choice(list(mapping['Gateways'].keys()))
111-
chassis = self.select(mapping, gateway_name)
105+
chassis = self.select(mapping, gateway_name,
106+
candidates=mapping['Chassis'])
112107
self.assertEqual([ovn_const.OVN_GATEWAY_INVALID_CHASSIS], chassis)
113108

114109
def test_no_chassis_available_for_new_gateway(self):
115110
mapping = self.fake_chassis_gateway_mappings['None']
116111
gateway_name = self.new_gateway_name
117-
chassis = self.select(mapping, gateway_name)
112+
chassis = self.select(mapping, gateway_name,
113+
candidates=mapping['Chassis'])
118114
self.assertEqual([ovn_const.OVN_GATEWAY_INVALID_CHASSIS], chassis)
119115

120116
def test_random_chassis_available_for_new_gateway(self):
121117
mapping = self.fake_chassis_gateway_mappings['Multiple1']
122118
gateway_name = self.new_gateway_name
123-
chassis = self.select(mapping, gateway_name)
119+
chassis = self.select(mapping, gateway_name,
120+
candidates=mapping['Chassis'])
124121
self.assertCountEqual(chassis, mapping.get('Chassis'))
125122

123+
def test_no_candidates_provided(self):
124+
mapping = self.fake_chassis_gateway_mappings['None']
125+
gateway_name = self.new_gateway_name
126+
chassis = self.select(mapping, gateway_name)
127+
self.assertEqual([ovn_const.OVN_GATEWAY_INVALID_CHASSIS], chassis)
128+
self.mock_log.warning.assert_called_once_with(
129+
'Gateway %s was not scheduled on any chassis, no candidates are '
130+
'available', gateway_name)
131+
126132
def test_filter_existing_chassis(self):
127133
# filter_existing_chassis is scheduler independent, but calling
128134
# it from Base class didnt seem right. Also, there is no need to have
@@ -171,19 +177,22 @@ def setUp(self):
171177
def test_no_chassis_available_for_existing_gateway(self):
172178
mapping = self.fake_chassis_gateway_mappings['None']
173179
gateway_name = random.choice(list(mapping['Gateways'].keys()))
174-
chassis = self.select(mapping, gateway_name)
180+
chassis = self.select(mapping, gateway_name,
181+
candidates=mapping['Chassis'])
175182
self.assertEqual([ovn_const.OVN_GATEWAY_INVALID_CHASSIS], chassis)
176183

177184
def test_no_chassis_available_for_new_gateway(self):
178185
mapping = self.fake_chassis_gateway_mappings['None']
179186
gateway_name = self.new_gateway_name
180-
chassis = self.select(mapping, gateway_name)
187+
chassis = self.select(mapping, gateway_name,
188+
candidates=mapping['Chassis'])
181189
self.assertEqual([ovn_const.OVN_GATEWAY_INVALID_CHASSIS], chassis)
182190

183191
def test_least_loaded_chassis_available_for_new_gateway1(self):
184192
mapping = self.fake_chassis_gateway_mappings['Multiple1']
185193
gateway_name = self.new_gateway_name
186-
chassis = self.select(mapping, gateway_name)
194+
chassis = self.select(mapping, gateway_name,
195+
candidates=mapping['Chassis'])
187196
self.assertCountEqual(chassis, mapping.get('Chassis'))
188197
# least loaded will be the first one in the list,
189198
# networking-ovn will assign highest priority to this first element
@@ -192,28 +201,32 @@ def test_least_loaded_chassis_available_for_new_gateway1(self):
192201
def test_least_loaded_chassis_available_for_new_gateway2(self):
193202
mapping = self.fake_chassis_gateway_mappings['Multiple2']
194203
gateway_name = self.new_gateway_name
195-
chassis = self.select(mapping, gateway_name)
204+
chassis = self.select(mapping, gateway_name,
205+
candidates=mapping['Chassis'])
196206
# hv1 will have least priority
197207
self.assertEqual(chassis[2], 'hv1')
198208

199209
def test_least_loaded_chassis_available_for_new_gateway3(self):
200210
mapping = self.fake_chassis_gateway_mappings['Multiple3']
201211
gateway_name = self.new_gateway_name
202-
chassis = self.select(mapping, gateway_name)
212+
chassis = self.select(mapping, gateway_name,
213+
candidates=mapping['Chassis'])
203214
# least loaded chassis will be in the front of the list
204215
self.assertEqual(['hv1', 'hv3', 'hv2'], chassis)
205216

206217
def test_least_loaded_chassis_with_rebalance(self):
207218
mapping = self.fake_chassis_gateway_mappings['Multiple4']
208219
gateway_name = self.new_gateway_name
209-
chassis = self.select(mapping, gateway_name)
220+
chassis = self.select(mapping, gateway_name,
221+
candidates=mapping['Chassis'])
210222
# least loaded chassis will be in the front of the list
211223
self.assertEqual(['hv2', 'hv1'], chassis)
212224

213225
def test_existing_chassis_available_for_existing_gateway(self):
214226
mapping = self.fake_chassis_gateway_mappings['Multiple1']
215227
gateway_name = random.choice(list(mapping['Gateways'].keys()))
216-
chassis = self.select(mapping, gateway_name)
228+
chassis = self.select(mapping, gateway_name,
229+
candidates=mapping['Chassis'])
217230
self.assertEqual(ovn_const.MAX_GW_CHASSIS, len(chassis))
218231

219232
def test__get_chassis_load_by_prios_several_ports(self):

neutron/tests/unit/services/ovn_l3/test_plugin.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from neutron.api.v2 import router as api_router
3535
from neutron.common.ovn import constants as ovn_const
3636
from neutron.common.ovn import utils
37+
from neutron.conf.plugins.ml2.drivers import driver_type as driver_type_conf
3738
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf as config
3839
from neutron.db import extraroute_db
3940
from neutron import manager as neutron_manager
@@ -65,6 +66,7 @@ def _start_mock(self, path, return_value, new_callable=None):
6566
def setUp(self):
6667
mock.patch('neutron.plugins.ml2.drivers.ovn.mech_driver.ovsdb.'
6768
'impl_idl_ovn.Backend.schema_helper').start()
69+
driver_type_conf.register_ml2_drivers_geneve_opts(cfg=cfg.CONF)
6870
cfg.CONF.set_override('max_header_size', 38, group='ml2_type_geneve')
6971
super(TestOVNL3RouterPlugin, self).setUp()
7072
revision_plugin.RevisionPlugin()
@@ -1559,12 +1561,10 @@ def test_schedule_unhosted_gateways(self, get_gppm):
15591561
chassis_physnets=chassis_mappings,
15601562
cms=chassis, availability_zone_hints=[])] * 3)
15611563
self.mock_schedule.assert_has_calls([
1562-
mock.call(self.nb_idl(), self.sb_idl(),
1563-
'lrp-foo-1', [], ['chassis1', 'chassis2']),
1564-
mock.call(self.nb_idl(), self.sb_idl(),
1565-
'lrp-foo-2', [], ['chassis2']),
1566-
mock.call(self.nb_idl(), self.sb_idl(),
1567-
'lrp-foo-3', [], [])])
1564+
mock.call(self.nb_idl(), 'lrp-foo-1', [],
1565+
['chassis1', 'chassis2']),
1566+
mock.call(self.nb_idl(), 'lrp-foo-2', [], ['chassis2']),
1567+
mock.call(self.nb_idl(), 'lrp-foo-3', [], [])])
15681568
# make sure that for second port primary chassis stays untouched
15691569
self.nb_idl().update_lrouter_port.assert_has_calls([
15701570
mock.call('lrp-foo-1',
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
upgrade:
3+
- |
4+
In ML2/OVN, any new router gateway port (OVN logical router port) will be
5+
scheduled only on those chassis configured as gateway. Any existing router
6+
gateway port will preserve the current chassis assignation.

0 commit comments

Comments
 (0)