Skip to content

Commit b96dc96

Browse files
committed
dvr: Avoid installing non-dvr openflow rule on startup
The tunneling bridge uses different openflow rules depending if the agent is running in DVR mode or not. With DVR enabled initial rule was installed that caused traffic coming from the integration bridge to be flooded to all tunnels. After a few miliseconds this flow was replaced by a DVR specific flow, correctly dropping the traffic. This small time window caused a network loop on the compute node with restarted agent. This patch skips installing the non-dvr specific flow in case OVS agent is working in DVR mode. Hence the traffic is never flooded to the tunnels. Closes-bug: #2028795 Signed-off-by: Jakub Libosvar <[email protected]> Change-Id: I3ce026054286c8e28ec1500f1a4aa607fe73f337 (cherry picked from commit ba6f7bf)
1 parent 2badef9 commit b96dc96

File tree

6 files changed

+69
-19
lines changed

6 files changed

+69
-19
lines changed

neutron/plugins/ml2/drivers/openvswitch/agent/openflow/native/br_tun.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,19 @@ class OVSTunnelBridge(ovs_bridge.OVSAgentBridge,
3333
dvr_process_next_table_id = constants.PATCH_LV_TO_TUN
3434
of_tables = constants.TUN_BR_ALL_TABLES
3535

36-
def setup_default_table(self, patch_int_ofport, arp_responder_enabled):
36+
def setup_default_table(
37+
self, patch_int_ofport, arp_responder_enabled, dvr_enabled):
3738
(dp, ofp, ofpp) = self._get_dp()
3839

39-
# Table 0 (default) will sort incoming traffic depending on in_port
40-
self.install_goto(dest_table_id=constants.PATCH_LV_TO_TUN,
41-
priority=1,
42-
in_port=patch_int_ofport)
40+
if not dvr_enabled:
41+
# Table 0 (default) will sort incoming traffic depending on in_port
42+
# This table is needed only for non-dvr environment because
43+
# OVSDVRProcessMixin overwrites this flow in its
44+
# install_dvr_process() method.
45+
self.install_goto(dest_table_id=constants.PATCH_LV_TO_TUN,
46+
priority=1,
47+
in_port=patch_int_ofport)
48+
4349
self.install_drop() # default drop
4450

4551
if arp_responder_enabled:

neutron/plugins/ml2/drivers/openvswitch/agent/ovs_dvr_neutron_agent.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -264,16 +264,20 @@ def setup_dvr_flows_on_tun_br(self):
264264
if not self.enable_tunneling:
265265
return
266266

267-
self.tun_br.install_goto(dest_table_id=ovs_constants.DVR_PROCESS,
268-
priority=1,
269-
in_port=self.patch_int_ofport)
267+
self._setup_dvr_flows_on_tun_br(self.tun_br, self.patch_int_ofport)
268+
269+
@staticmethod
270+
def _setup_dvr_flows_on_tun_br(tun_br, patch_int_ofport):
271+
tun_br.install_goto(dest_table_id=ovs_constants.DVR_PROCESS,
272+
priority=1,
273+
in_port=patch_int_ofport)
270274

271275
# table-miss should be sent to learning table
272-
self.tun_br.install_goto(table_id=ovs_constants.DVR_NOT_LEARN,
273-
dest_table_id=ovs_constants.LEARN_FROM_TUN)
276+
tun_br.install_goto(table_id=ovs_constants.DVR_NOT_LEARN,
277+
dest_table_id=ovs_constants.LEARN_FROM_TUN)
274278

275-
self.tun_br.install_goto(table_id=ovs_constants.DVR_PROCESS,
276-
dest_table_id=ovs_constants.PATCH_LV_TO_TUN)
279+
tun_br.install_goto(table_id=ovs_constants.DVR_PROCESS,
280+
dest_table_id=ovs_constants.PATCH_LV_TO_TUN)
277281

278282
def setup_dvr_flows_on_phys_br(self, bridge_mappings=None):
279283
'''Setup up initial dvr flows into br-phys'''

neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,8 @@ def setup_tunnel_br_flows(self):
15101510
Add all flows to the tunnel bridge.
15111511
'''
15121512
self.tun_br.setup_default_table(self.patch_int_ofport,
1513-
self.arp_responder_enabled)
1513+
self.arp_responder_enabled,
1514+
self.enable_distributed_routing)
15141515

15151516
def _reconfigure_physical_bridges(self, bridges):
15161517
try:

neutron/tests/functional/agent/test_ovs_flows.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from neutron.agent.linux import ip_lib
2525
from neutron.cmd.sanity import checks
2626
from neutron.common import utils as common_utils
27+
from neutron.plugins.ml2.drivers.openvswitch.agent \
28+
import ovs_dvr_neutron_agent as ovsdvragt
2729
from neutron.plugins.ml2.drivers.openvswitch.agent \
2830
import ovs_neutron_agent as ovsagt
2931
from neutron.tests.common import base as common_base
@@ -299,8 +301,9 @@ class OVSFlowTestCase(OVSAgentTestBase):
299301
"""
300302

301303
def setUp(self):
304+
dvr_enabled = True
302305
cfg.CONF.set_override('enable_distributed_routing',
303-
True,
306+
dvr_enabled,
304307
group='AGENT')
305308
super(OVSFlowTestCase, self).setUp()
306309
self.phys_br = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
@@ -322,7 +325,9 @@ def setUp(self):
322325
prefix=cfg.CONF.OVS.tun_peer_patch_port),
323326
common_utils.get_rand_device_name(
324327
prefix=cfg.CONF.OVS.int_peer_patch_port))
325-
self.br_tun.setup_default_table(self.tun_p, True)
328+
self.br_tun.setup_default_table(self.tun_p, True, dvr_enabled)
329+
ovsdvragt.OVSDVRNeutronAgent._setup_dvr_flows_on_tun_br(self.br_tun,
330+
self.tun_p)
326331

327332
def test_provision_local_vlan(self):
328333
kwargs = {'port': 123, 'lvid': 888, 'segmentation_id': 777}

neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/openflow/native/test_br_tun.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ def test_setup_default_table(self):
5252
patch_int_ofport = 5555
5353
arp_responder_enabled = False
5454
self.br.setup_default_table(patch_int_ofport=patch_int_ofport,
55-
arp_responder_enabled=arp_responder_enabled)
55+
arp_responder_enabled=arp_responder_enabled,
56+
dvr_enabled=False)
5657
(dp, ofp, ofpp) = self._get_dp()
5758
expected = [
5859
call._send_msg(ofpp.OFPFlowMod(dp,
@@ -160,7 +161,8 @@ def test_setup_default_table_arp_responder_enabled(self):
160161
patch_int_ofport = 5555
161162
arp_responder_enabled = True
162163
self.br.setup_default_table(patch_int_ofport=patch_int_ofport,
163-
arp_responder_enabled=arp_responder_enabled)
164+
arp_responder_enabled=arp_responder_enabled,
165+
dvr_enabled=False)
164166
(dp, ofp, ofpp) = self._get_dp()
165167
expected = [
166168
call._send_msg(ofpp.OFPFlowMod(dp,
@@ -280,6 +282,33 @@ def test_setup_default_table_arp_responder_enabled(self):
280282
]
281283
self.assertEqual(expected, self.mock.mock_calls)
282284

285+
def _test_setup_default_table_dvr_helper(self, dvr_enabled):
286+
patch_int_ofport = 5555
287+
arp_responder_enabled = True
288+
self.br.setup_default_table(patch_int_ofport=patch_int_ofport,
289+
arp_responder_enabled=arp_responder_enabled,
290+
dvr_enabled=dvr_enabled)
291+
(dp, ofp, ofpp) = self._get_dp()
292+
non_dvr_specific_call = call._send_msg(
293+
ofpp.OFPFlowMod(
294+
dp,
295+
cookie=self.stamp,
296+
instructions=[ofpp.OFPInstructionGotoTable(table_id=2)],
297+
match=ofpp.OFPMatch(in_port=patch_int_ofport),
298+
priority=1, table_id=0),
299+
active_bundle=None)
300+
301+
if dvr_enabled:
302+
self.assertNotIn(non_dvr_specific_call, self.mock.mock_calls)
303+
else:
304+
self.assertIn(non_dvr_specific_call, self.mock.mock_calls)
305+
306+
def test_setup_default_table_dvr_enabled(self):
307+
self._test_setup_default_table_dvr_helper(dvr_enabled=True)
308+
309+
def test_setup_default_table_dvr_disabled(self):
310+
self._test_setup_default_table_dvr_helper(dvr_enabled=False)
311+
283312
def test_provision_local_vlan(self):
284313
network_type = 'vxlan'
285314
lvid = 888

neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ def lookup_br(br_name, *args, **kwargs):
188188
'_check_bridge_datapath_id').start()
189189
self._define_expected_calls()
190190

191-
def _define_expected_calls(self, arp_responder=False, igmp_snooping=False):
191+
def _define_expected_calls(
192+
self, arp_responder=False, igmp_snooping=False):
192193
self.mock_int_bridge_cls_expected = [
193194
mock.call(self.INT_BRIDGE,
194195
datapath_type=mock.ANY),
@@ -268,7 +269,11 @@ def _define_expected_calls(self, arp_responder=False, igmp_snooping=False):
268269
]
269270

270271
self.mock_tun_bridge_expected += [
271-
mock.call.setup_default_table(self.INT_OFPORT, arp_responder),
272+
# NOTE: Parameters passed to setup_default_table() method are named
273+
# in the production code. That's why we can't use keyword parameter
274+
# here. The last parameter passed below is dvr_enabled set to False
275+
mock.call.setup_default_table(
276+
self.INT_OFPORT, arp_responder, False),
272277
]
273278

274279
self.ipdevice_expected = []

0 commit comments

Comments
 (0)