Skip to content

Commit 7da91ba

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 d6ee668 commit 7da91ba

File tree

6 files changed

+136
-23
lines changed

6 files changed

+136
-23
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: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ def check_for_ha_chassis_group(self):
617617

618618
raise periodics.NeverAgain()
619619

620-
# TODO(lucasagomes): Remove this in the Z cycle
620+
# TODO(lucasagomes): Remove this in the B+3 cycle
621621
# A static spacing value is used here, but this method will only run
622622
# once per lock due to the use of periodics.NeverAgain().
623623
@periodics.periodic(spacing=600, run_immediately=True)
@@ -628,21 +628,36 @@ def check_for_mcast_flood_reports(self):
628628
cmds = []
629629
for port in self._nb_idl.lsp_list().execute(check_error=True):
630630
port_type = port.type.strip()
631-
if port_type in ("vtep", ovn_const.LSP_TYPE_LOCALPORT, "router"):
632-
continue
633-
634631
options = port.options
635-
if port_type == ovn_const.LSP_TYPE_LOCALNET:
636-
mcast_flood_value = options.get(
632+
mcast_flood_reports_value = options.get(
637633
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS)
638-
if mcast_flood_value == 'false':
634+
635+
if self._ovn_client.is_mcast_flood_broken:
636+
if port_type in ("vtep", ovn_const.LSP_TYPE_LOCALPORT,
637+
"router"):
639638
continue
640-
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'false'})
641-
elif ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS in options:
642-
continue
643639

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

647662
if cmds:
648663
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

@@ -94,6 +95,7 @@ def __init__(self, nb_idl, sb_idl):
9495

9596
self._plugin_property = None
9697
self._l3_plugin_property = None
98+
self._is_mcast_flood_broken = None
9799

98100
# TODO(ralonsoh): handle the OVN client extensions with an ext. manager
99101
self._qos_driver = qos_extension.OVNClientQosExtension(driver=self)
@@ -338,6 +340,20 @@ def update_lsp_host_info(self, context, db_port, up=True):
338340

339341
self._transaction(cmd)
340342

343+
# TODO(lucasagomes): Remove this method and the logic around the broken
344+
# mcast_flood_reports configuration option on any other port that is not
345+
# type "localnet" when the fixed version of OVN becomes the norm.
346+
# The commit in core OVN fixing this issue is the
347+
# https://github.com/ovn-org/ovn/commit/6aeeccdf272bc60630581e46aa42d97f4f56d4fa
348+
@property
349+
def is_mcast_flood_broken(self):
350+
if self._is_mcast_flood_broken is None:
351+
schema_version = self._nb_idl.get_schema_version()
352+
self._is_mcast_flood_broken = (
353+
versionutils.convert_version_to_tuple(schema_version) <
354+
(6, 3, 0))
355+
return self._is_mcast_flood_broken
356+
341357
def _get_port_options(self, port):
342358
context = n_context.get_admin_context()
343359
binding_prof = utils.validate_and_get_data_from_binding_profile(port)
@@ -500,12 +516,8 @@ def _get_port_options(self, port):
500516
if port_type != ovn_const.LSP_TYPE_VIRTUAL:
501517
options[ovn_const.LSP_OPTIONS_REQUESTED_CHASSIS_KEY] = chassis
502518

503-
# TODO(lucasagomes): Enable the mcast_flood_reports by default,
504-
# according to core OVN developers it shouldn't cause any harm
505-
# and will be ignored when mcast_snoop is False. We can revise
506-
# this once https://bugzilla.redhat.com/show_bug.cgi?id=1933990
507-
# (see comment #3) is fixed in Core OVN.
508-
if port_type not in ('vtep', ovn_const.LSP_TYPE_LOCALPORT, 'router'):
519+
if self.is_mcast_flood_broken and port_type not in (
520+
'vtep', ovn_const.LSP_TYPE_LOCALPORT, 'router'):
509521
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
510522

511523
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
@@ -522,7 +522,8 @@ def test_check_port_has_address_scope(self):
522522
"lsp1", external_ids=external_ids
523523
)
524524

525-
def test_check_for_mcast_flood_reports(self):
525+
def test_check_for_mcast_flood_reports_broken(self):
526+
self.fake_ovn_client.is_mcast_flood_broken = True
526527
nb_idl = self.fake_ovn_client._nb_idl
527528
lsp0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
528529
attrs={'name': 'lsp0',
@@ -563,14 +564,86 @@ def test_check_for_mcast_flood_reports(self):
563564
self.assertRaises(periodics.NeverAgain,
564565
self.periodic.check_for_mcast_flood_reports)
565566

566-
# Assert only lsp1, lsp5 and lsp6 were called because they are the
567-
# only ones meeting the criteria
567+
# Assert only lsp1 and lsp5 were called because they are the
568+
# only ones meeting to set mcast_flood_reports to 'true'
568569
expected_calls = [
569570
mock.call('lsp1', mcast_flood_reports='true'),
570-
mock.call('lsp5', mcast_flood_reports='true', mcast_flood='false'),
571-
mock.call('lsp6', mcast_flood_reports='true', mcast_flood='false')]
571+
mock.call('lsp5', mcast_flood_reports='true')]
572572

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

575648
def test_check_router_mac_binding_options(self):
576649
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)