]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Use patch ports to interconnect integration/physical bridges
authorCedric Brandily <zzelle@gmail.com>
Wed, 26 Feb 2014 23:00:16 +0000 (00:00 +0100)
committerCedric Brandily <zzelle@gmail.com>
Thu, 26 Jun 2014 09:00:04 +0000 (11:00 +0200)
Use by default patch ports to interconnect the integration to
physical bridges for the openvswitch agent in order to increase
performance.

For ovs/kernel without ovs patch port support on kernel side, veths
can be used through the new option OVS.use_veth_interconnection.

Patch port support can be tested using:

    neutron-sanity-check --ovs_patch

DocImpact
Closes-Bug: #1285335
Change-Id: I47106e57bf5b23891d7bf43815959e0d10156fba

etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini
neutron/cmd/sanity_check.py
neutron/plugins/ofagent/agent/ofa_neutron_agent.py
neutron/plugins/openvswitch/agent/ovs_neutron_agent.py
neutron/plugins/openvswitch/common/config.py
neutron/plugins/openvswitch/common/constants.py
neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py
neutron/tests/unit/openvswitch/test_ovs_tunnel.py

index 4beee58fa8ab31df7f5697d3027314760ba0fb9e..5a378d8b20a1dbf65b2decff50f4c4a43c5236da 100644 (file)
 # bridge_mappings =
 # Example: bridge_mappings = physnet1:br-eth1
 
+# (BoolOpt) Use veths instead of patch ports to interconnect the integration
+# bridge to physical networks. Support kernel without ovs patch port support
+# so long as it is set to True.
+# use_veth_interconnection = False
+
 [agent]
 # Agent's polling interval in seconds
 # polling_interval = 2
 # (IntOpt) This is the MTU size of veth interfaces.
 # Do not change unless you have a good reason to.
 # The default MTU size of veth interfaces is 1500.
+# This option has no effect if use_veth_interconnection is False
 # veth_mtu =
 # Example: veth_mtu = 1504
 
index 015be141f5b9e1ca583feb0d1d64a795d064ec8c..e2f7d145a031d34d214e19a8bc728b902b5a35b1 100644 (file)
@@ -23,6 +23,7 @@ from oslo.config import cfg
 
 LOG = logging.getLogger(__name__)
 cfg.CONF.import_group('AGENT', 'neutron.plugins.openvswitch.common.config')
+cfg.CONF.import_group('OVS', 'neutron.plugins.openvswitch.common.config')
 
 
 class BoolOptCallback(cfg.BoolOpt):
@@ -69,6 +70,8 @@ def enable_tests_from_config():
         cfg.CONF.set_override('ovs_vxlan', True)
     if cfg.CONF.AGENT.tunnel_types:
         cfg.CONF.set_override('ovs_patch', True)
+    if not cfg.CONF.OVS.use_veth_interconnection:
+        cfg.CONF.set_override('ovs_patch', True)
 
 
 def all_tests_passed():
