Merge "Fix IGMP inconsistency across drivers"
This commit is contained in:
commit
ef3089547b
|
@ -36,8 +36,7 @@ OVN Database information
|
||||||
|
|
||||||
The ``igmp_snooping_enable`` configuration from Neutron is translated
|
The ``igmp_snooping_enable`` configuration from Neutron is translated
|
||||||
into the ``mcast_snoop`` option set in the ``other_config`` column
|
into the ``mcast_snoop`` option set in the ``other_config`` column
|
||||||
from the ``Logical_Switch`` table in the OVN Northbound Database
|
from the ``Logical_Switch`` table in the OVN Northbound Database:
|
||||||
(``mcast_flood_unregistered`` is always "false"):
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
|
@ -71,7 +70,6 @@ command below (populated only when igmp_snooping_enable is True):
|
||||||
groups and broadcast all the multicast traffic. This behavior can
|
groups and broadcast all the multicast traffic. This behavior can
|
||||||
impact when updating/upgrading the OVN services.
|
impact when updating/upgrading the OVN services.
|
||||||
|
|
||||||
|
|
||||||
Extra information
|
Extra information
|
||||||
~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -92,5 +90,11 @@ The permutations from different configurations are:
|
||||||
* With IGMP snooping enabled and multicast group address **is in**
|
* With IGMP snooping enabled and multicast group address **is in**
|
||||||
the 224.0.0.X range: IP Multicast traffic **is** flooded.
|
the 224.0.0.X range: IP Multicast traffic **is** flooded.
|
||||||
|
|
||||||
|
* Apart from the ``igmp_snooping_enable`` configuration option mentioned
|
||||||
|
before, there are 3 other configuration options supported by the OVN
|
||||||
|
driver: ``igmp_flood``, ``igmp_flood_reports`` and
|
||||||
|
``igmp_flood_unregistered``. Check the :ref:`ML2 configuration
|
||||||
|
reference page <ML2_CONF>` for more information.
|
||||||
|
|
||||||
|
|
||||||
.. _`RFC 4541 session 2.1.2`: https://tools.ietf.org/html/rfc4541
|
.. _`RFC 4541 session 2.1.2`: https://tools.ietf.org/html/rfc4541
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
.. _ML2_CONF:
|
||||||
|
|
||||||
============
|
============
|
||||||
ml2_conf.ini
|
ml2_conf.ini
|
||||||
============
|
============
|
||||||
|
|
|
@ -334,8 +334,14 @@ class OVSBridge(BaseOVS):
|
||||||
|
|
||||||
def set_igmp_snooping_state(self, state):
|
def set_igmp_snooping_state(self, state):
|
||||||
state = bool(state)
|
state = bool(state)
|
||||||
|
# NOTE(lucasagomes): The mcast-snooping-disable-flood-unregistered
|
||||||
|
# has the opposite value of the config in Neutron. That's because
|
||||||
|
# IGMP Neutron configs are more value consistent using True to
|
||||||
|
# enable a feature and False to disable it.
|
||||||
|
flood_value = ('false' if
|
||||||
|
cfg.CONF.OVS.igmp_flood_unregistered else 'true')
|
||||||
other_config = {
|
other_config = {
|
||||||
'mcast-snooping-disable-flood-unregistered': 'false'}
|
'mcast-snooping-disable-flood-unregistered': flood_value}
|
||||||
with self.ovsdb.transaction() as txn:
|
with self.ovsdb.transaction() as txn:
|
||||||
txn.add(
|
txn.add(
|
||||||
self.ovsdb.db_set('Bridge', self.br_name,
|
self.ovsdb.db_set('Bridge', self.br_name,
|
||||||
|
@ -344,11 +350,10 @@ class OVSBridge(BaseOVS):
|
||||||
self.ovsdb.db_set('Bridge', self.br_name,
|
self.ovsdb.db_set('Bridge', self.br_name,
|
||||||
('other_config', other_config)))
|
('other_config', other_config)))
|
||||||
|
|
||||||
def set_igmp_snooping_flood(self, port_name, state):
|
def set_igmp_snooping_flood(self, port_name):
|
||||||
state = str(state)
|
|
||||||
other_config = {
|
other_config = {
|
||||||
'mcast-snooping-flood-reports': state,
|
'mcast-snooping-flood-reports': ovs_conf.get_igmp_flood_reports(),
|
||||||
'mcast-snooping-flood': state}
|
'mcast-snooping-flood': ovs_conf.get_igmp_flood()}
|
||||||
self.ovsdb.db_set(
|
self.ovsdb.db_set(
|
||||||
'Port', port_name,
|
'Port', port_name,
|
||||||
('other_config', other_config)).execute(
|
('other_config', other_config)).execute(
|
||||||
|
|
|
@ -229,6 +229,8 @@ class CoreChecks(base.BaseChecks):
|
||||||
self.extra_dhcp_options_check),
|
self.extra_dhcp_options_check),
|
||||||
(_('OVN support for BM provisioning over IPv6 check'),
|
(_('OVN support for BM provisioning over IPv6 check'),
|
||||||
self.ovn_for_bm_provisioning_over_ipv6_check),
|
self.ovn_for_bm_provisioning_over_ipv6_check),
|
||||||
|
(_('ML2/OVS IGMP Flood check'),
|
||||||
|
self.ml2_ovs_igmp_flood_check),
|
||||||
]
|
]
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -639,3 +641,31 @@ class CoreChecks(base.BaseChecks):
|
||||||
'c5fd51bd154147a567097eaf61fbebc0b5b39e28 which added '
|
'c5fd51bd154147a567097eaf61fbebc0b5b39e28 which added '
|
||||||
'support for iPXE over IPv6. It is available in '
|
'support for iPXE over IPv6. It is available in '
|
||||||
'OVN >= 23.06.0.'))
|
'OVN >= 23.06.0.'))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ml2_ovs_igmp_flood_check(checker):
|
||||||
|
"""Check for IGMP related traffic behavior changes for ML2/OVS
|
||||||
|
|
||||||
|
Since LP#2044272, the default behavior of IGMP related traffic has
|
||||||
|
changed for the ML2/OVS driver. This check raises a warning and
|
||||||
|
instruct the user how to configure IGMP to keep the same behavior
|
||||||
|
as prior to the upgrade.
|
||||||
|
"""
|
||||||
|
# NOTE(lucasagomes): igmp_flood_reports is not checked as part
|
||||||
|
# of this function because its default is already True.
|
||||||
|
if ('ovn' not in cfg.CONF.ml2.mechanism_drivers and
|
||||||
|
cfg.CONF.OVS.igmp_snooping_enable and
|
||||||
|
not cfg.CONF.OVS.igmp_flood_unregistered and
|
||||||
|
not cfg.CONF.OVS.igmp_flood):
|
||||||
|
return upgradecheck.Result(
|
||||||
|
upgradecheck.Code.WARNING,
|
||||||
|
_('For non-ML2/OVN deployments where ``igmp_snooping_enable`` '
|
||||||
|
'is enabled, the default behavior of IGMP related traffic '
|
||||||
|
'has changed after LP#2044272. To keep the same behavior '
|
||||||
|
'as before please ensure that the configuration options: '
|
||||||
|
'``igmp_flood_unregistered`` and ``igmp_flood`` are also '
|
||||||
|
'enabled in the [OVS] section of the configuration file.'))
|
||||||
|
|
||||||
|
return upgradecheck.Result(
|
||||||
|
upgradecheck.Code.SUCCESS,
|
||||||
|
_('IGMP related traffic configuration is not affected.'))
|
||||||
|
|
|
@ -37,15 +37,40 @@ OPTS = [
|
||||||
help=_('Enable IGMP snooping for integration bridge. If this '
|
help=_('Enable IGMP snooping for integration bridge. If this '
|
||||||
'option is set to True, support for Internet Group '
|
'option is set to True, support for Internet Group '
|
||||||
'Management Protocol (IGMP) is enabled in integration '
|
'Management Protocol (IGMP) is enabled in integration '
|
||||||
'bridge. '
|
'bridge.')),
|
||||||
'Setting this option to True will also enable the Open '
|
cfg.BoolOpt('igmp_flood', default=False,
|
||||||
'vSwitch mcast-snooping-disable-flood-unregistered '
|
help=_('Multicast packets (except reports) are '
|
||||||
'flag. This option will disable flooding of '
|
'unconditionally forwarded to the ports bridging a '
|
||||||
|
'logical network to a physical network.')),
|
||||||
|
cfg.BoolOpt('igmp_flood_reports', default=True,
|
||||||
|
help=_('Multicast reports are unconditionally forwarded '
|
||||||
|
'to the ports bridging a logical network to a '
|
||||||
|
'physical network.')),
|
||||||
|
cfg.BoolOpt('igmp_flood_unregistered', default=False,
|
||||||
|
help=_('This option enables or disables flooding of '
|
||||||
'unregistered multicast packets to all ports. '
|
'unregistered multicast packets to all ports. '
|
||||||
'The switch will send unregistered multicast packets '
|
'If False, The switch will send unregistered '
|
||||||
'only to ports connected to multicast routers.')),
|
'multicast packets only to ports connected to '
|
||||||
|
'multicast routers.')),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def register_ovs_agent_opts(cfg=cfg.CONF):
|
def register_ovs_agent_opts(cfg=cfg.CONF):
|
||||||
cfg.register_opts(OPTS, 'OVS')
|
cfg.register_opts(OPTS, 'OVS')
|
||||||
|
|
||||||
|
|
||||||
|
def get_igmp_snooping_enabled():
|
||||||
|
return str(cfg.CONF.OVS.igmp_snooping_enable).lower()
|
||||||
|
|
||||||
|
|
||||||
|
def get_igmp_flood():
|
||||||
|
return str(cfg.CONF.OVS.igmp_flood).lower()
|
||||||
|
|
||||||
|
|
||||||
|
def get_igmp_flood_reports():
|
||||||
|
return str(cfg.CONF.OVS.igmp_flood_reports).lower()
|
||||||
|
|
||||||
|
|
||||||
|
def get_igmp_flood_unregistered():
|
||||||
|
return str(cfg.CONF.OVS.igmp_flood_unregistered).lower()
|
||||||
|
|
|
@ -370,10 +370,6 @@ def is_ovn_emit_need_to_frag_enabled():
|
||||||
return cfg.CONF.ovn.ovn_emit_need_to_frag
|
return cfg.CONF.ovn.ovn_emit_need_to_frag
|
||||||
|
|
||||||
|
|
||||||
def is_igmp_snooping_enabled():
|
|
||||||
return cfg.CONF.OVS.igmp_snooping_enable
|
|
||||||
|
|
||||||
|
|
||||||
def is_ovn_dhcp_disabled_for_baremetal():
|
def is_ovn_dhcp_disabled_for_baremetal():
|
||||||
return cfg.CONF.ovn.disable_ovn_dhcp_for_baremetal_ports
|
return cfg.CONF.ovn.disable_ovn_dhcp_for_baremetal_ports
|
||||||
|
|
||||||
|
|
|
@ -1558,9 +1558,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
|
||||||
"version of OVS does not support tunnels or patch "
|
"version of OVS does not support tunnels or patch "
|
||||||
"ports. Agent terminated!")
|
"ports. Agent terminated!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
self.int_br.set_igmp_snooping_flood(
|
self.int_br.set_igmp_snooping_flood(self.conf.OVS.int_peer_patch_port)
|
||||||
self.conf.OVS.int_peer_patch_port,
|
|
||||||
self.conf.OVS.igmp_snooping_enable)
|
|
||||||
if self.conf.AGENT.drop_flows_on_start:
|
if self.conf.AGENT.drop_flows_on_start:
|
||||||
self.tun_br.uninstall_flows(cookie=ovs_lib.COOKIE_ANY)
|
self.tun_br.uninstall_flows(cookie=ovs_lib.COOKIE_ANY)
|
||||||
|
|
||||||
|
@ -1691,8 +1689,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
|
||||||
else:
|
else:
|
||||||
int_ofport = self.int_br.add_patch_port(
|
int_ofport = self.int_br.add_patch_port(
|
||||||
int_if_name, ovs_const.NONEXISTENT_PEER)
|
int_if_name, ovs_const.NONEXISTENT_PEER)
|
||||||
self.int_br.set_igmp_snooping_flood(
|
self.int_br.set_igmp_snooping_flood(int_if_name)
|
||||||
int_if_name, self.conf.OVS.igmp_snooping_enable)
|
|
||||||
if br.port_exists(phys_if_name):
|
if br.port_exists(phys_if_name):
|
||||||
phys_ofport = br.get_port_ofport(phys_if_name)
|
phys_ofport = br.get_port_ofport(phys_if_name)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -34,6 +34,7 @@ from ovsdbapp.backend.ovs_idl import event as row_event
|
||||||
|
|
||||||
from neutron.common.ovn import constants as ovn_const
|
from neutron.common.ovn import constants as ovn_const
|
||||||
from neutron.common.ovn import utils
|
from neutron.common.ovn import utils
|
||||||
|
from neutron.conf.agent import ovs_conf
|
||||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||||
from neutron.db import l3_attrs_db
|
from neutron.db import l3_attrs_db
|
||||||
from neutron.db import ovn_hash_ring_db as hash_ring_db
|
from neutron.db import ovn_hash_ring_db as hash_ring_db
|
||||||
|
@ -471,18 +472,28 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
||||||
# once per lock due to the use of periodics.NeverAgain().
|
# once per lock due to the use of periodics.NeverAgain().
|
||||||
@has_lock_periodic(spacing=600, run_immediately=True)
|
@has_lock_periodic(spacing=600, run_immediately=True)
|
||||||
def check_for_igmp_snoop_support(self):
|
def check_for_igmp_snoop_support(self):
|
||||||
with self._nb_idl.transaction(check_error=True) as txn:
|
snooping_conf = ovs_conf.get_igmp_snooping_enabled()
|
||||||
value = ('true' if ovn_conf.is_igmp_snooping_enabled()
|
flood_conf = ovs_conf.get_igmp_flood_unregistered()
|
||||||
else 'false')
|
|
||||||
for ls in self._nb_idl.ls_list().execute(check_error=True):
|
cmds = []
|
||||||
if (ls.other_config.get(ovn_const.MCAST_SNOOP,
|
for ls in self._nb_idl.ls_list().execute(check_error=True):
|
||||||
None) == value or not ls.name):
|
snooping = ls.other_config.get(ovn_const.MCAST_SNOOP)
|
||||||
continue
|
flood = ls.other_config.get(ovn_const.MCAST_FLOOD_UNREGISTERED)
|
||||||
txn.add(self._nb_idl.db_set(
|
|
||||||
|
if (not ls.name or (snooping == snooping_conf and
|
||||||
|
flood == flood_conf)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
cmds.append(self._nb_idl.db_set(
|
||||||
'Logical_Switch', ls.name,
|
'Logical_Switch', ls.name,
|
||||||
('other_config', {
|
('other_config', {
|
||||||
ovn_const.MCAST_SNOOP: value,
|
ovn_const.MCAST_SNOOP: snooping_conf,
|
||||||
ovn_const.MCAST_FLOOD_UNREGISTERED: 'false'})))
|
ovn_const.MCAST_FLOOD_UNREGISTERED: flood_conf})))
|
||||||
|
|
||||||
|
if cmds:
|
||||||
|
with self._nb_idl.transaction(check_error=True) as txn:
|
||||||
|
for cmd in cmds:
|
||||||
|
txn.add(cmd)
|
||||||
|
|
||||||
raise periodics.NeverAgain()
|
raise periodics.NeverAgain()
|
||||||
|
|
||||||
|
@ -557,12 +568,16 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
||||||
# once per lock due to the use of periodics.NeverAgain().
|
# once per lock due to the use of periodics.NeverAgain().
|
||||||
@has_lock_periodic(spacing=600, run_immediately=True)
|
@has_lock_periodic(spacing=600, run_immediately=True)
|
||||||
def check_for_mcast_flood_reports(self):
|
def check_for_mcast_flood_reports(self):
|
||||||
|
mcast_flood_conf = ovs_conf.get_igmp_flood()
|
||||||
|
mcast_flood_reports_conf = ovs_conf.get_igmp_flood_reports()
|
||||||
cmds = []
|
cmds = []
|
||||||
for port in self._nb_idl.lsp_list().execute(check_error=True):
|
for port in self._nb_idl.lsp_list().execute(check_error=True):
|
||||||
port_type = port.type.strip()
|
port_type = port.type.strip()
|
||||||
options = port.options
|
options = port.options
|
||||||
mcast_flood_reports_value = options.get(
|
mcast_flood_reports_value = options.get(
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS)
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS)
|
||||||
|
mcast_flood_value = options.get(
|
||||||
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD)
|
||||||
|
|
||||||
if self._ovn_client.is_mcast_flood_broken:
|
if self._ovn_client.is_mcast_flood_broken:
|
||||||
if port_type in ("vtep", ovn_const.LSP_TYPE_LOCALPORT,
|
if port_type in ("vtep", ovn_const.LSP_TYPE_LOCALPORT,
|
||||||
|
@ -591,6 +606,15 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
|
||||||
'Logical_Switch_Port', port.name, 'options',
|
'Logical_Switch_Port', port.name, 'options',
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS, if_exists=True))
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS, if_exists=True))
|
||||||
|
|
||||||
|
elif (port_type == ovn_const.LSP_TYPE_LOCALNET and (
|
||||||
|
mcast_flood_conf != mcast_flood_value or
|
||||||
|
mcast_flood_reports_conf != mcast_flood_reports_value)):
|
||||||
|
options.update({
|
||||||
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD: mcast_flood_conf,
|
||||||
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS:
|
||||||
|
mcast_flood_reports_conf})
|
||||||
|
cmds.append(self._nb_idl.lsp_set_options(port.name, **options))
|
||||||
|
|
||||||
if cmds:
|
if cmds:
|
||||||
with self._nb_idl.transaction(check_error=True) as txn:
|
with self._nb_idl.transaction(check_error=True) as txn:
|
||||||
for cmd in cmds:
|
for cmd in cmds:
|
||||||
|
|
|
@ -47,6 +47,7 @@ from neutron.common.ovn import acl as ovn_acl
|
||||||
from neutron.common.ovn import constants as ovn_const
|
from neutron.common.ovn import constants as ovn_const
|
||||||
from neutron.common.ovn import utils
|
from neutron.common.ovn import utils
|
||||||
from neutron.common import utils as common_utils
|
from neutron.common import utils as common_utils
|
||||||
|
from neutron.conf.agent import ovs_conf
|
||||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||||
from neutron.db import ovn_revision_numbers_db as db_rev
|
from neutron.db import ovn_revision_numbers_db as db_rev
|
||||||
from neutron.db import segments_db
|
from neutron.db import segments_db
|
||||||
|
@ -1889,10 +1890,13 @@ class OVNClient(object):
|
||||||
physnet = segment.get(segment_def.PHYSICAL_NETWORK)
|
physnet = segment.get(segment_def.PHYSICAL_NETWORK)
|
||||||
fdb_enabled = ('true' if ovn_conf.is_learn_fdb_enabled()
|
fdb_enabled = ('true' if ovn_conf.is_learn_fdb_enabled()
|
||||||
else 'false')
|
else 'false')
|
||||||
options = {'network_name': physnet,
|
options = {
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
|
'network_name': physnet,
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'false',
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS:
|
||||||
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: fdb_enabled}
|
ovs_conf.get_igmp_flood_reports(),
|
||||||
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD:
|
||||||
|
ovs_conf.get_igmp_flood(),
|
||||||
|
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: fdb_enabled}
|
||||||
cmd = self._nb_idl.create_lswitch_port(
|
cmd = self._nb_idl.create_lswitch_port(
|
||||||
lport_name=utils.ovn_provnet_port_name(segment['id']),
|
lport_name=utils.ovn_provnet_port_name(segment['id']),
|
||||||
lswitch_name=utils.ovn_name(network_id),
|
lswitch_name=utils.ovn_name(network_id),
|
||||||
|
@ -1920,12 +1924,14 @@ class OVNClient(object):
|
||||||
','.join(common_utils.get_az_hints(network))}}
|
','.join(common_utils.get_az_hints(network))}}
|
||||||
|
|
||||||
# Enable IGMP snooping if igmp_snooping_enable is enabled in Neutron
|
# Enable IGMP snooping if igmp_snooping_enable is enabled in Neutron
|
||||||
value = 'true' if ovn_conf.is_igmp_snooping_enabled() else 'false'
|
|
||||||
vlan_transparent = (
|
vlan_transparent = (
|
||||||
'true' if network.get('vlan_transparent') else 'false')
|
'true' if network.get('vlan_transparent') else 'false')
|
||||||
params['other_config'] = {ovn_const.MCAST_SNOOP: value,
|
params['other_config'] = {
|
||||||
ovn_const.MCAST_FLOOD_UNREGISTERED: 'false',
|
ovn_const.MCAST_SNOOP:
|
||||||
ovn_const.VLAN_PASSTHRU: vlan_transparent}
|
ovs_conf.get_igmp_snooping_enabled(),
|
||||||
|
ovn_const.MCAST_FLOOD_UNREGISTERED:
|
||||||
|
ovs_conf.get_igmp_flood_unregistered(),
|
||||||
|
ovn_const.VLAN_PASSTHRU: vlan_transparent}
|
||||||
if utils.is_provider_network(network):
|
if utils.is_provider_network(network):
|
||||||
params['other_config'][ovn_const.LS_OPTIONS_FDB_AGE_THRESHOLD] = (
|
params['other_config'][ovn_const.LS_OPTIONS_FDB_AGE_THRESHOLD] = (
|
||||||
ovn_conf.get_fdb_age_threshold())
|
ovn_conf.get_fdb_age_threshold())
|
||||||
|
|
|
@ -19,6 +19,7 @@ from unittest import mock
|
||||||
from neutron_lib import constants as p_const
|
from neutron_lib import constants as p_const
|
||||||
from neutron_lib.plugins.ml2 import ovs_constants
|
from neutron_lib.plugins.ml2 import ovs_constants
|
||||||
from neutron_lib.services.qos import constants as qos_constants
|
from neutron_lib.services.qos import constants as qos_constants
|
||||||
|
from oslo_config import cfg
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
from ovsdbapp.backend.ovs_idl import event
|
from ovsdbapp.backend.ovs_idl import event
|
||||||
|
|
||||||
|
@ -638,7 +639,11 @@ class BaseOVSTestCase(base.BaseSudoTestCase):
|
||||||
port_name = 'test_output_port_2'
|
port_name = 'test_output_port_2'
|
||||||
self._create_bridge()
|
self._create_bridge()
|
||||||
self._create_port(port_name)
|
self._create_port(port_name)
|
||||||
self.ovs.set_igmp_snooping_flood(port_name, True)
|
|
||||||
|
# Enable flood
|
||||||
|
cfg.CONF.set_override('igmp_flood', True, group='OVS')
|
||||||
|
cfg.CONF.set_override('igmp_flood_reports', True, group='OVS')
|
||||||
|
self.ovs.set_igmp_snooping_flood(port_name)
|
||||||
ports_other_config = self.ovs.db_get_val('Port', port_name,
|
ports_other_config = self.ovs.db_get_val('Port', port_name,
|
||||||
'other_config')
|
'other_config')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -648,7 +653,10 @@ class BaseOVSTestCase(base.BaseSudoTestCase):
|
||||||
'true',
|
'true',
|
||||||
ports_other_config.get('mcast-snooping-flood-reports', '').lower())
|
ports_other_config.get('mcast-snooping-flood-reports', '').lower())
|
||||||
|
|
||||||
self.ovs.set_igmp_snooping_flood(port_name, False)
|
# Disable flood
|
||||||
|
cfg.CONF.set_override('igmp_flood', False, group='OVS')
|
||||||
|
cfg.CONF.set_override('igmp_flood_reports', False, group='OVS')
|
||||||
|
self.ovs.set_igmp_snooping_flood(port_name)
|
||||||
ports_other_config = self.ovs.db_get_val('Port', port_name,
|
ports_other_config = self.ovs.db_get_val('Port', port_name,
|
||||||
'other_config')
|
'other_config')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|
|
@ -205,8 +205,10 @@ class OVSBridgeTestCase(OVSBridgeTestBase):
|
||||||
br_other_config = self.ovs.ovsdb.db_find(
|
br_other_config = self.ovs.ovsdb.db_find(
|
||||||
'Bridge', ('name', '=', self.br.br_name), columns=['other_config']
|
'Bridge', ('name', '=', self.br.br_name), columns=['other_config']
|
||||||
).execute()[0]['other_config']
|
).execute()[0]['other_config']
|
||||||
|
expected_flood_value = ('false' if
|
||||||
|
cfg.CONF.OVS.igmp_flood_unregistered else 'true')
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
'false',
|
expected_flood_value,
|
||||||
br_other_config.get(
|
br_other_config.get(
|
||||||
'mcast-snooping-disable-flood-unregistered', '').lower())
|
'mcast-snooping-disable-flood-unregistered', '').lower())
|
||||||
|
|
||||||
|
|
|
@ -1684,8 +1684,7 @@ class TestOvsNeutronAgent(object):
|
||||||
'int-br-eth', ovs_constants.NONEXISTENT_PEER),
|
'int-br-eth', ovs_constants.NONEXISTENT_PEER),
|
||||||
]
|
]
|
||||||
expected_calls += [
|
expected_calls += [
|
||||||
mock.call.int_br.set_igmp_snooping_flood(
|
mock.call.int_br.set_igmp_snooping_flood('int-br-eth'),
|
||||||
'int-br-eth', igmp_snooping_enabled),
|
|
||||||
mock.call.phys_br.port_exists('phy-br-eth'),
|
mock.call.phys_br.port_exists('phy-br-eth'),
|
||||||
]
|
]
|
||||||
if port_exists:
|
if port_exists:
|
||||||
|
@ -1778,8 +1777,7 @@ class TestOvsNeutronAgent(object):
|
||||||
'int-br-eth', ovs_constants.NONEXISTENT_PEER),
|
'int-br-eth', ovs_constants.NONEXISTENT_PEER),
|
||||||
]
|
]
|
||||||
expected_calls += [
|
expected_calls += [
|
||||||
mock.call.int_br.set_igmp_snooping_flood(
|
mock.call.int_br.set_igmp_snooping_flood('int-br-eth'),
|
||||||
'int-br-eth', False),
|
|
||||||
mock.call.phys_br.port_exists('phy-br-eth'),
|
mock.call.phys_br.port_exists('phy-br-eth'),
|
||||||
]
|
]
|
||||||
if port_exists:
|
if port_exists:
|
||||||
|
|
|
@ -228,8 +228,7 @@ class TunnelTest(object):
|
||||||
mock.call.port_exists('int-%s' % self.MAP_TUN_BRIDGE),
|
mock.call.port_exists('int-%s' % self.MAP_TUN_BRIDGE),
|
||||||
mock.call.add_patch_port('int-%s' % self.MAP_TUN_BRIDGE,
|
mock.call.add_patch_port('int-%s' % self.MAP_TUN_BRIDGE,
|
||||||
ovs_constants.NONEXISTENT_PEER),
|
ovs_constants.NONEXISTENT_PEER),
|
||||||
mock.call.set_igmp_snooping_flood('int-%s' % self.MAP_TUN_BRIDGE,
|
mock.call.set_igmp_snooping_flood('int-%s' % self.MAP_TUN_BRIDGE),
|
||||||
igmp_snooping),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
self.mock_int_bridge_expected += [
|
self.mock_int_bridge_expected += [
|
||||||
|
@ -259,7 +258,7 @@ class TunnelTest(object):
|
||||||
self.mock_int_bridge_expected += [
|
self.mock_int_bridge_expected += [
|
||||||
mock.call.port_exists('patch-tun'),
|
mock.call.port_exists('patch-tun'),
|
||||||
mock.call.add_patch_port('patch-tun', 'patch-int'),
|
mock.call.add_patch_port('patch-tun', 'patch-int'),
|
||||||
mock.call.set_igmp_snooping_flood('patch-tun', igmp_snooping),
|
mock.call.set_igmp_snooping_flood('patch-tun'),
|
||||||
]
|
]
|
||||||
self.mock_int_bridge_expected += [
|
self.mock_int_bridge_expected += [
|
||||||
mock.call.get_vif_ports((ovs_lib.INVALID_OFPORT,
|
mock.call.get_vif_ports((ovs_lib.INVALID_OFPORT,
|
||||||
|
|
|
@ -52,6 +52,7 @@ from neutron.common.ovn import constants as ovn_const
|
||||||
from neutron.common.ovn import exceptions as ovn_exceptions
|
from neutron.common.ovn import exceptions as ovn_exceptions
|
||||||
from neutron.common.ovn import hash_ring_manager
|
from neutron.common.ovn import hash_ring_manager
|
||||||
from neutron.common.ovn import utils as ovn_utils
|
from neutron.common.ovn import utils as ovn_utils
|
||||||
|
from neutron.conf.agent import ovs_conf
|
||||||
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
from neutron.conf.plugins.ml2.drivers.ovn import ovn_conf
|
||||||
from neutron.db import db_base_plugin_v2
|
from neutron.db import db_base_plugin_v2
|
||||||
from neutron.db import ovn_revision_numbers_db
|
from neutron.db import ovn_revision_numbers_db
|
||||||
|
@ -881,10 +882,13 @@ class TestOVNMechanismDriver(TestOVNMechanismDriverBase):
|
||||||
external_ids={},
|
external_ids={},
|
||||||
lport_name=ovn_utils.ovn_provnet_port_name(segments[0]['id']),
|
lport_name=ovn_utils.ovn_provnet_port_name(segments[0]['id']),
|
||||||
lswitch_name=ovn_utils.ovn_name(net['id']),
|
lswitch_name=ovn_utils.ovn_name(net['id']),
|
||||||
options={'network_name': 'physnet1',
|
options={
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
|
'network_name': 'physnet1',
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'false',
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS:
|
||||||
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: 'false'},
|
ovs_conf.get_igmp_flood_reports(),
|
||||||
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD:
|
||||||
|
ovs_conf.get_igmp_flood(),
|
||||||
|
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: 'false'},
|
||||||
tag=2,
|
tag=2,
|
||||||
type='localnet')
|
type='localnet')
|
||||||
|
|
||||||
|
@ -3190,10 +3194,13 @@ class TestOVNMechanismDriverSegment(MechDriverSetupBase,
|
||||||
external_ids={},
|
external_ids={},
|
||||||
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
|
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
|
||||||
lswitch_name=ovn_utils.ovn_name(net['id']),
|
lswitch_name=ovn_utils.ovn_name(net['id']),
|
||||||
options={'network_name': 'physnet1',
|
options={
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
|
'network_name': 'physnet1',
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'false',
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS:
|
||||||
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: 'false'},
|
ovs_conf.get_igmp_flood_reports(),
|
||||||
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD:
|
||||||
|
ovs_conf.get_igmp_flood(),
|
||||||
|
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: 'false'},
|
||||||
tag=200,
|
tag=200,
|
||||||
type='localnet')
|
type='localnet')
|
||||||
ovn_nb_api.create_lswitch_port.reset_mock()
|
ovn_nb_api.create_lswitch_port.reset_mock()
|
||||||
|
@ -3205,10 +3212,13 @@ class TestOVNMechanismDriverSegment(MechDriverSetupBase,
|
||||||
external_ids={},
|
external_ids={},
|
||||||
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
|
lport_name=ovn_utils.ovn_provnet_port_name(new_segment['id']),
|
||||||
lswitch_name=ovn_utils.ovn_name(net['id']),
|
lswitch_name=ovn_utils.ovn_name(net['id']),
|
||||||
options={'network_name': 'physnet2',
|
options={
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS: 'true',
|
'network_name': 'physnet2',
|
||||||
ovn_const.LSP_OPTIONS_MCAST_FLOOD: 'false',
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD_REPORTS:
|
||||||
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: 'false'},
|
ovs_conf.get_igmp_flood_reports(),
|
||||||
|
ovn_const.LSP_OPTIONS_MCAST_FLOOD:
|
||||||
|
ovs_conf.get_igmp_flood(),
|
||||||
|
ovn_const.LSP_OPTIONS_LOCALNET_LEARN_FDB: 'false'},
|
||||||
tag=300,
|
tag=300,
|
||||||
type='localnet')
|
type='localnet')
|
||||||
segments = segments_db.get_network_segments(
|
segments = segments_db.get_network_segments(
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
---
|
||||||
|
prelude: >
|
||||||
|
The ML2/OVS and ML2/OVN drivers had inconsistencies on how IGMP was
|
||||||
|
configured. Prior to this patch Neutron only exposed a single IGMP
|
||||||
|
configuration option (``igmp_snooping_enabled``) and other features
|
||||||
|
such as flooding IGMP packets, flooding reports and flooding to
|
||||||
|
unregistered ports were hard coded differently in each driver.
|
||||||
|
This patch introduces Neutron configuration options for those listed
|
||||||
|
IGMP features.
|
||||||
|
As part of this work, default values in the IGMP snooping
|
||||||
|
configuration had to be changed for the ML2/OVS backend. Please
|
||||||
|
check in the following sections for more details.
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
New configuration options for IGMP were added in the [OVS] section:
|
||||||
|
``igmp_flood``, ``igmp_flood_reports`` and ``igmp_flood_unregistered``.
|
||||||
|
This gives operators full control on how IGMP should be configured for
|
||||||
|
their deployments.
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
In order to make IGMP configuration consistent across drivers
|
||||||
|
some defaults had to be changed for ML2/OVS. This change did not
|
||||||
|
affect the defaults in the ML2/OVN driver.
|
||||||
|
|
||||||
|
If ML2/OVS is used and ``igmp_snooping_enable`` is enabled, in order
|
||||||
|
to make the IGMP related traffic run as before please ensure that
|
||||||
|
the following configuration options are enabled after the upgrade.
|
||||||
|
|
||||||
|
``[OVS]/igmp_flood_unregistered`` = True
|
||||||
|
``[OVS]/igmp_flood`` = True
|
||||||
|
``[OVS]/igmp_flood_reports`` = True
|
Loading…
Reference in New Issue