Skip to content

Commit 5e83f84

Browse files
Zuulopenstack-gerrit
authored andcommitted
Merge "[OVN] Disable the mcast_flood_reports option for LSPs" into stable/zed
2 parents 6a9990d + 7da91ba commit 5e83f84

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)