index a38e5846182e4c59f6fa37e942ec57565f78e430..0907ea4aa6b0c13eb216017b5d9bafb9bd1fc0ac 100644 (file)
@@ -854,8 +854,8 @@ class OFANeutronAgent(n_rpc.RpcCallback,
 
     def _phys_br_patch_physical_bridge_with_integration_bridge(
             self, br, physical_network, bridge, ip_wrapper):
-        int_veth_name = constants.VETH_INTEGRATION_PREFIX + bridge
-        phys_veth_name = constants.VETH_PHYSICAL_PREFIX + bridge
+        int_veth_name = constants.PEER_INTEGRATION_PREFIX + bridge
+        phys_veth_name = constants.PEER_PHYSICAL_PREFIX + bridge
         self._phys_br_prepare_create_veth(br, int_veth_name, phys_veth_name)
         int_veth, phys_veth = self._phys_br_create_veth(br, int_veth_name,
                                                         phys_veth_name,
index a84c212d0ad326bfb109a20062287ebcb6fbf477..f3e96d95dbfbb156ae4d30141a8fcc902ff75b25 100644 (file)
@@ -110,9 +110,9 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
     tunnels on the tunnel bridge.
 
     For each virtual network realized as a VLAN or flat network, a
-    veth is used to connect the local VLAN on the integration bridge
-    with the physical network bridge, with flow rules adding,
-    modifying, or stripping VLAN tags as necessary.
+    veth or a pair of patch ports is used to connect the local VLAN on
+    the integration bridge with the physical network bridge, with flow
+    rules adding, modifying, or stripping VLAN tags as necessary.
     '''
 
     # history
@@ -127,7 +127,8 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
                  minimize_polling=False,
                  ovsdb_monitor_respawn_interval=(
                      constants.DEFAULT_OVSDBMON_RESPAWN),
-                 arp_responder=False):
+                 arp_responder=False,
+                 use_veth_interconnection=False):
         '''Constructor.
 
         :param integ_br: name of the integration bridge.
@@ -148,8 +149,11 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
                the ovsdb monitor.
         :param arp_responder: Optional, enable local ARP responder if it is
                supported.
+        :param use_veth_interconnection: use veths instead of patch ports to
+               interconnect the integration bridge to physical bridges.
         '''
         super(OVSNeutronAgent, self).__init__()
+        self.use_veth_interconnection = use_veth_interconnection
         self.veth_mtu = veth_mtu
         self.root_helper = root_helper
         self.available_local_vlans = set(moves.xrange(q_const.MIN_VLAN_TAG,
@@ -858,10 +862,11 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
                              priority=0,
                              actions="drop")
 
-    def get_veth_name(self, prefix, name):
-        """Construct a veth name based on the prefix and name that does not
-           exceed the maximum length allowed for a linux device. Longer names
-           are hashed to help ensure uniqueness.
+    def get_peer_name(self, prefix, name):
+        """Construct a peer name based on the prefix and name.
+
+        The peer name can not exceed the maximum length allowed for a linux
+        device. Longer names are hashed to help ensure uniqueness.
         """
         if len(prefix + name) <= q_const.DEVICE_NAME_MAX_LEN:
             return prefix + name
@@ -910,40 +915,55 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
             br.add_flow(priority=1, actions="normal")
             self.phys_brs[physical_network] = br
 
-            # create veth to patch physical bridge with integration bridge
-            int_veth_name = self.get_veth_name(
-                constants.VETH_INTEGRATION_PREFIX, bridge)
-            self.int_br.delete_port(int_veth_name)
-            phys_veth_name = self.get_veth_name(
-                constants.VETH_PHYSICAL_PREFIX, bridge)
-            br.delete_port(phys_veth_name)
-            if ip_lib.device_exists(int_veth_name, self.root_helper):
-                ip_lib.IPDevice(int_veth_name, self.root_helper).link.delete()
-                # Give udev a chance to process its rules here, to avoid
-                # race conditions between commands launched by udev rules
-                # and the subsequent call to ip_wrapper.add_veth
-                utils.execute(['/sbin/udevadm', 'settle', '--timeout=10'])
-            int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name,
-                                                      phys_veth_name)
-            self.int_ofports[physical_network] = self.int_br.add_port(int_veth)
-            self.phys_ofports[physical_network] = br.add_port(phys_veth)
-
-            # block all untranslated traffic over veth between bridges
-            self.int_br.add_flow(priority=2,
-                                 in_port=self.int_ofports[physical_network],
+            # interconnect physical and integration bridges using veth/patchs
+            int_if_name = self.get_peer_name(constants.PEER_INTEGRATION_PREFIX,
+                                             bridge)
+            phys_if_name = self.get_peer_name(constants.PEER_PHYSICAL_PREFIX,
+                                              bridge)
+            self.int_br.delete_port(int_if_name)
+            br.delete_port(phys_if_name)
+            if self.use_veth_interconnection:
+                if ip_lib.device_exists(int_if_name, self.root_helper):
+                    ip_lib.IPDevice(int_if_name,
+                                    self.root_helper).link.delete()
+                    # Give udev a chance to process its rules here, to avoid
+                    # race conditions between commands launched by udev rules
+                    # and the subsequent call to ip_wrapper.add_veth
+                    utils.execute(['/sbin/udevadm', 'settle', '--timeout=10'])
+                int_veth, phys_veth = ip_wrapper.add_veth(int_if_name,
+                                                          phys_if_name)
+                int_ofport = self.int_br.add_port(int_veth)
+                phys_ofport = br.add_port(phys_veth)
+            else:
+                # Create patch ports without associating them in order to block
+                # untranslated traffic before association
+                int_ofport = self.int_br.add_patch_port(
+                    int_if_name, constants.NONEXISTENT_PEER)
+                phys_ofport = br.add_patch_port(
+                    phys_if_name, constants.NONEXISTENT_PEER)
+
+            self.int_ofports[physical_network] = int_ofport
+            self.phys_ofports[physical_network] = phys_ofport
+
+            # block all untranslated traffic between bridges
+            self.int_br.add_flow(priority=2, in_port=int_ofport,
                                  actions="drop")
-            br.add_flow(priority=2,
-                        in_port=self.phys_ofports[physical_network],
-                        actions="drop")
-
-            # enable veth to pass traffic
-            int_veth.link.set_up()
-            phys_veth.link.set_up()
-
-            if self.veth_mtu:
-                # set up mtu size for veth interfaces
-                int_veth.link.set_mtu(self.veth_mtu)
-                phys_veth.link.set_mtu(self.veth_mtu)
+            br.add_flow(priority=2, in_port=phys_ofport, actions="drop")
+
+            if self.use_veth_interconnection:
+                # enable veth to pass traffic
+                int_veth.link.set_up()
+                phys_veth.link.set_up()
+                if self.veth_mtu:
+                    # set up mtu size for veth interfaces
+                    int_veth.link.set_mtu(self.veth_mtu)
+                    phys_veth.link.set_mtu(self.veth_mtu)
+            else:
+                # associate patch ports to pass traffic
+                self.int_br.set_db_attribute('Interface', int_if_name,
+                                             'options:peer', phys_if_name)
+                br.set_db_attribute('Interface', phys_if_name,
+                                    'options:peer', int_if_name)
 
     def scan_ports(self, registered_ports, updated_ports=None):
         cur_ports = self.int_br.get_vif_port_set()
@@ -1474,6 +1494,7 @@ def create_agent_config_map(config):
         veth_mtu=config.AGENT.veth_mtu,
         l2_population=config.AGENT.l2_population,
         arp_responder=config.AGENT.arp_responder,
+        use_veth_interconnection=config.OVS.use_veth_interconnection,
     )
 
     # If enable_tunneling is TRUE, set tunnel_type to default to GRE
index 9bfea1e0daa46e2e76defd2c9d9a7c727e386301..849044db6975c1b6110169158b28e075169cc3d2 100644 (file)
@@ -54,6 +54,9 @@ ovs_opts = [
     cfg.StrOpt('tunnel_type', default='',
                help=_("The type of tunnels to use when utilizing tunnels, "
                       "either 'gre' or 'vxlan'")),
+    cfg.BoolOpt('use_veth_interconnection', default=False,
+                help=_("Use veths instead of patch ports to interconnect the "
+                       "integration bridge to physical bridges")),
 ]
 
 agent_opts = [
index 57f0863258d7beab57ebe2fb9c88a8f8c7e4a30a..da45415cae626733b1e069c404b7d78e3e6cf696 100644 (file)
@@ -25,10 +25,14 @@ TUNNEL = 'tunnel'
 # Values for network_type
 VXLAN_UDP_PORT = 4789
 
-# Name prefixes for veth device pair linking the integration bridge
-# with the physical bridge for a physical network
-VETH_INTEGRATION_PREFIX = 'int-'
-VETH_PHYSICAL_PREFIX = 'phy-'
+# Name prefixes for veth device or patch port pair linking the integration
+# bridge with the physical bridge for a physical network
+PEER_INTEGRATION_PREFIX = 'int-'
+PEER_PHYSICAL_PREFIX = 'phy-'
+
+# Nonexistent peer used to create patch ports without associating them, it
+# allows to define flows before association
+NONEXISTENT_PEER = 'nonexistent-peer'
 
 # The different types of tunnels
 TUNNEL_NETWORK_TYPES = [p_const.TYPE_GRE, p_const.TYPE_VXLAN]
index cad247c023e726d0da39b6a1e26d7cd4e35f8d98..a07acc89d05dd3f584d2d81d3f5c4e783ed47513 100644 (file)
@@ -447,6 +447,59 @@ class TestOvsNeutronAgent(base.BaseTestCase):
         self.assertEqual(set(['123']), self.agent.updated_ports)
 
     def test_setup_physical_bridges(self):
+        with contextlib.nested(
+            mock.patch.object(ip_lib, "device_exists"),
+            mock.patch.object(sys, "exit"),
+            mock.patch.object(utils, "execute"),
+            mock.patch.object(ovs_lib.OVSBridge, "remove_all_flows"),
+            mock.patch.object(ovs_lib.OVSBridge, "add_flow"),
+            mock.patch.object(ovs_lib.OVSBridge, "add_patch_port"),
+            mock.patch.object(ovs_lib.OVSBridge, "delete_port"),
+            mock.patch.object(ovs_lib.OVSBridge, "set_db_attribute"),
+            mock.patch.object(self.agent.int_br, "add_flow"),
+            mock.patch.object(self.agent.int_br, "add_patch_port"),
+            mock.patch.object(self.agent.int_br, "delete_port"),
+            mock.patch.object(self.agent.int_br, "set_db_attribute"),
+        ) as (devex_fn, sysexit_fn, utilsexec_fn, remflows_fn, ovs_add_flow_fn,
+              ovs_addpatch_port_fn, ovs_delport_fn, ovs_set_attr_fn,
+              br_add_flow_fn, br_addpatch_port_fn, br_delport_fn,
+              br_set_attr_fn):
+            devex_fn.return_value = True
+            parent = mock.MagicMock()
+            parent.attach_mock(ovs_addpatch_port_fn, 'phy_add_patch_port')
+            parent.attach_mock(ovs_add_flow_fn, 'phy_add_flow')
+            parent.attach_mock(ovs_set_attr_fn, 'phy_set_attr')
+            parent.attach_mock(br_addpatch_port_fn, 'int_add_patch_port')
+            parent.attach_mock(br_add_flow_fn, 'int_add_flow')
+            parent.attach_mock(br_set_attr_fn, 'int_set_attr')
+
+            ovs_addpatch_port_fn.return_value = "phy_ofport"
+            br_addpatch_port_fn.return_value = "int_ofport"
+            self.agent.setup_physical_bridges({"physnet1": "br-eth"})
+            expected_calls = [
+                mock.call.phy_add_flow(priority=1, actions='normal'),
+                mock.call.int_add_patch_port('int-br-eth',
+                                             constants.NONEXISTENT_PEER),
+                mock.call.phy_add_patch_port('phy-br-eth',
+                                             constants.NONEXISTENT_PEER),
+                mock.call.int_add_flow(priority=2, in_port='int_ofport',
+                                       actions='drop'),
+                mock.call.phy_add_flow(priority=2, in_port='phy_ofport',
+                                       actions='drop'),
+                mock.call.int_set_attr('Interface', 'int-br-eth',
+                                       'options:peer', 'phy-br-eth'),
+                mock.call.phy_set_attr('Interface', 'phy-br-eth',
+                                       'options:peer', 'int-br-eth'),
+
+            ]
+            parent.assert_has_calls(expected_calls)
+            self.assertEqual(self.agent.int_ofports["physnet1"],
+                             "int_ofport")
+            self.assertEqual(self.agent.phys_ofports["physnet1"],
+                             "phy_ofport")
+
+    def test_setup_physical_bridges_using_veth_interconnection(self):
+        self.agent.use_veth_interconnection = True
         with contextlib.nested(
             mock.patch.object(ip_lib, "device_exists"),
             mock.patch.object(sys, "exit"),
@@ -486,15 +539,16 @@ class TestOvsNeutronAgent(base.BaseTestCase):
             self.assertEqual(self.agent.phys_ofports["physnet1"],
                              "int_ofport")
 
-    def test_get_veth_name(self):
+    def test_get_peer_name(self):
             bridge1 = "A_REALLY_LONG_BRIDGE_NAME1"
             bridge2 = "A_REALLY_LONG_BRIDGE_NAME2"
-            self.assertEqual(len(self.agent.get_veth_name('int-', bridge1)),
+            self.agent.use_veth_interconnection = True
+            self.assertEqual(len(self.agent.get_peer_name('int-', bridge1)),
                              n_const.DEVICE_NAME_MAX_LEN)
-            self.assertEqual(len(self.agent.get_veth_name('int-', bridge2)),
+            self.assertEqual(len(self.agent.get_peer_name('int-', bridge2)),
                              n_const.DEVICE_NAME_MAX_LEN)
-            self.assertNotEqual(self.agent.get_veth_name('int-', bridge1),
-                                self.agent.get_veth_name('int-', bridge2))
+            self.assertNotEqual(self.agent.get_peer_name('int-', bridge1),
+                                self.agent.get_peer_name('int-', bridge2))
 
     def test_port_unbound(self):
         with mock.patch.object(self.agent, "reclaim_local_vlan") as reclvl_fn:
index d246ff96fd20faa0050a54c88d9eacd0432d3023..b02b9da795c026c4e76b1d64e82595a9c0826db6 100644 (file)
@@ -63,6 +63,8 @@ class DummyVlanBinding:
 
 
 class TunnelTest(base.BaseTestCase):
+    USE_VETH_INTERCONNECTION = False
+    VETH_MTU = None
 
     def setUp(self):
         super(TunnelTest, self).setUp()
@@ -85,8 +87,9 @@ class TunnelTest(base.BaseTestCase):
         self.NET_MAPPING = {'net1': self.MAP_TUN_BRIDGE}
         self.INT_OFPORT = 11111
         self.TUN_OFPORT = 22222
-        self.MAP_TUN_OFPORT = 33333
-        self.VETH_MTU = None
+        self.MAP_TUN_INT_OFPORT = 33333
+        self.MAP_TUN_PHY_OFPORT = 44444
+
         self.inta = mock.Mock()
         self.intb = mock.Mock()
 
@@ -94,10 +97,50 @@ class TunnelTest(base.BaseTestCase):
                             self.TUN_BRIDGE: mock.Mock(),
                             self.MAP_TUN_BRIDGE: mock.Mock(),
                             }
+        self.ovs_int_ofports = {
+            'patch-tun': self.TUN_OFPORT,
+            'int-%s' % self.MAP_TUN_BRIDGE: self.MAP_TUN_INT_OFPORT
+        }
 
         self.mock_bridge = mock.patch.object(ovs_lib, 'OVSBridge').start()
         self.mock_bridge.side_effect = (lambda br_name, root_helper:
                                         self.ovs_bridges[br_name])
+
+        self.mock_int_bridge = self.ovs_bridges[self.INT_BRIDGE]
+        self.mock_int_bridge.add_port.return_value = self.MAP_TUN_INT_OFPORT
+        self.mock_int_bridge.add_patch_port.side_effect = (
+            lambda tap, peer: self.ovs_int_ofports[tap])
+
+        self.mock_map_tun_bridge = self.ovs_bridges[self.MAP_TUN_BRIDGE]
+        self.mock_map_tun_bridge.br_name = self.MAP_TUN_BRIDGE
+        self.mock_map_tun_bridge.add_port.return_value = (
+            self.MAP_TUN_PHY_OFPORT)
+        self.mock_map_tun_bridge.add_patch_port.return_value = (
+            self.MAP_TUN_PHY_OFPORT)
+
+        self.mock_tun_bridge = self.ovs_bridges[self.TUN_BRIDGE]
+        self.mock_tun_bridge.add_port.return_value = self.INT_OFPORT
+        self.mock_tun_bridge.add_patch_port.return_value = self.INT_OFPORT
+
+        self.device_exists = mock.patch.object(ip_lib, 'device_exists').start()
+        self.device_exists.return_value = True
+
+        self.ipdevice = mock.patch.object(ip_lib, 'IPDevice').start()
+
+        self.ipwrapper = mock.patch.object(ip_lib, 'IPWrapper').start()
+        add_veth = self.ipwrapper.return_value.add_veth
+        add_veth.return_value = [self.inta, self.intb]
+
+        self.get_bridges = mock.patch.object(ovs_lib, 'get_bridges').start()
+        self.get_bridges.return_value = [self.INT_BRIDGE,
+                                         self.TUN_BRIDGE,
+                                         self.MAP_TUN_BRIDGE]
+
+        self.execute = mock.patch('neutron.agent.linux.utils.execute').start()
+
+        self._define_expected_calls()
+
+    def _define_expected_calls(self):
         self.mock_bridge_expected = [
             mock.call(self.INT_BRIDGE, 'sudo'),
             mock.call(self.MAP_TUN_BRIDGE, 'sudo'),
@@ -115,31 +158,36 @@ class TunnelTest(base.BaseTestCase):
                                actions='drop'),
         ]
 
-        self.mock_map_tun_bridge = self.ovs_bridges[self.MAP_TUN_BRIDGE]
-        self.mock_map_tun_bridge.br_name = self.MAP_TUN_BRIDGE
-        self.mock_map_tun_bridge.add_port.return_value = None
         self.mock_map_tun_bridge_expected = [
             mock.call.remove_all_flows(),
             mock.call.add_flow(priority=1, actions='normal'),
             mock.call.delete_port('phy-%s' % self.MAP_TUN_BRIDGE),
-            mock.call.add_port(self.intb),
+            mock.call.add_patch_port('phy-%s' % self.MAP_TUN_BRIDGE,
+                                     constants.NONEXISTENT_PEER),
         ]
-        self.mock_int_bridge.add_port.return_value = None
         self.mock_int_bridge_expected += [
             mock.call.delete_port('int-%s' % self.MAP_TUN_BRIDGE),
-            mock.call.add_port(self.inta)
+            mock.call.add_patch_port('int-%s' % self.MAP_TUN_BRIDGE,
+                                     constants.NONEXISTENT_PEER),
         ]
-        self.inta_expected = [mock.call.link.set_up()]
-        self.intb_expected = [mock.call.link.set_up()]
 
         self.mock_int_bridge_expected += [
-            mock.call.add_flow(priority=2, in_port=None, actions='drop')
+            mock.call.add_flow(priority=2,
+                               in_port=self.MAP_TUN_INT_OFPORT,
+                               actions='drop'),
+            mock.call.set_db_attribute(
+                'Interface', 'int-%s' % self.MAP_TUN_BRIDGE,
+                'options:peer', 'phy-%s' % self.MAP_TUN_BRIDGE),
         ]
         self.mock_map_tun_bridge_expected += [
-            mock.call.add_flow(priority=2, in_port=None, actions='drop')
+            mock.call.add_flow(priority=2,
+                               in_port=self.MAP_TUN_PHY_OFPORT,
+                               actions='drop'),
+            mock.call.set_db_attribute(
+                'Interface', 'phy-%s' % self.MAP_TUN_BRIDGE,
+                'options:peer', 'int-%s' % self.MAP_TUN_BRIDGE),
         ]
 
-        self.mock_tun_bridge = self.ovs_bridges[self.TUN_BRIDGE]
         self.mock_tun_bridge_expected = [
             mock.call.reset_bridge(),
             mock.call.add_patch_port('patch-int', 'patch-tun'),
@@ -147,8 +195,6 @@ class TunnelTest(base.BaseTestCase):
         self.mock_int_bridge_expected += [
             mock.call.add_patch_port('patch-tun', 'patch-int')
         ]
-        self.mock_int_bridge.add_patch_port.return_value = self.TUN_OFPORT
-        self.mock_tun_bridge.add_patch_port.return_value = self.INT_OFPORT
 
         self.mock_tun_bridge_expected += [
             mock.call.remove_all_flows(),
@@ -195,37 +241,31 @@ class TunnelTest(base.BaseTestCase):
                                actions="drop")
         ]
 
-        self.device_exists = mock.patch.object(ip_lib, 'device_exists').start()
-        self.device_exists.return_value = True
         self.device_exists_expected = [
             mock.call(self.MAP_TUN_BRIDGE, 'sudo'),
-            mock.call('int-%s' % self.MAP_TUN_BRIDGE, 'sudo'),
         ]
 
-        self.ipdevice = mock.patch.object(ip_lib, 'IPDevice').start()
-        self.ipdevice_expected = [
-            mock.call('int-%s' % self.MAP_TUN_BRIDGE, 'sudo'),
-            mock.call().link.delete()
-        ]
-        self.ipwrapper = mock.patch.object(ip_lib, 'IPWrapper').start()
-        add_veth = self.ipwrapper.return_value.add_veth
-        add_veth.return_value = [self.inta, self.intb]
-        self.ipwrapper_expected = [
-            mock.call('sudo'),
-            mock.call().add_veth('int-%s' % self.MAP_TUN_BRIDGE,
-                                 'phy-%s' % self.MAP_TUN_BRIDGE)
-        ]
+        self.ipdevice_expected = []
+        self.ipwrapper_expected = [mock.call('sudo')]
 
-        self.get_bridges = mock.patch.object(ovs_lib, 'get_bridges').start()
-        self.get_bridges.return_value = [self.INT_BRIDGE,
-                                         self.TUN_BRIDGE,
-                                         self.MAP_TUN_BRIDGE]
-        self.get_bridges_expected = [
-            mock.call('sudo')
-        ]
-        self.execute = mock.patch('neutron.agent.linux.utils.execute').start()
-        self.execute_expected = [mock.call(['/sbin/udevadm', 'settle',
-                                            '--timeout=10'])]
+        self.get_bridges_expected = [mock.call('sudo')]
+
+        self.inta_expected = []
+        self.intb_expected = []
+        self.execute_expected = []
+
+    def _build_agent(self, **kwargs):
+        kwargs.setdefault('integ_br', self.INT_BRIDGE)
+        kwargs.setdefault('tun_br', self.TUN_BRIDGE)
+        kwargs.setdefault('local_ip', '10.0.0.1')
+        kwargs.setdefault('bridge_mappings', self.NET_MAPPING)
+        kwargs.setdefault('root_helper', 'sudo')
+        kwargs.setdefault('polling_interval', 2)
+        kwargs.setdefault('tunnel_types', ['gre'])
+        kwargs.setdefault('veth_mtu', self.VETH_MTU)
+        kwargs.setdefault('use_veth_interconnection',
+                          self.USE_VETH_INTERCONNECTION)
+        return ovs_neutron_agent.OVSNeutronAgent(**kwargs)
 
     def _verify_mock_call(self, mock_obj, expected):
         mock_obj.assert_has_calls(expected)
@@ -248,11 +288,7 @@ class TunnelTest(base.BaseTestCase):
         self._verify_mock_call(self.execute, self.execute_expected)
 
     def test_construct(self):
-        agent = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                                  self.TUN_BRIDGE,
-                                                  '10.0.0.1', self.NET_MAPPING,
-                                                  'sudo', 2, ['gre'],
-                                                  self.VETH_MTU)
+        agent = self._build_agent()
         self.assertEqual(agent.agent_id, 'ovs-agent-%s' % cfg.CONF.host)
         self._verify_mock_calls()
 
@@ -260,12 +296,7 @@ class TunnelTest(base.BaseTestCase):
     #                 ML2 l2 population mechanism driver.
     #                 The next two tests use l2_pop flag to test ARP responder
     def test_construct_with_arp_responder(self):
-        ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                          self.TUN_BRIDGE,
-                                          '10.0.0.1', self.NET_MAPPING,
-                                          'sudo', 2, ['gre'],
-                                          self.VETH_MTU, l2_population=True,
-                                          arp_responder=True)
+        self._build_agent(l2_population=True, arp_responder=True)
         self.mock_tun_bridge_expected.insert(
             5, mock.call.add_flow(table=constants.PATCH_LV_TO_TUN,
                                   priority=1,
@@ -283,21 +314,11 @@ class TunnelTest(base.BaseTestCase):
         self._verify_mock_calls()
 
     def test_construct_without_arp_responder(self):
-        ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                          self.TUN_BRIDGE,
-                                          '10.0.0.1', self.NET_MAPPING,
-                                          'sudo', 2, ['gre'],
-                                          self.VETH_MTU, l2_population=False,
-                                          arp_responder=True)
+        self._build_agent(l2_population=False, arp_responder=True)
         self._verify_mock_calls()
 
     def test_construct_vxlan(self):
-        ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                          self.TUN_BRIDGE,
-                                          '10.0.0.1',
-                                          self.NET_MAPPING,
-                                          'sudo', 2, ['vxlan'],
-                                          self.VETH_MTU)
+        self._build_agent(tunnel_types=['vxlan'])
         self._verify_mock_calls()
 
     def test_provision_local_vlan(self):
@@ -315,11 +336,7 @@ class TunnelTest(base.BaseTestCase):
                                (LV_ID, constants.LEARN_FROM_TUN)),
         ]
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.available_local_vlans = set([LV_ID])
         a.tun_br_ofports = TUN_OFPORTS
         a.provision_local_vlan(NET_UUID, p_const.TYPE_GRE, None, LS_ID)
@@ -328,7 +345,7 @@ class TunnelTest(base.BaseTestCase):
     def test_provision_local_vlan_flat(self):
         action_string = 'strip_vlan,normal'
         self.mock_map_tun_bridge_expected.append(
-            mock.call.add_flow(priority=4, in_port=self.MAP_TUN_OFPORT,
+            mock.call.add_flow(priority=4, in_port=self.MAP_TUN_PHY_OFPORT,
                                dl_vlan=LV_ID, actions=action_string))
 
         action_string = 'mod_vlan_vid:%s,normal' % LV_ID
@@ -336,31 +353,23 @@ class TunnelTest(base.BaseTestCase):
             mock.call.add_flow(priority=3, in_port=self.INT_OFPORT,
                                dl_vlan=65535, actions=action_string))
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.available_local_vlans = set([LV_ID])
         a.phys_brs['net1'] = self.mock_map_tun_bridge
-        a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
+        a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT
         a.int_ofports['net1'] = self.INT_OFPORT
         a.provision_local_vlan(NET_UUID, p_const.TYPE_FLAT, 'net1', LS_ID)
         self._verify_mock_calls()
 
     def test_provision_local_vlan_flat_fail(self):
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.provision_local_vlan(NET_UUID, p_const.TYPE_FLAT, 'net2', LS_ID)
         self._verify_mock_calls()
 
     def test_provision_local_vlan_vlan(self):
         action_string = 'mod_vlan_vid:%s,normal' % LS_ID
         self.mock_map_tun_bridge_expected.append(
-            mock.call.add_flow(priority=4, in_port=self.MAP_TUN_OFPORT,
+            mock.call.add_flow(priority=4, in_port=self.MAP_TUN_PHY_OFPORT,
                                dl_vlan=LV_ID, actions=action_string))
 
         action_string = 'mod_vlan_vid:%s,normal' % LS_ID
@@ -368,24 +377,16 @@ class TunnelTest(base.BaseTestCase):
             mock.call.add_flow(priority=3, in_port=self.INT_OFPORT,
                                dl_vlan=LV_ID, actions=action_string))
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.available_local_vlans = set([LV_ID])
         a.phys_brs['net1'] = self.mock_map_tun_bridge
-        a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
+        a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT
         a.int_ofports['net1'] = self.INT_OFPORT
         a.provision_local_vlan(NET_UUID, p_const.TYPE_VLAN, 'net1', LS_ID)
         self._verify_mock_calls()
 
     def test_provision_local_vlan_vlan_fail(self):
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.provision_local_vlan(NET_UUID, p_const.TYPE_VLAN, 'net2', LS_ID)
         self._verify_mock_calls()
 
@@ -396,11 +397,7 @@ class TunnelTest(base.BaseTestCase):
             mock.call.delete_flows(dl_vlan=LVM.vlan)
         ]
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.available_local_vlans = set()
         a.local_vlan_map[NET_UUID] = LVM
         a.reclaim_local_vlan(NET_UUID)
@@ -410,18 +407,14 @@ class TunnelTest(base.BaseTestCase):
     def test_reclaim_local_vlan_flat(self):
         self.mock_map_tun_bridge_expected.append(
             mock.call.delete_flows(
-                in_port=self.MAP_TUN_OFPORT, dl_vlan=LVM_FLAT.vlan))
+                in_port=self.MAP_TUN_PHY_OFPORT, dl_vlan=LVM_FLAT.vlan))
         self.mock_int_bridge_expected.append(
             mock.call.delete_flows(
                 dl_vlan=65535, in_port=self.INT_OFPORT))
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.phys_brs['net1'] = self.mock_map_tun_bridge
-        a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
+        a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT
         a.int_ofports['net1'] = self.INT_OFPORT
 
         a.available_local_vlans = set()
@@ -433,18 +426,14 @@ class TunnelTest(base.BaseTestCase):
     def test_reclaim_local_vlan_vlan(self):
         self.mock_map_tun_bridge_expected.append(
             mock.call.delete_flows(
-                in_port=self.MAP_TUN_OFPORT, dl_vlan=LVM_VLAN.vlan))
+                in_port=self.MAP_TUN_PHY_OFPORT, dl_vlan=LVM_VLAN.vlan))
         self.mock_int_bridge_expected.append(
             mock.call.delete_flows(
                 dl_vlan=LV_ID, in_port=self.INT_OFPORT))
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.phys_brs['net1'] = self.mock_map_tun_bridge
-        a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
+        a.phys_ofports['net1'] = self.MAP_TUN_PHY_OFPORT
         a.int_ofports['net1'] = self.INT_OFPORT
 
         a.available_local_vlans = set()
@@ -461,11 +450,7 @@ class TunnelTest(base.BaseTestCase):
             mock.call.delete_flows(in_port=VIF_PORT.ofport)
         ]
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.local_vlan_map[NET_UUID] = LVM
         a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID, False)
         self._verify_mock_calls()
@@ -473,11 +458,7 @@ class TunnelTest(base.BaseTestCase):
     def test_port_unbound(self):
         with mock.patch.object(ovs_neutron_agent.OVSNeutronAgent,
                                'reclaim_local_vlan') as reclaim_local_vlan:
-            a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                                  self.TUN_BRIDGE,
-                                                  '10.0.0.1', self.NET_MAPPING,
-                                                  'sudo', 2, ['gre'],
-                                                  self.VETH_MTU)
+            a = self._build_agent()
             a.local_vlan_map[NET_UUID] = LVM
             a.port_unbound(VIF_ID, NET_UUID)
 
@@ -494,11 +475,7 @@ class TunnelTest(base.BaseTestCase):
                                actions='drop')
         ]
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.available_local_vlans = set([LV_ID])
         a.local_vlan_map[NET_UUID] = LVM
         a.port_dead(VIF_PORT)
@@ -514,22 +491,14 @@ class TunnelTest(base.BaseTestCase):
                                actions='resubmit(,2)')
         ]
 
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.tunnel_update(
             mock.sentinel.ctx, tunnel_id='1', tunnel_ip='10.0.10.1',
             tunnel_type=p_const.TYPE_GRE)
         self._verify_mock_calls()
 
     def test_tunnel_update_self(self):
-        a = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                              self.TUN_BRIDGE,
-                                              '10.0.0.1', self.NET_MAPPING,
-                                              'sudo', 2, ['gre'],
-                                              self.VETH_MTU)
+        a = self._build_agent()
         a.tunnel_update(
             mock.sentinel.ctx, tunnel_id='1', tunnel_ip='10.0.0.1')
         self._verify_mock_calls()
@@ -561,12 +530,7 @@ class TunnelTest(base.BaseTestCase):
             process_network_ports.side_effect = [
                 False, Exception('Fake exception to get out of the loop')]
 
-            q_agent = ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
-                                                        self.TUN_BRIDGE,
-                                                        '10.0.0.1',
-                                                        self.NET_MAPPING,
-                                                        'sudo', 2, ['gre'],
-                                                        self.VETH_MTU)
+            q_agent = self._build_agent()
 
             # Hack to test loop
             # We start method and expect it will raise after 2nd loop
@@ -594,10 +558,130 @@ class TunnelTest(base.BaseTestCase):
         self._verify_mock_calls()
 
 
-class TunnelTestWithMTU(TunnelTest):
+class TunnelTestUseVethInterco(TunnelTest):
+    USE_VETH_INTERCONNECTION = True
 
-    def setUp(self):
-        super(TunnelTestWithMTU, self).setUp()
-        self.VETH_MTU = 1500
+    def _define_expected_calls(self):
+        self.mock_bridge_expected = [
+            mock.call(self.INT_BRIDGE, 'sudo'),
+            mock.call(self.MAP_TUN_BRIDGE, 'sudo'),
+            mock.call(self.TUN_BRIDGE, 'sudo'),
+        ]
+
+        self.mock_int_bridge_expected = [
+            mock.call.create(),
+            mock.call.set_secure_mode(),
+            mock.call.delete_port('patch-tun'),
+            mock.call.remove_all_flows(),
+            mock.call.add_flow(priority=1, actions='normal'),
+            mock.call.add_flow(table=constants.CANARY_TABLE, priority=0,
+                               actions="drop")
+        ]
+
+        self.mock_map_tun_bridge_expected = [
+            mock.call.remove_all_flows(),
+            mock.call.add_flow(priority=1, actions='normal'),
+            mock.call.delete_port('phy-%s' % self.MAP_TUN_BRIDGE),
+            mock.call.add_port(self.intb),
+        ]
+        self.mock_int_bridge_expected += [
+            mock.call.delete_port('int-%s' % self.MAP_TUN_BRIDGE),
+            mock.call.add_port(self.inta)
+        ]
+
+        self.mock_int_bridge_expected += [
+            mock.call.add_flow(priority=2,
+                               in_port=self.MAP_TUN_INT_OFPORT,
+                               actions='drop')
+        ]
+        self.mock_map_tun_bridge_expected += [
+            mock.call.add_flow(priority=2,
+                               in_port=self.MAP_TUN_PHY_OFPORT,
+                               actions='drop')
+        ]
+
+        self.mock_tun_bridge_expected = [
+            mock.call.reset_bridge(),
+            mock.call.add_patch_port('patch-int', 'patch-tun'),
+        ]
+        self.mock_int_bridge_expected += [
+            mock.call.add_patch_port('patch-tun', 'patch-int')
+        ]
+
+        self.mock_tun_bridge_expected += [
+            mock.call.remove_all_flows(),
+            mock.call.add_flow(priority=1,
+                               in_port=self.INT_OFPORT,
+                               actions="resubmit(,%s)" %
+                               constants.PATCH_LV_TO_TUN),
+            mock.call.add_flow(priority=0, actions='drop'),
+            mock.call.add_flow(priority=0,
+                               table=constants.PATCH_LV_TO_TUN,
+                               dl_dst=UCAST_MAC,
+                               actions="resubmit(,%s)" %
+                               constants.UCAST_TO_TUN),
+            mock.call.add_flow(priority=0,
+                               table=constants.PATCH_LV_TO_TUN,
+                               dl_dst=BCAST_MAC,
+                               actions="resubmit(,%s)" %
+                               constants.FLOOD_TO_TUN),
+        ]
+        for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
+            self.mock_tun_bridge_expected.append(
+                mock.call.add_flow(
+                    table=constants.TUN_TABLE[tunnel_type],
+                    priority=0,
+                    actions="drop"))
+        learned_flow = ("table=%s,"
+                        "priority=1,"
+                        "hard_timeout=300,"
+                        "NXM_OF_VLAN_TCI[0..11],"
+                        "NXM_OF_ETH_DST[]=NXM_OF_ETH_SRC[],"
+                        "load:0->NXM_OF_VLAN_TCI[],"
+                        "load:NXM_NX_TUN_ID[]->NXM_NX_TUN_ID[],"
+                        "output:NXM_OF_IN_PORT[]" %
+                        constants.UCAST_TO_TUN)
+        self.mock_tun_bridge_expected += [
+            mock.call.add_flow(table=constants.LEARN_FROM_TUN,
+                               priority=1,
+                               actions="learn(%s),output:%s" %
+                               (learned_flow, self.INT_OFPORT)),
+            mock.call.add_flow(table=constants.UCAST_TO_TUN,
+                               priority=0,
+                               actions="resubmit(,%s)" %
+                               constants.FLOOD_TO_TUN),
+            mock.call.add_flow(table=constants.FLOOD_TO_TUN,
+                               priority=0,
+                               actions="drop")
+        ]
+
+        self.device_exists_expected = [
+            mock.call(self.MAP_TUN_BRIDGE, 'sudo'),
+            mock.call('int-%s' % self.MAP_TUN_BRIDGE, 'sudo'),
+        ]
+
+        self.ipdevice_expected = [
+            mock.call('int-%s' % self.MAP_TUN_BRIDGE, 'sudo'),
+            mock.call().link.delete()
+        ]
+        self.ipwrapper_expected = [
+            mock.call('sudo'),
+            mock.call().add_veth('int-%s' % self.MAP_TUN_BRIDGE,
+                                 'phy-%s' % self.MAP_TUN_BRIDGE)
+        ]
+
+        self.get_bridges_expected = [mock.call('sudo')]
+
+        self.inta_expected = [mock.call.link.set_up()]
+        self.intb_expected = [mock.call.link.set_up()]
+        self.execute_expected = [mock.call(['/sbin/udevadm', 'settle',
+                                            '--timeout=10'])]
+
+
+class TunnelTestWithMTU(TunnelTestUseVethInterco):
+    VETH_MTU = 1500
+
+    def _define_expected_calls(self):
+        super(TunnelTestWithMTU, self)._define_expected_calls()
         self.inta_expected.append(mock.call.link.set_mtu(self.VETH_MTU))
         self.intb_expected.append(mock.call.link.set_mtu(self.VETH_MTU))