[OVN] Set mcast_flood_reports on LSPs

This patch enables the "mcast_flood_reports" and "mcast_flood" (on provnet
ports only) options in the Logical Switch Ports in the OVN driver. Without
these options, the ovn-controller will consume the IGMP queries and won't
send it to the LSP ports, meaning that external IGMP queries will never
arrive to the VMs.

In talks to the core OVN team, it was suggested [0] to enable the
"mcast_flood_reports" option by default in the OVN driver (at least until
fixed in core OVN) as a workaround to this problem. And, to avoid having
to update all ports (which can be many) based on the igmp_snooping_enable
configuration option, we are always setting "mcast_flood_reports" to
"true" in the LSPs. This won't cause any harm (also confirmed by core
OVN developers [0]) since it will be ignored if multicast snoop is
disabled.

[0] https://bugzilla.redhat.com/show_bug.cgi?id=1933990#c3

Closes-Bug: #1918108
Change-Id: I99a60b9af94b8208b5818b035e189822981bb269
Signed-off-by: Lucas Alvares Gomes <lucasagomes@gmail.com>
This commit is contained in:
Lucas Alvares Gomes 2021-03-08 11:44:24 +00:00
parent b7c4a11158
commit b04d64b90f
7 changed files with 97 additions and 8 deletions

View File

@ -274,6 +274,8 @@ LSP_TYPE_VIRTUAL = 'virtual'
LSP_TYPE_EXTERNAL = 'external'
LSP_OPTIONS_VIRTUAL_PARENTS_KEY = 'virtual-parents'
LSP_OPTIONS_VIRTUAL_IP_KEY = 'virtual-ip'
LSP_OPTIONS_MCAST_FLOOD_REPORTS = 'mcast_flood_reports'
LSP_OPTIONS_MCAST_FLOOD = 'mcast_flood'
HA_CHASSIS_GROUP_DEFAULT_NAME = 'default_ha_chassis_group'
HA_CHASSIS_GROUP_HIGHEST_PRIORITY = 32767

View File

@ -662,6 +662,34 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
txn.add(cmd)
raise periodics.NeverAgain()
# TODO(lucasagomes): Remove this in the Y cycle
# A static spacing value is used here, but this method will only run
# once per lock due to the use of periodics.NeverAgain().
@periodics.periodic(spacing=600, run_immediately=True)
def check_for_mcast_flood_reports(self):
cmds = []
for port in self._nb_idl.lsp_list().execute(check_error=True):
port_type = port.type.strip()
if port_type in ("vtep", "localport", "router"):
continue
options = port.options
if ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS in options:
continue
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
if port_type == ovn_const.LSP_TYPE_LOCALNET:
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'})
cmds.append(self._nb_idl.lsp_set_options(port.name, **options))
if cmds:
with self._nb_idl.transaction(check_error=True) as txn:
for cmd in cmds:
txn.add(cmd)
raise periodics.NeverAgain()
class HashRingHealthCheckPeriodics(object):

View File

