From e814e93e8ed92683c5b46083fe479cf384b81ed5 Mon Sep 17 00:00:00 2001 From: Lucas Alvares Gomes Date: Tue, 12 Oct 2021 11:42:39 +0100 Subject: [PATCH] [OVN] Tune OVN routers to reduce the mem footprint for ML2/OVN In order to avoid having a MAC_Binding table explosion and helping lowering the memory footprint when using ML2/OVN this patch is setting two options to the OVN routers: * always_learn_from_arp_request: By setting this to False we avoid learning from ARP replies observed in the network. Only the ARP requests sent by OVN will generate a MAC_Binding entry in the OVSDB database. For larger broadcasts domains this avoids having a MAC_Binding table explosion, reduce the DB size and memory footprint of ML2/OVN. * dynamic_neigh_routers: By setting this to True we avoid pre-populating flows for router to router communication, reduding the number of flows, DB size and memory footprint of ML2/OVN. For more information on these option for core OVN please refer to: https://www.ovn.org/support/dist-docs/ovn-nb.5.html This patch also includes a new maintenance task to include these options to existing routers in the system. Related-Bug: #1946318 Change-Id: I056acdec9b6ee2341d2bc4f7bd9a678f3bf91972 Signed-off-by: Lucas Alvares Gomes (cherry picked from commit a278c5ba789c014ec777a75fc9538179d6707202) --- .../ovn/mech_driver/ovsdb/maintenance.py | 26 +++++++++++++++++ .../ovn/mech_driver/ovsdb/ovn_client.py | 4 ++- .../ovn/mech_driver/ovsdb/test_maintenance.py | 28 +++++++++++++++++++ .../tests/unit/services/ovn_l3/test_plugin.py | 3 +- 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py index 65692c8ae03..438bab5617a 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py @@ -14,6 +14,7 @@ # under the License. import abc +import copy import inspect import threading @@ -675,6 +676,31 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase): raise periodics.NeverAgain() + # TODO(lucasagomes): Remove this in the Z cycle + # A static spacing value is used here, but this method will only run + # once per lock due to the use of periodics.NeverAgain(). + @periodics.periodic(spacing=600, run_immediately=True) + def check_router_mac_binding_options(self): + if not self.has_lock: + return + + cmds = [] + for router in self._nb_idl.lr_list().execute(check_error=True): + if (router.options.get('always_learn_from_arp_request') and + router.options.get('dynamic_neigh_routers')): + continue + + opts = copy.deepcopy(router.options) + opts.update({'always_learn_from_arp_request': 'false', + 'dynamic_neigh_routers': 'true'}) + cmds.append(self._nb_idl.update_lrouter(router.name, options=opts)) + + if cmds: + with self._nb_idl.transaction(check_error=True) as txn: + for cmd in cmds: + txn.add(cmd) + raise periodics.NeverAgain() + class HashRingHealthCheckPeriodics(object): diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py index 278918f50e8..c361a23ef27 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py @@ -1170,11 +1170,13 @@ class OVNClient(object): enabled = router.get('admin_state_up') lrouter_name = utils.ovn_name(router['id']) added_gw_port = None + options = {'always_learn_from_arp_request': 'false', + 'dynamic_neigh_routers': 'true'} with self._nb_idl.transaction(check_error=True) as txn: txn.add(self._nb_idl.create_lrouter(lrouter_name, external_ids=external_ids, enabled=enabled, - options={})) + options=options)) # TODO(lucasagomes): add_external_gateway is being only used # by the ovn_db_sync.py script, remove it after the database # synchronization work diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py index 5d6c7bc875e..80838e392e6 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py @@ -432,3 +432,31 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight, mock.call('lsp5', mcast_flood_reports='true', mcast_flood='false')] nb_idl.lsp_set_options.assert_has_calls(expected_calls) + + def test_check_router_mac_binding_options(self): + nb_idl = self.fake_ovn_client._nb_idl + lr0 = fakes.FakeOvsdbRow.create_one_ovsdb_row( + attrs={'name': 'lr0', + 'options': {'always_learn_from_arp_request': 'false', + 'dynamic_neigh_routers': 'true'}}) + lr1 = fakes.FakeOvsdbRow.create_one_ovsdb_row( + attrs={'name': 'lr1', 'options': {}}) + lr2 = fakes.FakeOvsdbRow.create_one_ovsdb_row( + attrs={'name': 'lr2', 'options': {}}) + nb_idl.lr_list.return_value.execute.return_value = [lr0, lr1, lr2] + + # Invoke the periodic method, it meant to run only once at startup + # so NeverAgain will be raised at the end + self.assertRaises(periodics.NeverAgain, + self.periodic.check_router_mac_binding_options) + + # Assert lr1 and lr2 had their options updated since the values + # were not set + expected_calls = [ + mock.call('lr1', + options={'always_learn_from_arp_request': 'false', + 'dynamic_neigh_routers': 'true'}), + mock.call('lr2', + options={'always_learn_from_arp_request': 'false', + 'dynamic_neigh_routers': 'true'})] + nb_idl.update_lrouter.assert_has_calls(expected_calls) diff --git a/neutron/tests/unit/services/ovn_l3/test_plugin.py b/neutron/tests/unit/services/ovn_l3/test_plugin.py index 03bba7a16f4..55db634dc21 100644 --- a/neutron/tests/unit/services/ovn_l3/test_plugin.py +++ b/neutron/tests/unit/services/ovn_l3/test_plugin.py @@ -538,7 +538,8 @@ class TestOVNL3RouterPlugin(test_mech_driver.Ml2PluginV2TestCase): ovn_const.OVN_ROUTER_AZ_HINTS_EXT_ID_KEY: ''} self.l3_inst._ovn.create_lrouter.assert_called_once_with( 'neutron-router-id', external_ids=external_ids, - enabled=True, options={}) + enabled=True, options={'always_learn_from_arp_request': 'false', + 'dynamic_neigh_routers': 'true'}) self.l3_inst._ovn.add_lrouter_port.assert_called_once_with( **self.fake_ext_gw_port_assert) expected_calls = [