Merge "Add support for FDB aging"

This commit is contained in:
Zuul 2023-10-24 16:00:17 +00:00 committed by Gerrit Code Review
commit f185379980
7 changed files with 133 additions and 2 deletions

View File

@ -402,6 +402,9 @@ LRP_OPTIONS_RESIDE_REDIR_CH = 'reside-on-redirect-chassis'
LRP_OPTIONS_REDIRECT_TYPE = 'redirect-type'
BRIDGE_REDIRECT_TYPE = "bridged"
# FDB AGE Settings
LS_OPTIONS_FDB_AGE_THRESHOLD = 'fdb_age_threshold'
# Port Binding types
PB_TYPE_VIRTUAL = 'virtual'

View File

@ -226,6 +226,12 @@ ovn_opts = [
'flooding for traffic towards unknown IPs when port '
'security is disabled. It requires OVN 22.09 or '
'newer.')),
cfg.IntOpt('fdb_age_threshold',
min=0,
default=0,
help=_('The number of seconds to keep FDB entries in the OVN '
'DB. The value defaults to 0, which means disabled. '
'This is supported by OVN >= 23.09.')),
]
nb_global_opts = [
@ -241,6 +247,14 @@ nb_global_opts = [
'is not an issue, setting it to True can reduce '
'the load and latency of the control plane. '
'The default value is False.')),
cfg.IntOpt('fdb_removal_limit',
min=0,
default=0,
help=_('FDB aging bulk removal limit. This limits how many '
'rows can expire in a single transaction. Default '
'is 0, which is unlimited. When the limit is reached, '
'the next batch removal is delayed by 5 seconds. '
'This is supported by OVN >= 23.09.')),
]
@ -360,3 +374,11 @@ def is_ovn_dhcp_disabled_for_baremetal():
def is_learn_fdb_enabled():
return cfg.CONF.ovn.localnet_learn_fdb
def get_fdb_age_threshold():
return str(cfg.CONF.ovn.fdb_age_threshold)
def get_fdb_removal_limit():
return str(cfg.CONF.ovn_nb_global.fdb_removal_limit)

View File

@ -798,6 +798,39 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
txn.add(cmd)
raise periodics.NeverAgain()
# A static spacing value is used here, but this method will only run
# once per lock due to the use of periodics.NeverAgain().
@has_lock_periodic(spacing=600, run_immediately=True)
def check_fdb_aging_settings(self):
"""Check FDB aging settings
Ensure FDB aging settings are enforced.
"""
context = n_context.get_admin_context()
cmds = []
config_fdb_age_threshold = ovn_conf.get_fdb_age_threshold()
# Get provider networks
nets = self._ovn_client._plugin.get_networks(context)
for net in nets:
if not utils.is_provider_network(net):
continue
ls_name = utils.ovn_name(net['id'])
ls = self._nb_idl.get_lswitch(ls_name)
ls_fdb_age_threshold = ls.other_config.get(
ovn_const.LS_OPTIONS_FDB_AGE_THRESHOLD)
if config_fdb_age_threshold != ls_fdb_age_threshold:
other_config = {ovn_const.LS_OPTIONS_FDB_AGE_THRESHOLD:
config_fdb_age_threshold}
cmds.append(self._nb_idl.db_set(
'Logical_Switch', ls_name,
('other_config', other_config)))
if cmds:
with self._nb_idl.transaction(check_error=True) as txn:
for cmd in cmds:
txn.add(cmd)
raise periodics.NeverAgain()
# TODO(fnordahl): Remove this in the B+3 cycle. This method removes the
# now redundant "external_ids:OVN_GW_NETWORK_EXT_ID_KEY" and
# "external_ids:OVN_GW_PORT_EXT_ID_KEY" from to each router.

View File

@ -1914,6 +1914,9 @@ class OVNClient(object):
params['other_config'] = {ovn_const.MCAST_SNOOP: value,
ovn_const.MCAST_FLOOD_UNREGISTERED: 'false',
ovn_const.VLAN_PASSTHRU: vlan_transparent}
if utils.is_provider_network(network):
params['other_config'][ovn_const.LS_OPTIONS_FDB_AGE_THRESHOLD] = (
ovn_conf.get_fdb_age_threshold())
return params
def create_network(self, context, network):

View File

