Skip to content

Commit cd6171a

Browse files
committed
[OVN] Disable the mcast_flood_reports option for LSPs
The mcast_flood_reports option was being enabled on LSPs as a workaround for a problem in core OVN. The issue in core OVN has been fixed and this workaround is now causing an increase in the number of actions on the table 38 of OVN (at the risk of hitting a size limit). This patch disables the mcast_flood_reports option on newer versions of OVN while keeping the backward compatibility with the old ones. Since the fix in core OVN does not expose any information to the CMS to tell us that the issue is fixed this patch uses the NB DB schema version to determine if this is an old or a new OVN version. Change-Id: I8f3f0c2d516e37145eb298b8f51d92fe9905158a Closes-Bug: #2026825 Signed-off-by: Lucas Alvares Gomes <[email protected]> (cherry picked from commit 06dbc52)
1 parent be0cb06 commit cd6171a

File tree

6 files changed

+137
-24
lines changed

6 files changed

+137
-24
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ def schema_helper(cls):
159159
cls.schema)
160160
return cls._schema_helper
161161

162+
@classmethod
163+
def get_schema_version(cls):
164+
return cls.schema_helper.schema_json['version']
165+
162166
@classmethod
163167
def schema_has_table(cls, table_name):
164168
return table_name in cls.schema_helper.schema_json['tables']

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

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ def check_for_ha_chassis_group(self):
619619

620620
raise periodics.NeverAgain()
621621

622-
# TODO(lucasagomes): Remove this in the Z cycle
622+
# TODO(lucasagomes): Remove this in the B+3 cycle
623623
# A static spacing value is used here, but this method will only run
624624
# once per lock due to the use of periodics.NeverAgain().
625625
@periodics.periodic(spacing=600, run_immediately=True)
@@ -630,21 +630,36 @@ def check_for_mcast_flood_reports(self):
630630
cmds = []
631631
for port in self._nb_idl.lsp_list().execute(check_error=True):
632632
port_type = port.type.strip()
633-
if port_type in ("vtep", ovn_const.LSP_TYPE_LOCALPORT, "router"):
634-
continue
635-
636633
options = port.options
637-
if port_type == ovn_const.LSP_TYPE_LOCALNET:
638-
mcast_flood_value = options.get(
639-
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS)
640-
if mcast_flood_value == 'false':
634+
mcast_flood_reports_value = options.get(
635+
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS)
636+
637+
if self._ovn_client.is_mcast_flood_broken:
638+
if port_type in ("vtep", ovn_const.LSP_TYPE_LOCALPORT,
639+
"router"):
641640
continue
642-
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'false'})
643-
elif ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS in options:
644-
continue
645641

