# veth_mtu =
# Example: veth_mtu = 1504
+# (BoolOpt) Flag to enable l2-population extension. This option should only be
+# used in conjunction with ml2 plugin and l2population mechanism driver. It'll
+# enable plugin to populate remote ports macs and IPs (using fdb_add/remove
+# RPC calbbacks instead of tunnel_sync/update) on OVS agents in order to
+# optimize tunnel management.
+#
+# l2_population = False
+
[securitygroup]
# Firewall driver for realizing neutron security group function.
# firewall_driver = neutron.agent.firewall.NoopFirewallDriver
self.br_name = br_name
self.root_helper = root_helper
self.re_id = self.re_compile_id()
+ self.defer_apply_flows = False
+ self.deferred_flows = {'add': '', 'mod': '', 'del': ''}
def re_compile_id(self):
external = 'external_ids\s*'
args = ["clear", table_name, record, column]
self.run_vsctl(args)
- def run_ofctl(self, cmd, args):
+ def run_ofctl(self, cmd, args, process_input=None):
full_args = ["ovs-ofctl", cmd, self.br_name] + args
try:
- return utils.execute(full_args, root_helper=self.root_helper)
+ return utils.execute(full_args, root_helper=self.root_helper,
+ process_input=process_input)
except Exception as e:
LOG.error(_("Unable to execute %(cmd)s. Exception: %(exception)s"),
{'cmd': full_args, 'exception': e})
def add_flow(self, **kwargs):
flow_str = self.add_or_mod_flow_str(**kwargs)
- self.run_ofctl("add-flow", [flow_str])
+ if self.defer_apply_flows:
+ self.deferred_flows['add'] += flow_str + '\n'
+ else:
+ self.run_ofctl("add-flow", [flow_str])
def mod_flow(self, **kwargs):
flow_str = self.add_or_mod_flow_str(**kwargs)
- self.run_ofctl("mod-flows", [flow_str])
+ if self.defer_apply_flows:
+ self.deferred_flows['mod'] += flow_str + '\n'
+ else:
+ self.run_ofctl("mod-flows", [flow_str])
def delete_flows(self, **kwargs):
kwargs['delete'] = True
if "actions" in kwargs:
flow_expr_arr.append("actions=%s" % (kwargs["actions"]))
flow_str = ",".join(flow_expr_arr)
- self.run_ofctl("del-flows", [flow_str])
+ if self.defer_apply_flows:
+ self.deferred_flows['del'] += flow_str + '\n'
+ else:
+ self.run_ofctl("del-flows", [flow_str])
+
+ def defer_apply_on(self):
+ LOG.debug(_('defer_apply_on'))
+ self.defer_apply_flows = True
+
+ def defer_apply_off(self):
+ LOG.debug(_('defer_apply_off'))
+ for action, flows in self.deferred_flows.items():
+ if flows:
+ LOG.debug(_('Applying following deferred flows '
+ 'to bridge %s'), self.br_name)
+ for line in flows.splitlines():
+ LOG.debug(_('%(action)s: %(flow)s'),
+ {'action': action, 'flow': line})
+ self.run_ofctl('%s-flows' % action, ['-'], flows)
+ self.defer_apply_flows = False
+ self.deferred_flows = {'add': '', 'mod': '', 'del': ''}
def add_tunnel_port(self, port_name, remote_ip, local_ip,
tunnel_type=constants.TYPE_GRE,
vxlan_udp_port=constants.VXLAN_UDP_PORT):
- self.run_vsctl(["add-port", self.br_name, port_name])
+ self.run_vsctl(["--may-exist", "add-port", self.br_name, port_name])
self.set_db_attribute("Interface", port_name, "type", tunnel_type)
if tunnel_type == constants.TYPE_VXLAN:
# Only set the VXLAN UDP port if it's not the default
# @author: Francois Eleouet, Orange
# @author: Mathieu Rohon, Orange
-SUPPORTED_AGENT_TYPES = []
+from neutron.common import constants
+
+SUPPORTED_AGENT_TYPES = [constants.AGENT_TYPE_OVS]
import eventlet
from oslo.config import cfg
+from neutron.agent import l2population_rpc
from neutron.agent.linux import ip_lib
from neutron.agent.linux import ovs_lib
from neutron.agent import rpc as agent_rpc
self.physical_network = physical_network
self.segmentation_id = segmentation_id
self.vif_ports = vif_ports
+ # set of tunnel ports on which packets should be flooded
+ self.tun_ofports = set()
def __str__(self):
return ("lv-id = %s type = %s phys-net = %s phys-id = %s" %
self.init_firewall()
-class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
+class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin,
+ l2population_rpc.L2populationRpcCallBackMixin):
'''Implements OVS-based tunneling, VLANs and flat networks.
Two local bridges are created: an integration bridge (defaults to
def __init__(self, integ_br, tun_br, local_ip,
bridge_mappings, root_helper,
polling_interval, tunnel_types=None,
- veth_mtu=None):
+ veth_mtu=None, l2_population=False):
'''Constructor.
:param integ_br: name of the integration bridge.
self.available_local_vlans = set(xrange(q_const.MIN_VLAN_TAG,
q_const.MAX_VLAN_TAG))
self.tunnel_types = tunnel_types or []
+ self.l2_pop = l2_population
self.agent_state = {
'binary': 'neutron-openvswitch-agent',
'host': cfg.CONF.host,
'topic': q_const.L2_AGENT_TOPIC,
'configurations': {'bridge_mappings': bridge_mappings,
- 'tunnel_types': self.tunnel_types},
+ 'tunnel_types': self.tunnel_types,
+ 'tunneling_ip': local_ip,
+ 'l2_population': self.l2_pop},
'agent_type': q_const.AGENT_TYPE_OVS,
'start_flag': True}
self.setup_integration_br()
self.setup_physical_bridges(bridge_mappings)
self.local_vlan_map = {}
- self.tun_br_ofports = {constants.TYPE_GRE: set(),
- constants.TYPE_VXLAN: set()}
+ self.tun_br_ofports = {constants.TYPE_GRE: {},
+ constants.TYPE_VXLAN: {}}
self.polling_interval = polling_interval
[topics.NETWORK, topics.DELETE],
[constants.TUNNEL, topics.UPDATE],
[topics.SECURITY_GROUP, topics.UPDATE]]
+ if self.l2_pop:
+ consumers.append([topics.L2POPULATION,
+ topics.UPDATE, cfg.CONF.host])
self.connection = agent_rpc.create_consumers(self.dispatcher,
self.topic,
consumers)
# The network may not be defined on this agent
lvm = self.local_vlan_map.get(network_id)
if lvm:
- self.reclaim_local_vlan(network_id, lvm)
+ self.reclaim_local_vlan(network_id)
else:
LOG.debug(_("Network %s not used on agent."), network_id)
if tunnel_ip == self.local_ip:
return
tun_name = '%s-%s' % (tunnel_type, tunnel_id)
- self.setup_tunnel_port(tun_name, tunnel_ip, tunnel_type)
+ if not self.l2_pop:
+ self.setup_tunnel_port(tun_name, tunnel_ip, tunnel_type)
+
+ def fdb_add(self, context, fdb_entries):
+ LOG.debug(_("fdb_add received"))
+ for network_id, values in fdb_entries.items():
+ lvm = self.local_vlan_map.get(network_id)
+ if not lvm:
+ # Agent doesn't manage any port in this network
+ continue
+ agent_ports = values.get('ports')
+ agent_ports.pop(self.local_ip, None)
+ if len(agent_ports):
+ self.tun_br.defer_apply_on()
+ for agent_ip, ports in agent_ports.items():
+ # Ensure we have a tunnel port with this remote agent
+ ofport = self.tun_br_ofports[
+ lvm.network_type].get(agent_ip)
+ if not ofport:
+ port_name = '%s-%s' % (lvm.network_type, agent_ip)
+ ofport = self.setup_tunnel_port(port_name, agent_ip,
+ lvm.network_type)
+ if ofport == 0:
+ continue
+ for port in ports:
+ self._add_fdb_flow(port, agent_ip, lvm, ofport)
+ self.tun_br.defer_apply_off()
+
+ def fdb_remove(self, context, fdb_entries):
+ LOG.debug(_("fdb_remove received"))
+ for network_id, values in fdb_entries.items():
+ lvm = self.local_vlan_map.get(network_id)
+ if not lvm:
+ # Agent doesn't manage any more ports in this network
+ continue
+ agent_ports = values.get('ports')
+ agent_ports.pop(self.local_ip, None)
+ if len(agent_ports):
+ self.tun_br.defer_apply_on()
+ for agent_ip, ports in agent_ports.items():
+ ofport = self.tun_br_ofports[
+ lvm.network_type].get(agent_ip)
+ if not ofport:
+ continue
+ for port in ports:
+ self._del_fdb_flow(port, agent_ip, lvm, ofport)
+ self.tun_br.defer_apply_off()
+
+ def _add_fdb_flow(self, port_info, agent_ip, lvm, ofport):
+ if port_info == q_const.FLOODING_ENTRY:
+ lvm.tun_ofports.add(ofport)
+ ofports = ','.join(lvm.tun_ofports)
+ self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
+ priority=1,
+ dl_vlan=lvm.vlan,
+ actions="strip_vlan,set_tunnel:%s,"
+ "output:%s" % (lvm.segmentation_id, ofports))
+ else:
+ # TODO(feleouet): add ARP responder entry
+ self.tun_br.add_flow(table=constants.UCAST_TO_TUN,
+ priority=2,
+ dl_vlan=lvm.vlan,
+ dl_dst=port_info[0],
+ actions="strip_vlan,set_tunnel:%s,output:%s" %
+ (lvm.segmentation_id, ofport))
+
+ def _del_fdb_flow(self, port_info, agent_ip, lvm, ofport):
+ if port_info == q_const.FLOODING_ENTRY:
+ lvm.tun_ofports.remove(ofport)
+ if len(lvm.tun_ofports) > 0:
+ ofports = ','.join(lvm.tun_ofports)
+ self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
+ priority=1,
+ dl_vlan=lvm.vlan,
+ actions="strip_vlan,"
+ "set_tunnel:%s,output:%s" %
+ (lvm.segmentation_id, ofports))
+ else:
+ # This local vlan doesn't require any more tunelling
+ self.tun_br.delete_flows(table=constants.FLOOD_TO_TUN,
+ dl_vlan=lvm.vlan)
+ # Check if this tunnel port is still used
+ self.cleanup_tunnel_port(ofport, lvm.network_type)
+ else:
+ #TODO(feleouet): remove ARP responder entry
+ self.tun_br.delete_flows(table=constants.UCAST_TO_TUN,
+ dl_vlan=lvm.vlan,
+ dl_dst=port_info[0])
def create_rpc_dispatcher(self):
'''Get the rpc dispatcher for this manager.
if network_type in constants.TUNNEL_NETWORK_TYPES:
if self.enable_tunneling:
# outbound broadcast/multicast
- ofports = ','.join(self.tun_br_ofports[network_type])
- self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
- priority=1,
- dl_vlan=lvid,
- actions="strip_vlan,set_tunnel:%s,"
- "output:%s" % (segmentation_id, ofports))
+ ofports = ','.join(self.tun_br_ofports[network_type].values())
+ if ofports:
+ self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
+ priority=1,
+ dl_vlan=lvid,
+ actions="strip_vlan,"
+ "set_tunnel:%s,output:%s" %
+ (segmentation_id, ofports))
# inbound from tunnels: set lvid in the right table
# and resubmit to Table LEARN_FROM_TUN for mac learning
self.tun_br.add_flow(table=constants.TUN_TABLE[network_type],
{'network_type': network_type,
'net_uuid': net_uuid})
- def reclaim_local_vlan(self, net_uuid, lvm):
+ def reclaim_local_vlan(self, net_uuid):
'''Reclaim a local VLAN.
:param net_uuid: the network uuid associated with this vlan.
:param lvm: a LocalVLANMapping object that tracks (vlan, lsw_id,
vif_ids) mapping.
'''
+ lvm = self.local_vlan_map.pop(net_uuid, None)
+ if lvm is None:
+ LOG.debug(_("Network %s not used on agent."), net_uuid)
+ return
+
LOG.info(_("Reclaiming vlan = %(vlan_id)s from net-id = %(net_uuid)s"),
{'vlan_id': lvm.vlan,
'net_uuid': net_uuid})
table=constants.TUN_TABLE[lvm.network_type],
tun_id=lvm.segmentation_id)
self.tun_br.delete_flows(dl_vlan=lvm.vlan)
+ if self.l2_pop:
+ # Try to remove tunnel ports if not used by other networks
+ for ofport in lvm.tun_ofports:
+ self.cleanup_tunnel_port(ofport, lvm.network_type)
elif lvm.network_type == constants.TYPE_FLAT:
if lvm.physical_network in self.phys_brs:
# outbound
{'network_type': lvm.network_type,
'net_uuid': net_uuid})
- del self.local_vlan_map[net_uuid]
self.available_local_vlans.add(lvm.vlan)
def port_bound(self, port, net_uuid,
lvm.vif_ports.pop(vif_id, None)
if not lvm.vif_ports:
- self.reclaim_local_vlan(net_uuid, lvm)
+ self.reclaim_local_vlan(net_uuid)
def port_dead(self, port):
'''Once a port has no binding, put it on the "dead vlan".
if ofport < 0:
LOG.error(_("Failed to set-up %(type)s tunnel port to %(ip)s"),
{'type': tunnel_type, 'ip': remote_ip})
- else:
- self.tun_br_ofports[tunnel_type].add(ofport)
- # Add flow in default table to resubmit to the right
- # tunelling table (lvid will be set in the latter)
- self.tun_br.add_flow(priority=1,
- in_port=ofport,
- actions="resubmit(,%s)" %
- constants.TUN_TABLE[tunnel_type])
+ return 0
+
+ self.tun_br_ofports[tunnel_type][remote_ip] = ofport
+ # Add flow in default table to resubmit to the right
+ # tunelling table (lvid will be set in the latter)
+ self.tun_br.add_flow(priority=1,
+ in_port=ofport,
+ actions="resubmit(,%s)" %
+ constants.TUN_TABLE[tunnel_type])
+
+ ofports = ','.join(self.tun_br_ofports[tunnel_type].values())
+ if ofports and not self.l2_pop:
# Update flooding flows to include the new tunnel
- ofports = ','.join(self.tun_br_ofports[tunnel_type])
for network_id, vlan_mapping in self.local_vlan_map.iteritems():
if vlan_mapping.network_type == tunnel_type:
self.tun_br.mod_flow(table=constants.FLOOD_TO_TUN,
"set_tunnel:%s,output:%s" %
(vlan_mapping.segmentation_id,
ofports))
+ return ofport
+
+ def cleanup_tunnel_port(self, tun_ofport, tunnel_type):
+ # Check if this tunnel port is still used
+ for lvm in self.local_vlan_map.values():
+ if tun_ofport in lvm.tun_ofports:
+ break
+ # If not, remove it
+ else:
+ for remote_ip, ofport in self.tun_br_ofports[tunnel_type].items():
+ if ofport == tun_ofport:
+ port_name = '%s-%s' % (tunnel_type, remote_ip)
+ self.tun_br.delete_port(port_name)
+ self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
def treat_devices_added(self, devices):
resync = False
details = self.plugin_rpc.tunnel_sync(self.context,
self.local_ip,
tunnel_type)
- tunnels = details['tunnels']
- for tunnel in tunnels:
- if self.local_ip != tunnel['ip_address']:
- tunnel_id = tunnel.get('id', tunnel['ip_address'])
- tun_name = '%s-%s' % (tunnel_type, tunnel_id)
- self.setup_tunnel_port(tun_name,
- tunnel['ip_address'],
- tunnel_type)
+ if not self.l2_pop:
+ tunnels = details['tunnels']
+ for tunnel in tunnels:
+ if self.local_ip != tunnel['ip_address']:
+ tunnel_id = tunnel.get('id', tunnel['ip_address'])
+ tun_name = '%s-%s' % (tunnel_type, tunnel_id)
+ self.setup_tunnel_port(tun_name,
+ tunnel['ip_address'],
+ tunnel_type)
except Exception as e:
LOG.debug(_("Unable to sync tunnel IP %(local_ip)s: %(e)s"),
{'local_ip': self.local_ip, 'e': e})
polling_interval=config.AGENT.polling_interval,
tunnel_types=config.AGENT.tunnel_types,
veth_mtu=config.AGENT.veth_mtu,
+ l2_population=config.AGENT.l2_population,
)
# If enable_tunneling is TRUE, set tunnel_type to default to GRE
help=_("The UDP port to use for VXLAN tunnels.")),
cfg.IntOpt('veth_mtu', default=None,
help=_("MTU size of veth interfaces")),
+ cfg.BoolOpt('l2_population', default=False,
+ help=_("Use ml2 l2population mechanism driver to learn "
+ "remote mac and IPs and improve tunnel scalability")),
]
def test_count_flows(self):
utils.execute(["ovs-ofctl", "dump-flows", self.BR_NAME],
- root_helper=self.root_helper).AndReturn('ignore'
- '\nflow-1\n')
+ root_helper=self.root_helper,
+ process_input=None).AndReturn('ignore\nflow-1\n')
self.mox.ReplayAll()
# counts the number of flows as total lines of output - 2
self.br.delete_flows(dl_vlan=vid)
self.mox.VerifyAll()
+ def test_defer_apply_flows(self):
+ self.mox.StubOutWithMock(self.br, 'add_or_mod_flow_str')
+ self.br.add_or_mod_flow_str(
+ flow='added_flow_1').AndReturn('added_flow_1')
+ self.br.add_or_mod_flow_str(
+ flow='added_flow_2').AndReturn('added_flow_2')
+
+ self.mox.StubOutWithMock(self.br, '_build_flow_expr_arr')
+ self.br._build_flow_expr_arr(delete=True,
+ flow='deleted_flow_1'
+ ).AndReturn(['deleted_flow_1'])
+ self.mox.StubOutWithMock(self.br, 'run_ofctl')
+ self.br.run_ofctl('add-flows', ['-'], 'added_flow_1\nadded_flow_2\n')
+ self.br.run_ofctl('del-flows', ['-'], 'deleted_flow_1\n')
+ self.mox.ReplayAll()
+
+ self.br.defer_apply_on()
+ self.br.add_flow(flow='added_flow_1')
+ self.br.defer_apply_on()
+ self.br.add_flow(flow='added_flow_2')
+ self.br.delete_flows(flow='deleted_flow_1')
+ self.br.defer_apply_off()
+ self.mox.VerifyAll()
+
def test_add_tunnel_port(self):
pname = "tap99"
local_ip = "1.1.1.1"
remote_ip = "9.9.9.9"
ofport = "6"
- utils.execute(["ovs-vsctl", self.TO, "add-port",
+ utils.execute(["ovs-vsctl", self.TO, "--may-exist", "add-port",
self.BR_NAME, pname], root_helper=self.root_helper)
utils.execute(["ovs-vsctl", self.TO, "set", "Interface",
pname, "type=gre"], root_helper=self.root_helper)
from neutron.agent.linux import ip_lib
from neutron.agent.linux import ovs_lib
+from neutron.common import constants as n_const
from neutron.openstack.common.rpc import common as rpc_common
from neutron.plugins.openvswitch.agent import ovs_neutron_agent
from neutron.plugins.openvswitch.common import constants
)
def test_network_delete(self):
- with mock.patch.object(self.agent, "reclaim_local_vlan") as recl_fn:
+ with contextlib.nested(
+ mock.patch.object(self.agent, "reclaim_local_vlan"),
+ mock.patch.object(self.agent.tun_br, "cleanup_tunnel_port")
+ ) as (recl_fn, clean_tun_fn):
self.agent.network_delete("unused_context",
network_id="123")
self.assertFalse(recl_fn.called)
-
self.agent.local_vlan_map["123"] = "LVM object"
self.agent.network_delete("unused_context",
network_id="123")
- recl_fn.assert_called_with("123", self.agent.local_vlan_map["123"])
+ self.assertFalse(clean_tun_fn.called)
+ recl_fn.assert_called_with("123")
def test_port_update(self):
with contextlib.nested(
constants.MINIMUM_OVS_VXLAN_VERSION,
expecting_ok=False)
+ def _prepare_l2_pop_ofports(self):
+ lvm1 = mock.Mock()
+ lvm1.network_type = 'gre'
+ lvm1.vlan = 'vlan1'
+ lvm1.segmentation_id = 'seg1'
+ lvm1.tun_ofports = set(['1'])
+ lvm2 = mock.Mock()
+ lvm2.network_type = 'gre'
+ lvm2.vlan = 'vlan2'
+ lvm2.segmentation_id = 'seg2'
+ lvm2.tun_ofports = set(['1', '2'])
+ self.agent.local_vlan_map = {'net1': lvm1, 'net2': lvm2}
+ self.agent.tun_br_ofports = {'gre':
+ {'ip_agent_1': '1', 'ip_agent_2': '2'}}
+
+ def test_fdb_ignore_network(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {'net3': {}}
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'add_flow'),
+ mock.patch.object(self.agent.tun_br, 'delete_flows'),
+ mock.patch.object(self.agent, 'setup_tunnel_port'),
+ mock.patch.object(self.agent, 'cleanup_tunnel_port')
+ ) as (add_flow_fn, del_flow_fn, add_tun_fn, clean_tun_fn):
+ self.agent.fdb_add(None, fdb_entry)
+ self.assertFalse(add_flow_fn.called)
+ self.assertFalse(add_tun_fn.called)
+ self.agent.fdb_remove(None, fdb_entry)
+ self.assertFalse(del_flow_fn.called)
+ self.assertFalse(clean_tun_fn.called)
+
+ def test_fdb_ignore_self(self):
+ self._prepare_l2_pop_ofports()
+ self.agent.local_ip = 'agent_ip'
+ fdb_entry = {'net2':
+ {'network_type': 'gre',
+ 'segment_id': 'tun2',
+ 'ports':
+ {'agent_ip':
+ [['mac', 'ip'],
+ n_const.FLOODING_ENTRY]}}}
+ with mock.patch.object(self.agent.tun_br,
+ "defer_apply_on") as defer_fn:
+ self.agent.fdb_add(None, fdb_entry)
+ self.assertFalse(defer_fn.called)
+
+ self.agent.fdb_remove(None, fdb_entry)
+ self.assertFalse(defer_fn.called)
+
+ def test_fdb_add_flows(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {'net1':
+ {'network_type': 'gre',
+ 'segment_id': 'tun1',
+ 'ports':
+ {'ip_agent_2':
+ [['mac', 'ip'],
+ n_const.FLOODING_ENTRY]}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'add_flow'),
+ mock.patch.object(self.agent.tun_br, 'mod_flow'),
+ mock.patch.object(self.agent.tun_br, 'setup_tunnel_port'),
+ ) as (add_flow_fn, mod_flow_fn, add_tun_fn):
+ add_tun_fn.return_value = '2'
+ self.agent.fdb_add(None, fdb_entry)
+ add_flow_fn.assert_called_with(table=constants.UCAST_TO_TUN,
+ priority=2,
+ dl_vlan='vlan1',
+ dl_dst='mac',
+ actions='strip_vlan,'
+ 'set_tunnel:seg1,output:2')
+ mod_flow_fn.assert_called_with(table=constants.FLOOD_TO_TUN,
+ priority=1,
+ dl_vlan='vlan1',
+ actions='strip_vlan,'
+ 'set_tunnel:seg1,output:1,2')
+
+ def test_fdb_del_flows(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {'net2':
+ {'network_type': 'gre',
+ 'segment_id': 'tun2',
+ 'ports':
+ {'ip_agent_2':
+ [['mac', 'ip'],
+ n_const.FLOODING_ENTRY]}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'mod_flow'),
+ mock.patch.object(self.agent.tun_br, 'delete_flows'),
+ ) as (mod_flow_fn, del_flow_fn):
+ self.agent.fdb_remove(None, fdb_entry)
+ del_flow_fn.assert_called_with(table=constants.UCAST_TO_TUN,
+ dl_vlan='vlan2',
+ dl_dst='mac')
+ mod_flow_fn.assert_called_with(table=constants.FLOOD_TO_TUN,
+ priority=1,
+ dl_vlan='vlan2',
+ actions='strip_vlan,'
+ 'set_tunnel:seg2,output:1')
+
+ def test_fdb_add_port(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {'net1':
+ {'network_type': 'gre',
+ 'segment_id': 'tun1',
+ 'ports': {'ip_agent_1': [['mac', 'ip']]}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'add_flow'),
+ mock.patch.object(self.agent.tun_br, 'mod_flow'),
+ mock.patch.object(self.agent, 'setup_tunnel_port')
+ ) as (add_flow_fn, mod_flow_fn, add_tun_fn):
+ self.agent.fdb_add(None, fdb_entry)
+ self.assertFalse(add_tun_fn.called)
+ fdb_entry['net1']['ports']['ip_agent_3'] = [['mac', 'ip']]
+ self.agent.fdb_add(None, fdb_entry)
+ add_tun_fn.assert_called_with('gre-ip_agent_3', 'ip_agent_3',
+ 'gre')
+
+ def test_fdb_del_port(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {'net2':
+ {'network_type': 'gre',
+ 'segment_id': 'tun2',
+ 'ports': {'ip_agent_2': [n_const.FLOODING_ENTRY]}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'delete_flows'),
+ mock.patch.object(self.agent.tun_br, 'delete_port')
+ ) as (del_flow_fn, del_port_fn):
+ self.agent.fdb_remove(None, fdb_entry)
+ del_port_fn.assert_called_once_with('gre-ip_agent_2')
+
+ def test_recl_lv_port_to_preserve(self):
+ self._prepare_l2_pop_ofports()
+ self.agent.l2_pop = True
+ self.agent.enable_tunneling = True
+ with mock.patch.object(
+ self.agent.tun_br, 'cleanup_tunnel_port'
+ ) as clean_tun_fn:
+ self.agent.reclaim_local_vlan('net1')
+ self.assertFalse(clean_tun_fn.called)
+
+ def test_recl_lv_port_to_remove(self):
+ self._prepare_l2_pop_ofports()
+ self.agent.l2_pop = True
+ self.agent.enable_tunneling = True
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'delete_port'),
+ mock.patch.object(self.agent.tun_br, 'delete_flows')
+ ) as (del_port_fn, del_flow_fn):
+ self.agent.reclaim_local_vlan('net2')
+ del_port_fn.assert_called_once_with('gre-ip_agent_2')
+
class AncillaryBridgesTest(base.BaseTestCase):
LVM_VLAN = ovs_neutron_agent.LocalVLANMapping(
LV_ID, 'vlan', 'net1', LS_ID, VIF_PORTS)
-GRE_OFPORTS = set(['11', '12'])
-VXLAN_OFPORTS = set(['13', '14'])
-TUN_OFPORTS = {constants.TYPE_GRE: GRE_OFPORTS,
- constants.TYPE_VXLAN: VXLAN_OFPORTS}
+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"
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, ','.join(GRE_OFPORTS)))
+ (LS_ID, ofports))
self.mock_tun_bridge.add_flow(table=constants.TUN_TABLE['gre'],
priority=1,
self.VETH_MTU)
a.available_local_vlans = set()
a.local_vlan_map[NET_UUID] = LVM
- a.reclaim_local_vlan(NET_UUID, LVM)
+ a.reclaim_local_vlan(NET_UUID)
self.assertTrue(LVM.vlan in a.available_local_vlans)
self.mox.VerifyAll()
a.available_local_vlans = set()
a.local_vlan_map[NET_UUID] = LVM_FLAT
- a.reclaim_local_vlan(NET_UUID, LVM_FLAT)
+ a.reclaim_local_vlan(NET_UUID)
self.assertTrue(LVM_FLAT.vlan in a.available_local_vlans)
self.mox.VerifyAll()
a.available_local_vlans = set()
a.local_vlan_map[NET_UUID] = LVM_VLAN
- a.reclaim_local_vlan(NET_UUID, LVM_VLAN)
+ a.reclaim_local_vlan(NET_UUID)
self.assertTrue(LVM_VLAN.vlan in a.available_local_vlans)
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, LVM)
+ ovs_neutron_agent.OVSNeutronAgent.reclaim_local_vlan(NET_UUID)
self.mox.ReplayAll()