vmware-nsx/neutron/tests/unit/openvswitch/test_ovs_tunnel.py
Francois Eleouet 98a9308728 OVS agent implementation of l2-population
This patchset implements l2-population RPC callbacks in OVS agents,
it enables plugin to populate forwarding table following portbindings
events.

For now, it doesn't include ARP responder implementation which is
deferred to a future patchset (As this feature isn't yet supported by
OVS, it will require the use of an external responder such as ebtables)

It anyway brings some improvements in tunnelling management, as agent
will tear-down unecessary tunnels, and flood packets on a per-network
basis rather than to all other agents.

These changes should anyway have a limited risk, as tunnel management
won't be affected as long as l2_population option is not set. This
option must be used in conjonction with ml2 plugin using l2population
mechanism driver.

Implements: blueprint l2-population

Change-Id: I5185eefedb0ff392bc8b99d16f810813e26ff58d
2013-09-12 01:13:32 +02:00

483 lines
21 KiB
Python

# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 Nicira Networks, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Dave Lapsley, Nicira Networks, Inc.
import mox
from oslo.config import cfg
from neutron.agent.linux import ip_lib
from neutron.agent.linux import ovs_lib
from neutron.openstack.common import log
from neutron.plugins.openvswitch.agent import ovs_neutron_agent
from neutron.plugins.openvswitch.common import constants
from neutron.tests import base
# Useful global dummy variables.
NET_UUID = '3faeebfe-5d37-11e1-a64b-000c29d5f0a7'
LS_ID = 42
LV_ID = 42
LV_IDS = [42, 43]
VIF_ID = '404deaec-5d37-11e1-a64b-000c29d5f0a8'
VIF_MAC = '3c:09:24:1e:78:23'
OFPORT_NUM = 1
VIF_PORT = ovs_lib.VifPort('port', OFPORT_NUM,
VIF_ID, VIF_MAC, 'switch')
VIF_PORTS = {VIF_ID: VIF_PORT}
LVM = ovs_neutron_agent.LocalVLANMapping(LV_ID, 'gre', None, LS_ID, VIF_PORTS)
LVM_FLAT = ovs_neutron_agent.LocalVLANMapping(
LV_ID, 'flat', 'net1', LS_ID, VIF_PORTS)
LVM_VLAN = ovs_neutron_agent.LocalVLANMapping(
LV_ID, 'vlan', 'net1', LS_ID, VIF_PORTS)
TUN_OFPORTS = {constants.TYPE_GRE: {'ip1': '11', 'ip2': '12'}}
BCAST_MAC = "01:00:00:00:00:00/01:00:00:00:00:00"
UCAST_MAC = "00:00:00:00:00:00/01:00:00:00:00:00"
class DummyPort:
def __init__(self, interface_id):
self.interface_id = interface_id
class DummyVlanBinding:
def __init__(self, network_id, vlan_id):
self.network_id = network_id
self.vlan_id = vlan_id
class TunnelTest(base.BaseTestCase):
def setUp(self):
super(TunnelTest, self).setUp()
cfg.CONF.set_override('rpc_backend',
'neutron.openstack.common.rpc.impl_fake')
cfg.CONF.set_override('report_interval', 0, 'AGENT')
self.mox = mox.Mox()
self.addCleanup(self.mox.UnsetStubs)
self.INT_BRIDGE = 'integration_bridge'
self.TUN_BRIDGE = 'tunnel_bridge'
self.MAP_TUN_BRIDGE = 'tunnel_bridge_mapping'
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.inta = self.mox.CreateMock(ip_lib.IPDevice)
self.intb = self.mox.CreateMock(ip_lib.IPDevice)
self.inta.link = self.mox.CreateMock(ip_lib.IpLinkCommand)
self.intb.link = self.mox.CreateMock(ip_lib.IpLinkCommand)
self.mox.StubOutClassWithMocks(ovs_lib, 'OVSBridge')
self.mock_int_bridge = ovs_lib.OVSBridge(self.INT_BRIDGE, 'sudo')
self.mock_int_bridge.get_local_port_mac().AndReturn('000000000001')
self.mock_int_bridge.delete_port('patch-tun')
self.mock_int_bridge.remove_all_flows()
self.mock_int_bridge.add_flow(priority=1, actions='normal')
self.mock_map_tun_bridge = ovs_lib.OVSBridge(
self.MAP_TUN_BRIDGE, 'sudo')
self.mock_map_tun_bridge.br_name = self.MAP_TUN_BRIDGE
self.mock_map_tun_bridge.remove_all_flows()
self.mock_map_tun_bridge.add_flow(priority=1, actions='normal')
self.mock_int_bridge.delete_port('int-tunnel_bridge_mapping')
self.mock_map_tun_bridge.delete_port('phy-tunnel_bridge_mapping')
self.mock_int_bridge.add_port(self.inta)
self.mock_map_tun_bridge.add_port(self.intb)
self.inta.link.set_up()
self.intb.link.set_up()
self.mock_int_bridge.add_flow(priority=2, in_port=None, actions='drop')
self.mock_map_tun_bridge.add_flow(
priority=2, in_port=None, actions='drop')
self.mock_tun_bridge = ovs_lib.OVSBridge(self.TUN_BRIDGE, 'sudo')
self.mock_tun_bridge.reset_bridge()
self.mock_int_bridge.add_patch_port(
'patch-tun', 'patch-int').AndReturn(self.TUN_OFPORT)
self.mock_tun_bridge.add_patch_port(
'patch-int', 'patch-tun').AndReturn(self.INT_OFPORT)
self.mock_tun_bridge.remove_all_flows()
self.mock_tun_bridge.add_flow(priority=1,
in_port=self.INT_OFPORT,
actions="resubmit(,%s)" %
constants.PATCH_LV_TO_TUN)
self.mock_tun_bridge.add_flow(priority=0, actions='drop')
self.mock_tun_bridge.add_flow(table=constants.PATCH_LV_TO_TUN,
dl_dst=UCAST_MAC,
actions="resubmit(,%s)" %
constants.UCAST_TO_TUN)
self.mock_tun_bridge.add_flow(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.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.add_flow(table=constants.LEARN_FROM_TUN,
priority=1,
actions="learn(%s),output:%s" %
(learned_flow, self.INT_OFPORT))
self.mock_tun_bridge.add_flow(table=constants.UCAST_TO_TUN,
priority=0,
actions="resubmit(,%s)" %
constants.FLOOD_TO_TUN)
self.mock_tun_bridge.add_flow(table=constants.FLOOD_TO_TUN,
priority=0,
actions="drop")
self.mox.StubOutWithMock(ip_lib, 'device_exists')
ip_lib.device_exists('tunnel_bridge_mapping', 'sudo').AndReturn(True)
ip_lib.device_exists(
'int-tunnel_bridge_mapping', 'sudo').AndReturn(True)
self.mox.StubOutWithMock(ip_lib.IpLinkCommand, 'delete')
ip_lib.IPDevice('int-tunnel_bridge_mapping').link.delete()
self.mox.StubOutClassWithMocks(ip_lib, 'IPWrapper')
ip_lib.IPWrapper('sudo').add_veth(
'int-tunnel_bridge_mapping',
'phy-tunnel_bridge_mapping').AndReturn([self.inta, self.intb])
self.mox.StubOutWithMock(ovs_lib, 'get_bridges')
ovs_lib.get_bridges('sudo').AndReturn([self.INT_BRIDGE,
self.TUN_BRIDGE,
self.MAP_TUN_BRIDGE])
def testConstruct(self):
self.mox.ReplayAll()
ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
self.TUN_BRIDGE,
'10.0.0.1', self.NET_MAPPING,
'sudo', 2, ['gre'],
self.VETH_MTU)
self.mox.VerifyAll()
def testConstructVXLAN(self):
self.mox.StubOutWithMock(ovs_lib, 'get_installed_ovs_klm_version')
ovs_lib.get_installed_ovs_klm_version().AndReturn("1.10")
self.mox.StubOutWithMock(ovs_lib, 'get_installed_ovs_usr_version')
ovs_lib.get_installed_ovs_usr_version('sudo').AndReturn("1.10")
self.mox.ReplayAll()
ovs_neutron_agent.OVSNeutronAgent(self.INT_BRIDGE,
self.TUN_BRIDGE,
'10.0.0.1', self.NET_MAPPING,
'sudo', 2, ['vxlan'],
self.VETH_MTU)
self.mox.VerifyAll()
def testProvisionLocalVlan(self):
ofports = ','.join(TUN_OFPORTS[constants.TYPE_GRE].values())
self.mock_tun_bridge.mod_flow(table=constants.FLOOD_TO_TUN,
priority=1,
dl_vlan=LV_ID,
actions="strip_vlan,"
"set_tunnel:%s,output:%s" %
(LS_ID, ofports))
self.mock_tun_bridge.add_flow(table=constants.TUN_TABLE['gre'],
priority=1,
tun_id=LS_ID,
actions="mod_vlan_vid:%s,resubmit(,%s)" %
(LV_ID, constants.LEARN_FROM_TUN))
self.mox.ReplayAll()
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.available_local_vlans = set([LV_ID])
a.tun_br_ofports = TUN_OFPORTS
a.provision_local_vlan(NET_UUID, constants.TYPE_GRE, None, LS_ID)
self.mox.VerifyAll()
def testProvisionLocalVlanFlat(self):
action_string = 'strip_vlan,normal'
self.mock_map_tun_bridge.add_flow(
priority=4, in_port=self.MAP_TUN_OFPORT,
dl_vlan=LV_ID, actions=action_string)
action_string = 'mod_vlan_vid:%s,normal' % LV_ID
self.mock_int_bridge.add_flow(priority=3, in_port=self.INT_OFPORT,
dl_vlan=65535, actions=action_string)
self.mox.ReplayAll()
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.available_local_vlans = set([LV_ID])
a.phys_brs['net1'] = self.mock_map_tun_bridge
a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
a.int_ofports['net1'] = self.INT_OFPORT
a.provision_local_vlan(NET_UUID, constants.TYPE_FLAT, 'net1', LS_ID)
self.mox.VerifyAll()
def testProvisionLocalVlanFlatFail(self):
self.mox.ReplayAll()
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.provision_local_vlan(NET_UUID, constants.TYPE_FLAT, 'net2', LS_ID)
self.mox.VerifyAll()
def testProvisionLocalVlanVlan(self):
action_string = 'mod_vlan_vid:%s,normal' % LS_ID
self.mock_map_tun_bridge.add_flow(
priority=4, in_port=self.MAP_TUN_OFPORT,
dl_vlan=LV_ID, actions=action_string)
action_string = 'mod_vlan_vid:%s,normal' % LS_ID
self.mock_int_bridge.add_flow(priority=3, in_port=self.INT_OFPORT,
dl_vlan=LV_ID, actions=action_string)
self.mox.ReplayAll()
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.available_local_vlans = set([LV_ID])
a.phys_brs['net1'] = self.mock_map_tun_bridge
a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
a.int_ofports['net1'] = self.INT_OFPORT
a.provision_local_vlan(NET_UUID, constants.TYPE_VLAN, 'net1', LS_ID)
self.mox.VerifyAll()
def testProvisionLocalVlanVlanFail(self):
self.mox.ReplayAll()
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.provision_local_vlan(NET_UUID, constants.TYPE_VLAN, 'net2', LS_ID)
self.mox.VerifyAll()
def testReclaimLocalVlan(self):
self.mock_tun_bridge.delete_flows(
table=constants.TUN_TABLE['gre'], tun_id=LS_ID)
self.mock_tun_bridge.delete_flows(dl_vlan=LVM.vlan)
self.mox.ReplayAll()
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.available_local_vlans = set()
a.local_vlan_map[NET_UUID] = LVM
a.reclaim_local_vlan(NET_UUID)
self.assertTrue(LVM.vlan in a.available_local_vlans)
self.mox.VerifyAll()
def testReclaimLocalVlanFlat(self):
self.mock_map_tun_bridge.delete_flows(
in_port=self.MAP_TUN_OFPORT, dl_vlan=LVM_FLAT.vlan)
self.mock_int_bridge.delete_flows(
dl_vlan=65535, in_port=self.INT_OFPORT)
self.mox.ReplayAll()
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.phys_brs['net1'] = self.mock_map_tun_bridge
a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
a.int_ofports['net1'] = self.INT_OFPORT
a.available_local_vlans = set()
a.local_vlan_map[NET_UUID] = LVM_FLAT
a.reclaim_local_vlan(NET_UUID)
self.assertTrue(LVM_FLAT.vlan in a.available_local_vlans)
self.mox.VerifyAll()
def testReclaimLocalVlanVLAN(self):
self.mock_map_tun_bridge.delete_flows(
in_port=self.MAP_TUN_OFPORT, dl_vlan=LVM_VLAN.vlan)
self.mock_int_bridge.delete_flows(
dl_vlan=LV_ID, in_port=self.INT_OFPORT)
self.mox.ReplayAll()
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.phys_brs['net1'] = self.mock_map_tun_bridge
a.phys_ofports['net1'] = self.MAP_TUN_OFPORT
a.int_ofports['net1'] = self.INT_OFPORT
a.available_local_vlans = set()
a.local_vlan_map[NET_UUID] = LVM_VLAN
a.reclaim_local_vlan(NET_UUID)
self.assertTrue(LVM_VLAN.vlan in a.available_local_vlans)
self.mox.VerifyAll()
def testPortBound(self):
self.mock_int_bridge.set_db_attribute('Port', VIF_PORT.port_name,
'tag', str(LVM.vlan))
self.mock_int_bridge.delete_flows(in_port=VIF_PORT.ofport)
self.mox.ReplayAll()
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.local_vlan_map[NET_UUID] = LVM
a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID)
self.mox.VerifyAll()
def testPortUnbound(self):
self.mox.StubOutWithMock(
ovs_neutron_agent.OVSNeutronAgent, 'reclaim_local_vlan')
ovs_neutron_agent.OVSNeutronAgent.reclaim_local_vlan(NET_UUID)
self.mox.ReplayAll()
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.local_vlan_map[NET_UUID] = LVM
a.port_unbound(VIF_ID, NET_UUID)
self.mox.VerifyAll()
def testPortDead(self):
self.mock_int_bridge.set_db_attribute(
'Port', VIF_PORT.port_name, 'tag', ovs_neutron_agent.DEAD_VLAN_TAG)
self.mock_int_bridge.add_flow(priority=2, in_port=VIF_PORT.ofport,
actions='drop')
self.mox.ReplayAll()
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.available_local_vlans = set([LV_ID])
a.local_vlan_map[NET_UUID] = LVM
a.port_dead(VIF_PORT)
self.mox.VerifyAll()
def testTunnelUpdate(self):
self.mock_tun_bridge.add_tunnel_port('gre-1', '10.0.10.1', '10.0.0.1',
'gre', 4789)
self.mox.ReplayAll()
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.tunnel_update(
mox.MockAnything, tunnel_id='1', tunnel_ip='10.0.10.1',
tunnel_type=constants.TYPE_GRE)
self.mox.VerifyAll()
def testTunnelUpdateSelf(self):
self.mox.ReplayAll()
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.tunnel_update(
mox.MockAnything, tunnel_id='1', tunnel_ip='10.0.0.1')
self.mox.VerifyAll()
def testDaemonLoop(self):
reply2 = {'current': set(['tap0']),
'added': set([]),
'removed': set([])}
reply3 = {'current': set(['tap2']),
'added': set([]),
'removed': set([])}
self.mox.StubOutWithMock(log.ContextAdapter, 'exception')
log.ContextAdapter.exception(
_("Error in agent event loop")).AndRaise(
Exception('Fake exception to get out of the loop'))
self.mox.StubOutWithMock(
ovs_neutron_agent.OVSNeutronAgent, 'update_ports')
ovs_neutron_agent.OVSNeutronAgent.update_ports(set()).AndReturn(reply2)
ovs_neutron_agent.OVSNeutronAgent.update_ports(
set(['tap0'])).AndReturn(reply3)
self.mox.StubOutWithMock(
ovs_neutron_agent.OVSNeutronAgent, 'process_network_ports')
ovs_neutron_agent.OVSNeutronAgent.process_network_ports(
{'current': set(['tap0']),
'removed': set([]),
'added': set([])}).AndReturn(False)
ovs_neutron_agent.OVSNeutronAgent.process_network_ports(
{'current': set(['tap0']),
'removed': set([]),
'added': set([])}).AndRaise(
Exception('Fake exception to get out of the loop'))
self.mox.ReplayAll()
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)
# Hack to test loop
# We start method and expect it will raise after 2nd loop
# If something goes wrong, mox.VerifyAll() will catch it
try:
q_agent.daemon_loop()
except Exception:
pass
self.mox.VerifyAll()
class TunnelTestWithMTU(TunnelTest):
def setUp(self):
super(TunnelTestWithMTU, self).setUp()
self.VETH_MTU = 1500
self.inta.link.set_mtu(self.VETH_MTU)
self.intb.link.set_mtu(self.VETH_MTU)