@ -20,6 +20,7 @@ from oslo_config import cfg
from futurist import periodics
from neutron_lib.api.definitions import external_net as extnet_apidef
from neutron_lib.api.definitions import floating_ip_port_forwarding as pf_def
from neutron_lib.api.definitions import provider_net as provnet_apidef
from neutron_lib import constants as n_const
from neutron_lib import context as n_context
from neutron_lib.exceptions import l3 as lib_l3_exc
@ -60,8 +61,12 @@ class _TestMaintenanceHelper(base.TestOVNFunctionalBase):
ovn_const.OVN_NETWORK_NAME_EXT_ID_KEY) == name):
return row
def _create_network(self, name, external=False):
data = {'network': {'name': name, extnet_apidef.EXTERNAL: external}}
def _create_network(self, name, external=False, provider=None):
data = {'network': {'name': name,
extnet_apidef.EXTERNAL: external}}
if provider:
data['network'][provnet_apidef.NETWORK_TYPE] = 'flat'
data['network'][provnet_apidef.PHYSICAL_NETWORK] = provider
req = self.new_create_request('networks', data, self.fmt,
as_admin=True)
res = req.get_response(self.api)
@ -756,6 +761,26 @@ class TestMaintenance(_TestMaintenanceHelper):
self.assertEqual(
'false', ls['other_config'][ovn_const.MCAST_FLOOD_UNREGISTERED])
def test_check_for_aging_settings(self):
net = self._create_network('net', provider='datacentre')
ls = self.nb_api.get_lswitch(utils.ovn_name(net['id']))
self.assertEqual(
'0', ls.other_config.get(ovn_const.LS_OPTIONS_FDB_AGE_THRESHOLD))
# Change the value of the configuration
cfg.CONF.set_override('fdb_age_threshold', 5, group='ovn')
# Call the maintenance task and check that the value has been
# updated in the Logical Switch
self.assertRaises(periodics.NeverAgain,
self.maint.check_fdb_aging_settings)
ls = self.nb_api.get_lswitch(utils.ovn_name(net['id']))
self.assertEqual(
'5', ls.other_config.get(ovn_const.LS_OPTIONS_FDB_AGE_THRESHOLD))
def test_floating_ip(self):
ext_net = self._create_network('ext_networktest', external=True)
ext_subnet = self._create_subnet(

View File

@ -891,6 +891,37 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
self.fake_ovn_client._nb_idl.db_set.assert_has_calls(
expected_calls)
def test_check_fdb_aging_settings(self):
cfg.CONF.set_override('fdb_age_threshold', 5, group='ovn')
networks = [{'id': 'foo',
'provider:physical_network': 'datacentre'}]
self.fake_ovn_client._plugin.get_networks.return_value = networks
fake_ls = mock.Mock(other_config={})
self.fake_ovn_client._nb_idl.get_lswitch.return_value = fake_ls
self.assertRaises(
periodics.NeverAgain,
self.periodic.check_fdb_aging_settings)
self.fake_ovn_client._nb_idl.db_set.assert_called_once_with(
'Logical_Switch', 'neutron-foo',
('other_config', {constants.LS_OPTIONS_FDB_AGE_THRESHOLD: '5'}))
def test_check_fdb_aging_settings_with_threshold_set(self):
cfg.CONF.set_override('fdb_age_threshold', 5, group='ovn')
networks = [{'id': 'foo',
'provider:network_type': n_const.TYPE_VLAN}]
self.fake_ovn_client._plugin.get_networks.return_value = networks
fake_ls = mock.Mock(other_config={
constants.LS_OPTIONS_FDB_AGE_THRESHOLD: '5'})
self.fake_ovn_client._nb_idl.get_lswitch.return_value = fake_ls
self.assertRaises(
periodics.NeverAgain,
self.periodic.check_fdb_aging_settings)
self.fake_ovn_client._nb_idl.db_set.assert_not_called()
def test_remove_gw_ext_ids_from_logical_router(self):
nb_idl = self.fake_ovn_client._nb_idl
# lr0: GW port ID, not GW network ID --> we need to remove port ID.

View File

@ -0,0 +1,14 @@
---
features:
- |
In OVN 22.09 the option ``localnet_learn_fdb`` was added, enabling localnet
ports to learn MAC addresses and store them at the FDB table. There was no
aging mechanism for those MACs until OVN 23.06, where the configuration
option ``fdb_age_threshold`` was added. This enables to set the maximum
time the learned MACs will stay in the FDB table (in seconds). When the
``localnet_learn_fdb`` configuration option is enabled, the proper value
for ``fdb_age_threshold`` should also be set, to avoid
performance/scalability issues due to the table growing too much --
especially when provider networks are large. In addition the configuration
option ``fdb_removal_limit`` was also added to avoid removing a large
number of entries at once.