@ -298,6 +298,14 @@ class OVNClient(object):
options.update({'requested-chassis':
port.get(portbindings.HOST_ID, '')})
# TODO(lucasagomes): Enable the mcast_flood_reports by default,
# according to core OVN developers it shouldn't cause any harm
# and will be ignored when mcast_snoop is False. We can revise
# this once https://bugzilla.redhat.com/show_bug.cgi?id=1933990
# (see comment #3) is fixed in Core OVN.
if port_type not in ('vtep', 'localport', 'router'):
options.update({ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true'})
device_owner = port.get('device_owner', '')
sg_ids = ' '.join(utils.get_lsp_security_groups(port))
return OvnPortInfo(port_type, options, addresses, port_security,
@ -1534,6 +1542,9 @@ class OVNClient(object):
def create_provnet_port(self, network_id, segment, txn=None):
tag = segment.get(segment_def.SEGMENTATION_ID, [])
physnet = segment.get(segment_def.PHYSICAL_NETWORK)
options = {'network_name': physnet,
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'}
cmd = self._nb_idl.create_lswitch_port(
lport_name=utils.ovn_provnet_port_name(segment['id']),
lswitch_name=utils.ovn_name(network_id),
@ -1541,7 +1552,7 @@ class OVNClient(object):
external_ids={},
type=ovn_const.LSP_TYPE_LOCALNET,
tag=tag,
options={'network_name': physnet})
options=options)
self._transaction([cmd], txn=txn)
def delete_provnet_port(self, network_id, segment):

View File

@ -717,13 +717,11 @@ class TestProvnetPorts(base.TestOVNFunctionalBase):
ovn_localnetport = self._find_port_row_by_name(
utils.ovn_provnet_port_name(seg_db[0]['id']))
self.assertEqual(ovn_localnetport.tag, [100])
self.assertEqual(ovn_localnetport.options,
{'network_name': 'physnet1'})
self.assertEqual(ovn_localnetport.options['network_name'], 'physnet1')
seg_2 = self.create_segment(n1['id'], 'physnet2', '222')
ovn_localnetport = self._find_port_row_by_name(
utils.ovn_provnet_port_name(seg_2['id']))
self.assertEqual(ovn_localnetport.options,
{'network_name': 'physnet2'})
self.assertEqual(ovn_localnetport.options['network_name'], 'physnet2')
self.assertEqual(ovn_localnetport.tag, [222])
# Delete segments and ensure that localnet

View File

@ -407,3 +407,40 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
priority=constants.HA_CHASSIS_GROUP_HIGHEST_PRIORITY - 1)
]
nb_idl.ha_chassis_group_add_chassis.assert_has_calls(expected_calls)
def test_check_for_mcast_flood_reports(self):
nb_idl = self.fake_ovn_client._nb_idl
lsp0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp0',
'options': {'mcast_flood_reports': 'true'},
'type': ""})
lsp1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp1', 'options': {}, 'type': ""})
lsp2 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp2', 'options': {},
'type': "vtep"})
lsp3 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp3', 'options': {},
'type': "localport"})
lsp4 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp4', 'options': {},
'type': "router"})
lsp5 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp5', 'options': {}, 'type': 'localnet'})
nb_idl.lsp_list.return_value.execute.return_value = [
lsp0, lsp1, lsp2, lsp3, lsp4, lsp5]
# Invoke the periodic method, it meant to run only once at startup
# so NeverAgain will be raised at the end
self.assertRaises(periodics.NeverAgain,
self.periodic.check_for_mcast_flood_reports)
# Assert only lsp1 and lsp5 were called because they are the only
# ones meeting the criteria ("mcast_flood_reports" not yet set,
# and type "" or localnet)
expected_calls = [
mock.call('lsp1', mcast_flood_reports='true'),
mock.call('lsp5', mcast_flood_reports='true', mcast_flood='true')]
nb_idl.lsp_set_options.assert_has_calls(expected_calls)

View File

@ -777,7 +777,9 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
external_ids={},
lport_name=ovn_utils.ovn_provnet_port_name(segments[0]['id']),
lswitch_name=ovn_utils.ovn_name(net['id']),
options={'network_name': 'physnet1'},
options={'network_name': 'physnet1',
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'},
tag=2,
type='localnet')
@ -2219,7 +2221,9 @@ class TestOVNMechanismDriverSegment(test_segment.HostSegmentMappingTestCase):
external_ids={},
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
lswitch_name=ovn_utils.ovn_name(net['id']),
options={'network_name': 'phys_net1'},
options={'network_name': 'phys_net1',
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'},
tag=200,
type='localnet')
ovn_nb_api.create_lswitch_port.reset_mock()
@ -2231,7 +2235,9 @@ class TestOVNMechanismDriverSegment(test_segment.HostSegmentMappingTestCase):
external_ids={},
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
lswitch_name=ovn_utils.ovn_name(net['id']),
options={'network_name': 'phys_net2'},
options={'network_name': 'phys_net2',
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'true'},
tag=300,
type='localnet')
segments = segments_db.get_network_segments(

View File

@ -0,0 +1,7 @@
---
fixes:
- |
Fixes a configuration problem in the OVN driver that prevented
external IGMP queries from reaching the Virtual Machines. See
`bug 1918108 <https://bugs.launchpad.net/neutron/+bug/1918108>`_
for details.