646-
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
647-
cmds.append(self._nb_idl.lsp_set_options(port.name, **options))
642+
if port_type == ovn_const.LSP_TYPE_LOCALNET:
643+
mcast_flood_value = options.pop(
644+
ovn_const.LSP_OPTIONS_MCAST_FLOOD, None)
645+
if mcast_flood_value:
646+
cmds.append(self._nb_idl.db_remove(
647+
'Logical_Switch_Port', port.name, 'options',
648+
ovn_const.LSP_OPTIONS_MCAST_FLOOD,
649+
if_exists=True))
650+
651+
if mcast_flood_reports_value == 'true':
652+
continue
653+
654+
options.update(
655+
{ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
656+
cmds.append(self._nb_idl.lsp_set_options(port.name, **options))
657+
658+
elif (mcast_flood_reports_value and port_type !=
659+
ovn_const.LSP_TYPE_LOCALNET):
660+
cmds.append(self._nb_idl.db_remove(
661+
'Logical_Switch_Port', port.name, 'options',
662+
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS, if_exists=True))
648663

649664
if cmds:
650665
with self._nb_idl.transaction(check_error=True) as txn:

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

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from oslo_log import log
4040
from oslo_utils import excutils
4141
from oslo_utils import timeutils
42+
from oslo_utils import versionutils
4243
from ovsdbapp.backend.ovs_idl import idlutils
4344
import tenacity
4445

@@ -96,6 +97,7 @@ def __init__(self, nb_idl, sb_idl):
9697

9798
self._plugin_property = None
9899
self._l3_plugin_property = None
100+
self._is_mcast_flood_broken = None
99101

100102
# TODO(ralonsoh): handle the OVN client extensions with an ext. manager
101103
self._qos_driver = qos_extension.OVNClientQosExtension(driver=self)
@@ -298,6 +300,20 @@ def update_lsp_host_info(self, context, db_port, up=True):
298300

299301
self._transaction(cmd)
300302

303+
# TODO(lucasagomes): Remove this method and the logic around the broken
304+
# mcast_flood_reports configuration option on any other port that is not
305+
# type "localnet" when the fixed version of OVN becomes the norm.
306+
# The commit in core OVN fixing this issue is the
307+
# https://github.com/ovn-org/ovn/commit/6aeeccdf272bc60630581e46aa42d97f4f56d4fa
308+
@property
309+
def is_mcast_flood_broken(self):
310+
if self._is_mcast_flood_broken is None:
311+
schema_version = self._nb_idl.get_schema_version()
312+
self._is_mcast_flood_broken = (
313+
versionutils.convert_version_to_tuple(schema_version) <
314+
(6, 3, 0))
315+
return self._is_mcast_flood_broken
316+
301317
def _get_port_options(self, port):
302318
context = n_context.get_admin_context()
303319
bp_info = utils.validate_and_get_data_from_binding_profile(port)
@@ -439,12 +455,8 @@ def _get_port_options(self, port):
439455
if port_type != ovn_const.LSP_TYPE_VIRTUAL:
440456
options[ovn_const.LSP_OPTIONS_REQUESTED_CHASSIS_KEY] = chassis
441457

442-
# TODO(lucasagomes): Enable the mcast_flood_reports by default,
443-
# according to core OVN developers it shouldn't cause any harm
444-
# and will be ignored when mcast_snoop is False. We can revise
445-
# this once https://bugzilla.redhat.com/show_bug.cgi?id=1933990
446-
# (see comment #3) is fixed in Core OVN.
447-
if port_type not in ('vtep', ovn_const.LSP_TYPE_LOCALPORT, 'router'):
458+
if self.is_mcast_flood_broken and port_type not in (
459+
'vtep', ovn_const.LSP_TYPE_LOCALPORT, 'router'):
448460
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
449461

450462
device_owner = port.get('device_owner', '')

neutron/tests/unit/fake_resources.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ def __init__(self, **kwargs):
164164
self.ha_chassis_group_add_chassis = mock.Mock()
165165
self.ha_chassis_group_del_chassis = mock.Mock()
166166
self.lrp_get = mock.Mock()
167+
self.get_schema_version = mock.Mock(return_value='3.6.0')
167168

168169

169170
class FakeOvsdbSbOvnIdl(object):
@@ -188,6 +189,7 @@ def __init__(self, **kwargs):
188189
self.is_table_present = mock.Mock()
189190
self.is_table_present.return_value = False
190191
self.get_chassis_by_card_serial_from_cms_options = mock.Mock()
192+
self.get_schema_version = mock.Mock(return_value='3.6.0')
191193

192194

193195
class FakeOvsdbTransaction(object):

neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,8 @@ def test_check_port_has_address_scope(self):
537537
"lsp1", external_ids=external_ids
538538
)
539539

540-
def test_check_for_mcast_flood_reports(self):
540+
def test_check_for_mcast_flood_reports_broken(self):
541+
self.fake_ovn_client.is_mcast_flood_broken = True
541542
nb_idl = self.fake_ovn_client._nb_idl
542543
lsp0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
543544
attrs={'name': 'lsp0',
@@ -578,14 +579,86 @@ def test_check_for_mcast_flood_reports(self):
578579
self.assertRaises(periodics.NeverAgain,
579580
self.periodic.check_for_mcast_flood_reports)
580581

581-
# Assert only lsp1, lsp5 and lsp6 were called because they are the
582-
# only ones meeting the criteria
582+
# Assert only lsp1 and lsp5 were called because they are the
583+
# only ones meeting to set mcast_flood_reports to 'true'
583584
expected_calls = [
584585
mock.call('lsp1', mcast_flood_reports='true'),
585-
mock.call('lsp5', mcast_flood_reports='true', mcast_flood='false'),
586-
mock.call('lsp6', mcast_flood_reports='true', mcast_flood='false')]
586+
mock.call('lsp5', mcast_flood_reports='true')]
587587

588588
nb_idl.lsp_set_options.assert_has_calls(expected_calls)
589+
self.assertEqual(2, nb_idl.lsp_set_options.call_count)
590+
591+
# Assert only lsp6 and lsp7 were called because they are the
592+
# only ones meeting to remove mcast_flood
593+
expected_calls = [
594+
mock.call('Logical_Switch_Port', 'lsp6', 'options',
595+
constants.LSP_OPTIONS_MCAST_FLOOD,
596+
if_exists=True),
597+
mock.call('Logical_Switch_Port', 'lsp7', 'options',
598+
constants.LSP_OPTIONS_MCAST_FLOOD,
599+
if_exists=True)]
600+
601+
nb_idl.db_remove.assert_has_calls(expected_calls)
602+
self.assertEqual(2, nb_idl.db_remove.call_count)
603+
604+
def test_check_for_mcast_flood_reports(self):
605+
self.fake_ovn_client.is_mcast_flood_broken = False
606+
nb_idl = self.fake_ovn_client._nb_idl
607+
608+
lsp0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
609+
attrs={'name': 'lsp0',
610+
'options': {
611+
constants.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'},
612+
'type': ""})
613+
lsp1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
614+
attrs={'name': 'lsp1', 'options': {}, 'type': ""})
615+
lsp2 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
616+
attrs={'name': 'lsp2',
617+
'options': {
618+
constants.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'},
619+
'type': "vtep"})
620+
lsp3 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
621+
attrs={'name': 'lsp3', 'options': {},
622+
'type': constants.LSP_TYPE_LOCALPORT})
623+
lsp4 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
624+
attrs={'name': 'lsp4', 'options': {},
625+
'type': "router"})
626+
lsp5 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
627+
attrs={'name': 'lsp5', 'options': {},
628+
'type': constants.LSP_TYPE_LOCALNET})
629+
lsp6 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
630+
attrs={'name': 'lsp6',
631+
'options': {
632+
constants.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
633+
constants.LSP_OPTIONS_MCAST_FLOOD: 'true'},
634+
'type': constants.LSP_TYPE_LOCALNET})
635+
lsp7 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
636+
attrs={'name': 'lsp7',
637+
'options': {
638+
constants.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
639+
constants.LSP_OPTIONS_MCAST_FLOOD: 'false'},
640+
'type': constants.LSP_TYPE_LOCALNET})
641+
642+
nb_idl.lsp_list.return_value.execute.return_value = [
643+
lsp0, lsp1, lsp2, lsp3, lsp4, lsp5, lsp6, lsp7]
644+
645+
# Invoke the periodic method, it meant to run only once at startup
646+
# so NeverAgain will be raised at the end
647+
self.assertRaises(periodics.NeverAgain,
648+
self.periodic.check_for_mcast_flood_reports)
649+
650+
# Assert only lsp0 and lsp2 were called because they are the
651+
# only ones meeting the criteria
652+
expected_calls = [
653+
mock.call('Logical_Switch_Port', 'lsp0', 'options',
654+
constants.LSP_OPTIONS_MCAST_FLOOD_REPORTS,
655+
if_exists=True),
656+
mock.call('Logical_Switch_Port', 'lsp2', 'options',
657+
constants.LSP_OPTIONS_MCAST_FLOOD_REPORTS,
658+
if_exists=True)]
659+
660+
nb_idl.db_remove.assert_has_calls(expected_calls)
661+
self.assertEqual(2, nb_idl.db_remove.call_count)
589662

590663
def test_check_router_mac_binding_options(self):
591664
nb_idl = self.fake_ovn_client._nb_idl
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
For OVN versions v22.09.0 and above, the ``mcast_flood_reports`` option
5+
is now set to ``false`` on all ports except "localnet" types. In the past,
6+
this option was set to ``true`` as a workaround for a bug in core OVN
7+
multicast implementation.

0 commit comments

Comments
 (0)