From 4e566b11d0df61ddf50fd9fef554bbcaf6717d5c Mon Sep 17 00:00:00 2001 From: Jakub Libosvar Date: Mon, 25 Sep 2023 23:35:02 +0000 Subject: [PATCH] Introduce ovn_nb_global config section The patch introduces a new config section ovn_nb_global. All options from this section are passed to OVN Northbound NB_Global table to configure Northbound OVN. First option is ignore_lsp_down to override OVN default configuration. This option was changed in OVN to `true` [1] but for Neutron it's better to not answer ARP packets when ports are in DOWN status. [1] https://www.mail-archive.com/ovs-dev@openvswitch.org/msg60064.html Change-Id: I478249cae483fd2540a7ff3ab714e3d9c3e13f17 Signed-off-by: Jakub Libosvar --- .../conf/plugins/ml2/drivers/ovn/ovn_conf.py | 21 +++++++++++- .../ml2/drivers/ovn/mech_driver/ovsdb/api.py | 4 +++ .../ovn/mech_driver/ovsdb/impl_idl_ovn.py | 4 +++ .../ovn/mech_driver/ovsdb/maintenance.py | 16 +++++++++ .../ovn/mech_driver/ovsdb/test_maintenance.py | 33 +++++++++++++++++++ ...o-ARP-for-DOWN-ports-3620173f00089539.yaml | 11 +++++++ 6 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/OVN-not-responding-to-ARP-for-DOWN-ports-3620173f00089539.yaml diff --git a/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py b/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py index 0706477c4ff..290dd4dce05 100644 --- a/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py +++ b/neutron/conf/plugins/ml2/drivers/ovn/ovn_conf.py @@ -28,6 +28,8 @@ VLOG_LEVELS = {'CRITICAL': vlog.CRITICAL, 'ERROR': vlog.ERROR, 'WARNING': MIGRATE_MODE = "migrate" +OVN_NB_GLOBAL = "ovn_nb_global" + ovn_opts = [ cfg.StrOpt('ovn_nb_connection', default='tcp:127.0.0.1:6641', @@ -226,16 +228,33 @@ ovn_opts = [ 'newer.')), ] +nb_global_opts = [ + cfg.BoolOpt('ignore_lsp_down', + default=False, + help=_('If set to False, ARP/ND reply flows for logical ' + 'switch ports will be installed only if the port is ' + 'UP, i.e. claimed by a Chassis. If set to True, these ' + 'flows are installed regardless of the status of the ' + 'port, which can result in a situation that an ARP ' + 'request to an IP is resolved even before the relevant ' + 'VM/container is running. For environments where this ' + 'is not an issue, setting it to True can reduce ' + 'the load and latency of the control plane. ' + 'The default value is False.')), +] + def register_opts(): cfg.CONF.register_opts(ovn_opts, group='ovn') ovs_conf.register_ovs_agent_opts() + cfg.CONF.register_opts(nb_global_opts, group=OVN_NB_GLOBAL) def list_opts(): return [ ('ovn', ovn_opts), - ('ovs', ovs_conf.OPTS) + ('ovs', ovs_conf.OPTS), + (OVN_NB_GLOBAL, nb_global_opts), ] diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py index 2c9670ae7ca..4bd2774f782 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/api.py @@ -623,6 +623,10 @@ class API(api.API, metaclass=abc.ABCMeta): :returns: The NAT rule row or Load_Balancer row or None """ + @abc.abstractmethod + def set_nb_global_options(self, key, value): + """Set NB_Global options configuration""" + class SbAPI(api.API, metaclass=abc.ABCMeta): diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py index ad0e80c4f6c..d1f7144e63a 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/impl_idl_ovn.py @@ -848,6 +848,10 @@ class OvsdbNbOvnIdl(nb_impl_idl.OvnNbApiIdlImpl, Backend): def update_lb_external_ids(self, lb_name, values, if_exists=True): return cmd.UpdateLbExternalIds(self, lb_name, values, if_exists) + def set_nb_global_options(self, **options): + LOG.debug("Setting NB_Global options: %s", options) + return self.db_set("NB_Global", ".", options=options) + class OvsdbSbOvnIdl(sb_impl_idl.OvnSbApiIdlImpl, Backend): def __init__(self, connection): 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 f27e3fe68f8..b639af08713 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py @@ -1079,6 +1079,22 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase): context = n_context.get_admin_context() hash_ring_db.cleanup_old_nodes(context, days=5) + @has_lock_periodic(spacing=86400, run_immediately=True) + def configure_nb_global(self): + """Configure Northbound OVN NB_Global options + + The method goes over all config options from ovn_nb_global config + sections and configures same key/value pairs to the NB_Global:options + column. + """ + options = {opt.name: str(cfg.CONF.ovn_nb_global.get(opt.name)).lower() + for opt in ovn_conf.nb_global_opts} + + self._nb_idl.set_nb_global_options(**options).execute( + check_error=True) + + raise periodics.NeverAgain() + class HashRingHealthCheckPeriodics(object): diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py index 33d30522bdc..20129fafb99 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py @@ -1032,6 +1032,39 @@ class TestMaintenance(_TestMaintenanceHelper): # "Chassis_Private" register was missing. self.assertEqual(2, len(chassis_result)) + def test_configure_nb_global(self): + def options_intersect(options1, options2): + return bool(set( + new_nb_global_options.keys()).intersection(nb_options.keys())) + + new_nb_global_options = { + 'foo': 'bar', + 'baz': 'qux', + } + + cfg_nb_global_options = [ + ovn_config.cfg.StrOpt(key) for key in new_nb_global_options + ] + + def get_opt(key): + return new_nb_global_options[key] + + nb_options = self.nb_api.db_get('NB_Global', '.', 'options').execute( + check_error=True, log_errors=True) + self.assertFalse(options_intersect(new_nb_global_options, nb_options)) + + with mock.patch.object( + ovn_config, 'nb_global_opts', cfg_nb_global_options), \ + mock.patch.object( + ovn_config.cfg.CONF.ovn_nb_global, 'get', + side_effect=get_opt): + self.assertRaises(periodics.NeverAgain, + self.maint.configure_nb_global) + + nb_options = self.nb_api.db_get('NB_Global', '.', 'options').execute( + check_error=True, log_errors=True) + self.assertTrue(options_intersect(new_nb_global_options, nb_options)) + class TestLogMaintenance(_TestMaintenanceHelper, test_log_driver.LogApiTestCaseBase): diff --git a/releasenotes/notes/OVN-not-responding-to-ARP-for-DOWN-ports-3620173f00089539.yaml b/releasenotes/notes/OVN-not-responding-to-ARP-for-DOWN-ports-3620173f00089539.yaml new file mode 100644 index 00000000000..cdb6cbb971b --- /dev/null +++ b/releasenotes/notes/OVN-not-responding-to-ARP-for-DOWN-ports-3620173f00089539.yaml @@ -0,0 +1,11 @@ +--- +upgrade: + - | + Starting with OVN version v21.12.0, OVN replies to ARP requests for ports + that are in a DOWN status. It does not reply in versions older than + v21.12.0. In order to keep the same behavior in Neutron, the default OVN + behavior is overridden by Neutron and Neutron ports will no longer reply to + ARP packets if the ports are in a DOWN state. If it is required to reply to + ARP for such ports, the config option ``ignore_lsp_down`` from + ``[ovn_nb_global]`` section can be set to True in the Neutron config. It is + set to False by default.