diff --git a/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini b/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini index 4beee58fa8..5a378d8b20 100644 --- a/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini +++ b/etc/neutron/plugins/openvswitch/ovs_neutron_plugin.ini @@ -83,6 +83,11 @@ # 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 @@ -117,6 +122,7 @@ # (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 diff --git a/neutron/cmd/sanity_check.py b/neutron/cmd/sanity_check.py index 015be141f5..e2f7d145a0 100644 --- a/neutron/cmd/sanity_check.py +++ b/neutron/cmd/sanity_check.py @@ -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(): diff --git a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py index a38e584618..0907ea4aa6 100644 --- a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py +++ b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py @@ -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, diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index a84c212d0a..f3e96d95db 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -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) + # 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) - # block all untranslated traffic over veth between bridges - self.int_br.add_flow(priority=2, - in_port=self.int_ofports[physical_network], + 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") + br.add_flow(priority=2, in_port=phys_ofport, 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) + 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 diff --git a/neutron/plugins/openvswitch/common/config.py b/neutron/plugins/openvswitch/common/config.py index 9bfea1e0da..849044db69 100644 --- a/neutron/plugins/openvswitch/common/config.py +++ b/neutron/plugins/openvswitch/common/config.py @@ -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 = [ diff --git a/neutron/plugins/openvswitch/common/constants.py b/neutron/plugins/openvswitch/common/constants.py index 57f0863258..da45415cae 100644 --- a/neutron/plugins/openvswitch/common/constants.py +++ b/neutron/plugins/openvswitch/common/constants.py @@ -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] diff --git a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py index cad247c023..a07acc89d0 100644 --- a/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/openvswitch/test_ovs_neutron_agent.py @@ -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: diff --git a/neutron/tests/unit/openvswitch/test_ovs_tunnel.py b/neutron/tests/unit/openvswitch/test_ovs_tunnel.py index d246ff96fd..b02b9da795 100644 --- a/neutron/tests/unit/openvswitch/test_ovs_tunnel.py +++ b/neutron/tests/unit/openvswitch/test_ovs_tunnel.py @@ -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))