Skip to content

Commit 8ddf72d

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[OVN] Disable the mcast_flood_reports option for LSPs" into stable/2023.1
2 parents 6dc93c5 + cd6171a commit 8ddf72d

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)