from oslo.config import cfg
import six
+from neutron.common import constants as n_const
from neutron.common import log
+from neutron.openstack.common import log as logging
+
+LOG = logging.getLogger(__name__)
@six.add_metaclass(abc.ABCMeta)
class L2populationRpcCallBackMixin(object):
+ '''General mixin class of L2-population RPC call back.
+
+ The following methods are called through RPC.
+ add_fdb_entries(), remove_fdb_entries(), update_fdb_entries()
+ The following methods are used in a agent as an internal method.
+ fdb_add(), fdb_remove(), fdb_update()
+ '''
@log.log
def add_fdb_entries(self, context, fdb_entries, host=None):
@abc.abstractmethod
def fdb_update(self, context, fdb_entries):
pass
+
+
+class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin):
+ '''Mixin class of L2-population call back for Tunnel.
+
+ The following all methods are used in a agent as an internal method.
+ '''
+
+ @abc.abstractmethod
+ def add_fdb_flow(self, port_info, remote_ip, lvm, ofport):
+ '''Add flow for fdb
+
+ This method assumes to be used by method fdb_add_tun.
+ We expect to add a flow entry to send a packet to specified port
+ on bridge.
+ And you may edit some information for local arp respond.
+
+ :param port_info: list to include mac and ip.
+ [mac, ip]
+ :remote_ip: remote ip address.
+ :param lvm: a local VLAN map of network.
+ :param ofport: a port to add.
+ '''
+ pass
+
+ @abc.abstractmethod
+ def del_fdb_flow(self, port_info, remote_ip, lvm, ofport):
+ '''Delete flow for fdb
+
+ This method assumes to be used by method fdb_remove_tun.
+ We expect to delete a flow entry to send a packet to specified port
+ from bridge.
+ And you may delete some information for local arp respond.
+
+ :param port_info: a list to contain mac and ip.
+ [mac, ip]
+ :remote_ip: remote ip address.
+ :param lvm: local VLAN map of network.
+ :param ofport: a port to delete.
+ '''
+ pass
+
+ @abc.abstractmethod
+ def setup_tunnel_port(self, remote_ip, network_type):
+ '''Setup an added tunnel port.
+
+ This method assumes to be used by method fdb_add_tun.
+ We expect to prepare to call add_fdb_flow. It will be mainly adding
+ a port to a bridge.
+ If you need, you may do some preparation for a bridge.
+
+ :param remote_ip: an ip for port to setup.
+ :param network_type: a type of network.
+ :returns: a ofport value. the value 0 means to be unavailable port.
+ '''
+ pass
+
+ @abc.abstractmethod
+ def cleanup_tunnel_port(self, tun_ofport, tunnel_type):
+ '''Clean up a deleted tunnel port.
+
+ This method assumes to be used by method fdb_remove_tun.
+ We expect to clean up after calling del_fdb_flow. It will be mainly
+ deleting a port from a bridge.
+ If you need, you may do some cleanup for a bridge.
+
+ :param tun_ofport: a port value to cleanup.
+ :param tunnel_type: a type of tunnel.
+ '''
+ pass
+
+ def get_agent_ports(self, fdb_entries, local_vlan_map):
+ for network_id, values in fdb_entries.items():
+ lvm = local_vlan_map.get(network_id)
+ agent_ports = values.get('ports') if lvm else {}
+ yield (lvm, agent_ports)
+
+ @log.log
+ def fdb_add_tun(self, context, lvm, agent_ports, ofports):
+ for remote_ip, ports in agent_ports.items():
+ # Ensure we have a tunnel port with this remote agent
+ ofport = ofports[lvm.network_type].get(remote_ip)
+ if not ofport:
+ ofport = self.setup_tunnel_port(remote_ip, lvm.network_type)
+ if ofport == 0:
+ continue
+ for port in ports:
+ self.add_fdb_flow(port, remote_ip, lvm, ofport)
+
+ @log.log
+ def fdb_remove_tun(self, context, lvm, agent_ports, ofports):
+ for remote_ip, ports in agent_ports.items():
+ ofport = ofports[lvm.network_type].get(remote_ip)
+ if not ofport:
+ continue
+ for port in ports:
+ self.del_fdb_flow(port, remote_ip, lvm, ofport)
+ if port == n_const.FLOODING_ENTRY:
+ # Check if this tunnel port is still used
+ self.cleanup_tunnel_port(ofport, lvm.network_type)
+
+ @log.log
+ def fdb_update(self, context, fdb_entries):
+ '''Call methods named '_fdb_<action>'.
+
+ This method assumes that methods '_fdb_<action>' are defined in class.
+ Currently the following actions are available.
+ chg_ip
+ '''
+ for action, values in fdb_entries.items():
+ method = '_fdb_' + action
+ if not hasattr(self, method):
+ raise NotImplementedError()
+
+ getattr(self, method)(context, values)
from neutron.common import constants
SUPPORTED_AGENT_TYPES = [constants.AGENT_TYPE_OVS,
- constants.AGENT_TYPE_LINUXBRIDGE]
+ constants.AGENT_TYPE_LINUXBRIDGE,
+ constants.AGENT_TYPE_OFA]
from ryu.lib import hub
from ryu.ofproto import ofproto_v1_3 as ryu_ofp13
+from neutron.agent import l2population_rpc
from neutron.agent.linux import ip_lib
from neutron.agent.linux import ovs_lib
from neutron.agent.linux import polling
class OFANeutronAgent(n_rpc.RpcCallback,
- sg_rpc.SecurityGroupAgentRpcCallbackMixin):
+ sg_rpc.SecurityGroupAgentRpcCallbackMixin,
+ l2population_rpc.L2populationRpcCallBackTunnelMixin):
"""A agent for OpenFlow Agent ML2 mechanism driver.
OFANeutronAgent is a OpenFlow Agent agent for a ML2 plugin.
This is as a ryu application thread.
+ This has the following features.
- An agent acts as an OpenFlow controller on each compute nodes.
- OpenFlow 1.3 (vendor agnostic unlike OVS extensions).
+ - l2-population is mandatory.
"""
# history
def __init__(self, ryuapp, integ_br, tun_br, local_ip,
bridge_mappings, root_helper,
polling_interval, tunnel_types=None,
- veth_mtu=None, l2_population=False,
- minimize_polling=False,
+ veth_mtu=None, minimize_polling=False,
ovsdb_monitor_respawn_interval=(
constants.DEFAULT_OVSDBMON_RESPAWN)):
"""Constructor.
self.available_local_vlans = set(xrange(n_const.MIN_VLAN_TAG,
n_const.MAX_VLAN_TAG))
self.tunnel_types = tunnel_types or []
- self.l2_pop = l2_population
self.agent_state = {
'binary': 'neutron-ofa-agent',
'host': cfg.CONF.host,
'configurations': {'bridge_mappings': bridge_mappings,
'tunnel_types': self.tunnel_types,
'tunneling_ip': local_ip,
- 'l2_population': self.l2_pop},
+ 'l2_population': True},
'agent_type': n_const.AGENT_TYPE_OFA,
'start_flag': True}
# Define the listening consumers for the agent
consumers = [[topics.PORT, topics.UPDATE],
[topics.NETWORK, topics.DELETE],
- [constants.TUNNEL, topics.UPDATE],
- [topics.SECURITY_GROUP, topics.UPDATE]]
+ [topics.SECURITY_GROUP, topics.UPDATE],
+ [topics.L2POPULATION, topics.UPDATE, cfg.CONF.host]]
self.connection = agent_rpc.create_consumers(self.endpoints,
self.topic,
consumers)
# they are not used since there is no guarantee the notifications
# are processed in the same order as the relevant API requests
self.updated_ports.add(ports.get_normalized_port_name(port['id']))
- LOG.debug(_("port_update received port %s"), port['id'])
+ LOG.debug("port_update received port %s", port['id'])
+
+ def fdb_add(self, context, fdb_entries):
+ LOG.debug("fdb_add received")
+ for lvm, agent_ports in self.get_agent_ports(fdb_entries,
+ self.local_vlan_map):
+ agent_ports.pop(self.local_ip, None)
+ if len(agent_ports):
+ self.fdb_add_tun(context, lvm, agent_ports,
+ self.tun_br_ofports)
+
+ def fdb_remove(self, context, fdb_entries):
+ LOG.debug("fdb_remove received")
+ for lvm, agent_ports in self.get_agent_ports(fdb_entries,
+ self.local_vlan_map):
+ agent_ports.pop(self.local_ip, None)
+ if len(agent_ports):
+ self.fdb_remove_tun(context, lvm, agent_ports,
+ self.tun_br_ofports)
+
+ def _add_fdb_flooding_flow(self, lvm):
+ datapath = self.tun_br.datapath
+ ofp = datapath.ofproto
+ ofpp = datapath.ofproto_parser
+ match = ofpp.OFPMatch(
+ vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
+ actions = [ofpp.OFPActionPopVlan(),
+ ofpp.OFPActionSetField(
+ tunnel_id=int(lvm.segmentation_id))]
+ for tun_ofport in lvm.tun_ofports:
+ actions.append(ofpp.OFPActionOutput(int(tun_ofport), 0))
+ instructions = [ofpp.OFPInstructionActions(
+ ofp.OFPIT_APPLY_ACTIONS, actions)]
+ msg = ofpp.OFPFlowMod(datapath,
+ table_id=constants.FLOOD_TO_TUN,
+ command=ofp.OFPFC_ADD,
+ priority=1,
+ match=match, instructions=instructions)
+ self.ryu_send_msg(msg)
- def tunnel_update(self, context, **kwargs):
- LOG.debug(_("tunnel_update received"))
- if not self.enable_tunneling:
- return
- tunnel_ip = kwargs.get('tunnel_ip')
- tunnel_type = kwargs.get('tunnel_type')
- if not tunnel_type:
- LOG.error(_("No tunnel_type specified, cannot create tunnels"))
- return
- if tunnel_type not in self.tunnel_types:
- LOG.error(_("tunnel_type %s not supported by agent"), tunnel_type)
- return
- if tunnel_ip == self.local_ip:
- return
- tun_name = self._create_tunnel_port_name(tunnel_type, tunnel_ip)
- if not tun_name:
- return
- self.setup_tunnel_port(tun_name, tunnel_ip, tunnel_type)
+ def add_fdb_flow(self, port_info, remote_ip, lvm, ofport):
+ datapath = self.tun_br.datapath
+ ofp = datapath.ofproto
+ ofpp = datapath.ofproto_parser
+ if port_info == n_const.FLOODING_ENTRY:
+ lvm.tun_ofports.add(ofport)
+ self._add_fdb_flooding_flow(lvm)
+ else:
+ match = ofpp.OFPMatch(
+ vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
+ eth_dst=port_info[0])
+ actions = [ofpp.OFPActionPopVlan(),
+ ofpp.OFPActionSetField(
+ tunnel_id=int(lvm.segmentation_id)),
+ ofpp.OFPActionOutput(int(ofport), 0)]
+ instructions = [ofpp.OFPInstructionActions(
+ ofp.OFPIT_APPLY_ACTIONS, actions)]
+ msg = ofpp.OFPFlowMod(datapath,
+ table_id=constants.UCAST_TO_TUN,
+ command=ofp.OFPFC_ADD,
+ priority=2,
+ match=match, instructions=instructions)
+ self.ryu_send_msg(msg)
- def _provision_local_vlan_outbound_for_tunnel(self, lvid,
- segmentation_id, ofports):
- br = self.tun_br
- match = br.ofparser.OFPMatch(
- vlan_vid=int(lvid) | ryu_ofp13.OFPVID_PRESENT)
- actions = [br.ofparser.OFPActionPopVlan(),
- br.ofparser.OFPActionSetField(
- tunnel_id=int(segmentation_id))]
- for ofport in ofports:
- actions.append(br.ofparser.OFPActionOutput(ofport, 0))
- instructions = [br.ofparser.OFPInstructionActions(
- ryu_ofp13.OFPIT_APPLY_ACTIONS, actions)]
- msg = br.ofparser.OFPFlowMod(
- br.datapath,
- table_id=constants.FLOOD_TO_TUN,
- priority=1,
- match=match, instructions=instructions)
- self.ryu_send_msg(msg)
+ def del_fdb_flow(self, port_info, remote_ip, lvm, ofport):
+ datapath = self.tun_br.datapath
+ ofp = datapath.ofproto
+ ofpp = datapath.ofproto_parser
+ if port_info == n_const.FLOODING_ENTRY:
+ lvm.tun_ofports.remove(ofport)
+ if len(lvm.tun_ofports) > 0:
+ self._add_fdb_flooding_flow(lvm)
+ else:
+ # This local vlan doesn't require any more tunelling
+ match = ofpp.OFPMatch(
+ vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
+ msg = ofpp.OFPFlowMod(datapath,
+ table_id=constants.FLOOD_TO_TUN,
+ command=ofp.OFPFC_DELETE,
+ out_group=ofp.OFPG_ANY,
+ out_port=ofp.OFPP_ANY,
+ match=match)
+ self.ryu_send_msg(msg)
+ else:
+ match = ofpp.OFPMatch(
+ vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
+ eth_dst=port_info[0])
+ msg = ofpp.OFPFlowMod(datapath,
+ table_id=constants.UCAST_TO_TUN,
+ command=ofp.OFPFC_DELETE,
+ out_group=ofp.OFPG_ANY,
+ out_port=ofp.OFPP_ANY,
+ match=match)
+ self.ryu_send_msg(msg)
def _provision_local_vlan_inbound_for_tunnel(self, lvid, network_type,
segmentation_id):
self.ryu_send_msg(msg)
def _local_vlan_for_tunnel(self, lvid, network_type, segmentation_id):
- ofports = [int(ofport) for ofport in
- self.tun_br_ofports[network_type].values()]
- if ofports:
- self._provision_local_vlan_outbound_for_tunnel(
- lvid, segmentation_id, ofports)
self._provision_local_vlan_inbound_for_tunnel(lvid, network_type,
segmentation_id)
out_port=ryu_ofp13.OFPP_ANY,
match=match)
self.ryu_send_msg(msg)
+ # 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 in (p_const.TYPE_FLAT, p_const.TYPE_VLAN):
if lvm.physical_network in self.phys_brs:
self._reclaim_local_vlan_outbound(lvm)
else:
LOG.debug(_("No VIF port for port %s defined on agent."), port_id)
- def setup_tunnel_port(self, port_name, remote_ip, tunnel_type):
+ def _setup_tunnel_port(self, port_name, remote_ip, tunnel_type):
ofport = self.tun_br.add_tunnel_port(port_name,
remote_ip,
self.local_ip,
match=match,
instructions=instructions)
self.ryu_send_msg(msg)
+ return ofport
- ofports = [int(p) for p in self.tun_br_ofports[tunnel_type].values()]
- if ofports:
- # Update flooding flows to include the new tunnel
- for network_id, vlan_mapping in self.local_vlan_map.iteritems():
- if vlan_mapping.network_type == tunnel_type:
- match = self.tun_br.ofparser.OFPMatch(
- vlan_vid=int(vlan_mapping.vlan) |
- ryu_ofp13.OFPVID_PRESENT)
- actions = [
- self.tun_br.ofparser.OFPActionPopVlan(),
- self.tun_br.ofparser.OFPActionSetField(
- tunnel_id=int(vlan_mapping.segmentation_id))]
- actions.extend(
- self.tun_br.ofparser.OFPActionOutput(p, 0)
- for p in ofports
- )
- instructions = [
- self.tun_br.ofparser.OFPInstructionActions(
- ryu_ofp13.OFPIT_APPLY_ACTIONS,
- actions)]
- msg = self.tun_br.ofparser.OFPFlowMod(
- self.tun_br.datapath,
- table_id=constants.FLOOD_TO_TUN,
- priority=1,
- match=match,
- instructions=instructions)
- self.ryu_send_msg(msg)
+ def setup_tunnel_port(self, remote_ip, network_type):
+ port_name = self._create_tunnel_port_name(network_type, remote_ip)
+ if not port_name:
+ return 0
+ ofport = self._setup_tunnel_port(port_name,
+ remote_ip,
+ network_type)
return ofport
+ def _remove_tunnel_port(self, tun_ofport, tunnel_type):
+ datapath = self.tun_br.datapath
+ ofp = datapath.ofproto
+ ofpp = datapath.ofproto_parser
+ for remote_ip, ofport in self.tun_br_ofports[tunnel_type].items():
+ if ofport == tun_ofport:
+ port_name = self._create_tunnel_port_name(tunnel_type,
+ remote_ip)
+ if port_name:
+ self.tun_br.delete_port(port_name)
+ match = ofpp.OFPMatch(in_port=int(ofport))
+ msg = ofpp.OFPFlowMod(datapath,
+ command=ofp.OFPFC_DELETE,
+ out_group=ofp.OFPG_ANY,
+ out_port=ofp.OFPP_ANY,
+ match=match)
+ self.ryu_send_msg(msg)
+ self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
+
+ 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:
+ self._remove_tunnel_port(tun_ofport, tunnel_type)
+
def treat_devices_added_or_updated(self, devices):
resync = False
all_ports = dict((p.normalized_port_name(), p) for p in
resync = False
try:
for tunnel_type in self.tunnel_types:
- 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']:
- tun_name = self._create_tunnel_port_name(
- tunnel_type, tunnel['ip_address'])
- if not tun_name:
- continue
- self.setup_tunnel_port(tun_name,
- tunnel['ip_address'],
- tunnel_type)
+ self.plugin_rpc.tunnel_sync(self.context,
+ self.local_ip,
+ 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})
minimize_polling=config.AGENT.minimize_polling,
tunnel_types=config.AGENT.tunnel_types,
veth_mtu=config.AGENT.veth_mtu,
- l2_population=False,
ovsdb_monitor_respawn_interval=constants.DEFAULT_OVSDBMON_RESPAWN,
)
class OVSNeutronAgent(n_rpc.RpcCallback,
sg_rpc.SecurityGroupAgentRpcCallbackMixin,
- l2population_rpc.L2populationRpcCallBackMixin,
+ l2population_rpc.L2populationRpcCallBackTunnelMixin,
dvr_rpc.DVRAgentRpcCallbackMixin):
'''Implements OVS-based tunneling, VLANs and flat networks.
return
tun_name = '%s-%s' % (tunnel_type, tunnel_id)
if not self.l2_pop:
- self.setup_tunnel_port(tun_name, tunnel_ip, tunnel_type)
+ 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')
+ LOG.debug("fdb_add received")
+ for lvm, agent_ports in self.get_agent_ports(fdb_entries,
+ self.local_vlan_map):
agent_ports.pop(self.local_ip, None)
if len(agent_ports):
if not self.enable_distributed_routing:
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:
- remote_ip_hex = self.get_ip_in_hex(agent_ip)
- if not remote_ip_hex:
- continue
- port_name = '%s-%s' % (lvm.network_type, remote_ip_hex)
- 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.fdb_add_tun(context, lvm, agent_ports,
+ self.tun_br_ofports)
if not self.enable_distributed_routing:
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')
+ LOG.debug("fdb_remove received")
+ for lvm, agent_ports in self.get_agent_ports(fdb_entries,
+ self.local_vlan_map):
agent_ports.pop(self.local_ip, None)
if len(agent_ports):
if not self.enable_distributed_routing:
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.fdb_remove_tun(context, lvm, agent_ports,
+ self.tun_br_ofports)
if not self.enable_distributed_routing:
self.tun_br.defer_apply_off()
- def _add_fdb_flow(self, port_info, agent_ip, lvm, ofport):
+ def add_fdb_flow(self, port_info, remote_ip, lvm, ofport):
if port_info == q_const.FLOODING_ENTRY:
lvm.tun_ofports.add(ofport)
ofports = ','.join(lvm.tun_ofports)
"output:%s" %
(lvm.segmentation_id, ofport))
- def _del_fdb_flow(self, port_info, agent_ip, lvm, ofport):
+ def del_fdb_flow(self, port_info, remote_ip, lvm, ofport):
if port_info == q_const.FLOODING_ENTRY:
lvm.tun_ofports.remove(ofport)
if len(lvm.tun_ofports) > 0:
# This local vlan doesn't require any more tunnelling
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:
self._set_arp_responder('remove', lvm.vlan, port_info[0],
port_info[1])
for mac, ip in before:
self._set_arp_responder('remove', lvm.vlan, mac, ip)
- def fdb_update(self, context, fdb_entries):
- LOG.debug(_("fdb_update received"))
- for action, values in fdb_entries.items():
- method = '_fdb_' + action
- if not hasattr(self, method):
- raise NotImplementedError()
-
- getattr(self, method)(context, values)
-
def _set_arp_responder(self, action, lvid, mac_str, ip_str):
'''Set the ARP respond entry.
else:
LOG.debug(_("No VIF port for port %s defined on agent."), port_id)
- def setup_tunnel_port(self, port_name, remote_ip, tunnel_type):
+ def _setup_tunnel_port(self, port_name, remote_ip, tunnel_type):
ofport = self.tun_br.add_tunnel_port(port_name,
remote_ip,
self.local_ip,
ofports))
return ofport
+ def setup_tunnel_port(self, remote_ip, network_type):
+ remote_ip_hex = self.get_ip_in_hex(remote_ip)
+ if not remote_ip_hex:
+ return 0
+ port_name = '%s-%s' % (network_type, remote_ip_hex)
+ ofport = self._setup_tunnel_port(port_name,
+ remote_ip,
+ network_type)
+ 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():
continue
tun_name = '%s-%s' % (tunnel_type,
tunnel_id or remote_ip_hex)
- self.setup_tunnel_port(tun_name,
- tunnel['ip_address'],
- tunnel_type)
+ 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})
--- /dev/null
+# Copyright (C) 2014 VA Linux Systems Japan K.K.
+#
+# 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: Fumihiko Kakuma, VA Linux Systems Japan K.K.
+
+import collections
+
+from neutron.agent import l2population_rpc
+from neutron.plugins.openvswitch.agent import ovs_neutron_agent
+from neutron.tests import base
+
+
+class FakeNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin):
+
+ def fdb_add(self, context, fdb_entries):
+ pass
+
+ def fdb_remove(self, context, fdb_entries):
+ pass
+
+ def add_fdb_flow(self, port_info, remote_ip, lvm, ofport):
+ pass
+
+ def del_fdb_flow(self, port_info, remote_ip, lvm, ofport):
+ pass
+
+ def setup_tunnel_port(self, remote_ip, network_type):
+ pass
+
+ def cleanup_tunnel_port(self, tun_ofport, tunnel_type):
+ pass
+
+
+class TestL2populationRpcCallBackTunnelMixinBase(base.BaseTestCase):
+
+ def setUp(self):
+ super(TestL2populationRpcCallBackTunnelMixinBase, self).setUp()
+ self.fakeagent = FakeNeutronAgent()
+ Port = collections.namedtuple('Port', 'ip, ofport')
+ LVM = collections.namedtuple(
+ 'LVM', 'net, vlan, phys, segid, mac, ip, vif, port')
+
+ self.local_ip = '127.0.0.1'
+ self.type_gre = 'gre'
+ self.ports = [Port(ip='10.1.0.1', ofport='ofport1'),
+ Port(ip='10.1.0.2', ofport='ofport2'),
+ Port(ip='10.1.0.3', ofport='ofport3')]
+ self.ofports = {
+ self.type_gre: {
+ self.ports[0].ip: self.ports[0].ofport,
+ self.ports[1].ip: self.ports[1].ofport,
+ self.ports[2].ip: self.ports[2].ofport,
+ }
+ }
+
+ self.lvms = [LVM(net='net1', vlan=1, phys='phys1', segid='tun1',
+ mac='mac1', ip='1.1.1.1', vif='vifid1',
+ port='port1'),
+ LVM(net='net2', vlan=2, phys='phys2', segid='tun2',
+ mac='mac2', ip='2.2.2.2', vif='vifid2',
+ port='port2'),
+ LVM(net='net3', vlan=3, phys='phys3', segid='tun3',
+ mac='mac3', ip='3.3.3.3', vif='vifid3',
+ port='port3')]
+
+ self.agent_ports = {
+ self.ports[0].ip: [[self.lvms[0].mac, self.lvms[0].ip]],
+ self.ports[1].ip: [[self.lvms[1].mac, self.lvms[1].ip]],
+ self.ports[2].ip: [[self.lvms[2].mac, self.lvms[2].ip]],
+ }
+
+ self.fdb_entries1 = {
+ self.lvms[0].net: {
+ 'network_type': self.type_gre,
+ 'segment_id': self.lvms[0].segid,
+ 'ports': {
+ self.local_ip: [],
+ self.ports[0].ip: [[self.lvms[0].mac, self.lvms[0].ip]]},
+ },
+ self.lvms[1].net: {
+ 'network_type': self.type_gre,
+ 'segment_id': self.lvms[1].segid,
+ 'ports': {
+ self.local_ip: [],
+ self.ports[1].ip: [[self.lvms[1].mac, self.lvms[1].ip]]},
+ },
+ self.lvms[2].net: {
+ 'network_type': self.type_gre,
+ 'segment_id': self.lvms[2].segid,
+ 'ports': {
+ self.local_ip: [],
+ self.ports[2].ip: [[self.lvms[2].mac, self.lvms[2].ip]]},
+ },
+ }
+
+ self.lvm1 = ovs_neutron_agent.LocalVLANMapping(
+ self.lvms[0].vlan, self.type_gre, self.lvms[0].phys,
+ self.lvms[0].segid, {self.lvms[0].vif: self.lvms[0].port})
+ self.lvm2 = ovs_neutron_agent.LocalVLANMapping(
+ self.lvms[1].vlan, self.type_gre, self.lvms[1].phys,
+ self.lvms[1].segid, {self.lvms[1].vif: self.lvms[1].port})
+ self.lvm3 = ovs_neutron_agent.LocalVLANMapping(
+ self.lvms[2].vlan, self.type_gre, self.lvms[2].phys,
+ self.lvms[2].segid, {self.lvms[2].vif: self.lvms[2].port})
+
+ self.local_vlan_map1 = {
+ self.lvms[0].net: self.lvm1,
+ self.lvms[1].net: self.lvm2,
+ self.lvms[2].net: self.lvm3,
+ }
+
+ self.upd_fdb_entry1_val = {
+ self.lvms[0].net: {
+ self.ports[0].ip: {
+ 'before': [[self.lvms[0].mac, self.lvms[0].ip]],
+ 'after': [[self.lvms[1].mac, self.lvms[1].ip]],
+ },
+ self.ports[1].ip: {
+ 'before': [[self.lvms[0].mac, self.lvms[0].ip]],
+ 'after': [[self.lvms[1].mac, self.lvms[1].ip]],
+ },
+ },
+ self.lvms[1].net: {
+ self.ports[2].ip: {
+ 'before': [[self.lvms[0].mac, self.lvms[0].ip]],
+ 'after': [[self.lvms[2].mac, self.lvms[2].ip]],
+ },
+ },
+ }
+ self.upd_fdb_entry1 = {'chg_ip': self.upd_fdb_entry1_val}
--- /dev/null
+# Copyright (C) 2014 VA Linux Systems Japan K.K.
+#
+# 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: Fumihiko Kakuma, VA Linux Systems Japan K.K.
+
+import contextlib
+
+import mock
+
+from neutron.common import constants as n_const
+from neutron.tests.unit.agent import l2population_rpc_base
+
+
+class TestL2populationRpcCallBackTunnelMixin(
+ l2population_rpc_base.TestL2populationRpcCallBackTunnelMixinBase):
+
+ def test_get_agent_ports_no_data(self):
+ for lvm, agent_ports in self.fakeagent.get_agent_ports(
+ self.fdb_entries1, {}):
+ self.assertIsNone(lvm)
+ self.assertEqual({}, agent_ports)
+
+ def test_get_agent_ports_non_existence_key_in_lvm(self):
+ results = {}
+ del self.local_vlan_map1[self.lvms[1].net]
+ for lvm, agent_ports in self.fakeagent.get_agent_ports(
+ self.fdb_entries1, self.local_vlan_map1):
+ results[lvm] = agent_ports
+ expected = {
+ self.lvm1: {
+ self.ports[0].ip: [[self.lvms[0].mac, self.lvms[0].ip]],
+ self.local_ip: []},
+ None: {},
+ self.lvm3: {
+ self.ports[2].ip: [[self.lvms[2].mac, self.lvms[2].ip]],
+ self.local_ip: []},
+ }
+ self.assertEqual(expected, results)
+
+ def test_get_agent_ports_no_agent_ports(self):
+ results = {}
+ self.fdb_entries1[self.lvms[1].net]['ports'] = {}
+ for lvm, agent_ports in self.fakeagent.get_agent_ports(
+ self.fdb_entries1, self.local_vlan_map1):
+ results[lvm] = agent_ports
+ expected = {
+ self.lvm1: {
+ self.ports[0].ip: [[self.lvms[0].mac, self.lvms[0].ip]],
+ self.local_ip: []},
+ self.lvm2: {},
+ self.lvm3: {
+ self.ports[2].ip: [[self.lvms[2].mac, self.lvms[2].ip]],
+ self.local_ip: []},
+ }
+ self.assertEqual(expected, results)
+
+ def test_fdb_add_tun(self):
+ with contextlib.nested(
+ mock.patch.object(self.fakeagent, 'setup_tunnel_port'),
+ mock.patch.object(self.fakeagent, 'add_fdb_flow'),
+ ) as (mock_setup_tunnel_port, mock_add_fdb_flow):
+ self.fakeagent.fdb_add_tun('context', self.lvm1,
+ self.agent_ports, self.ofports)
+ expected = [
+ mock.call([self.lvms[0].mac, self.lvms[0].ip], self.ports[0].ip,
+ self.lvm1, self.ports[0].ofport),
+ mock.call([self.lvms[1].mac, self.lvms[1].ip], self.ports[1].ip,
+ self.lvm1, self.ports[1].ofport),
+ mock.call([self.lvms[2].mac, self.lvms[2].ip], self.ports[2].ip,
+ self.lvm1, self.ports[2].ofport),
+ ]
+ self.assertEqual(sorted(expected),
+ sorted(mock_add_fdb_flow.call_args_list))
+
+ def test_fdb_add_tun_non_existence_key_in_ofports(self):
+ ofport = self.lvm1.network_type + '0a0a0a0a'
+ del self.ofports[self.type_gre][self.ports[1].ip]
+ with contextlib.nested(
+ mock.patch.object(self.fakeagent, 'setup_tunnel_port',
+ return_value=ofport),
+ mock.patch.object(self.fakeagent, 'add_fdb_flow'),
+ ) as (mock_setup_tunnel_port, mock_add_fdb_flow):
+ self.fakeagent.fdb_add_tun('context', self.lvm1,
+ self.agent_ports, self.ofports)
+ mock_setup_tunnel_port.assert_called_once_with(
+ self.ports[1].ip, self.lvm1.network_type)
+ expected = [
+ mock.call([self.lvms[0].mac, self.lvms[0].ip], self.ports[0].ip,
+ self.lvm1, self.ports[0].ofport),
+ mock.call([self.lvms[1].mac, self.lvms[1].ip], self.ports[1].ip,
+ self.lvm1, ofport),
+ mock.call([self.lvms[2].mac, self.lvms[2].ip], self.ports[2].ip,
+ self.lvm1, self.ports[2].ofport),
+ ]
+ self.assertEqual(sorted(expected),
+ sorted(mock_add_fdb_flow.call_args_list))
+
+ def test_fdb_add_tun_unavailable_ofport(self):
+ del self.ofports[self.type_gre][self.ports[1].ip]
+ with contextlib.nested(
+ mock.patch.object(self.fakeagent, 'setup_tunnel_port',
+ return_value=0),
+ mock.patch.object(self.fakeagent, 'add_fdb_flow'),
+ ) as (mock_setup_tunnel_port, mock_add_fdb_flow):
+ self.fakeagent.fdb_add_tun('context', self.lvm1,
+ self.agent_ports, self.ofports)
+ mock_setup_tunnel_port.assert_called_once_with(
+ self.ports[1].ip, self.lvm1.network_type)
+ expected = [
+ mock.call([self.lvms[0].mac, self.lvms[0].ip], self.ports[0].ip,
+ self.lvm1, self.ports[0].ofport),
+ mock.call([self.lvms[2].mac, self.lvms[2].ip], self.ports[2].ip,
+ self.lvm1, self.ports[2].ofport),
+ ]
+ self.assertEqual(sorted(expected),
+ sorted(mock_add_fdb_flow.call_args_list))
+
+ def test_fdb_remove_tun(self):
+ with mock.patch.object(
+ self.fakeagent, 'del_fdb_flow') as mock_del_fdb_flow:
+ self.fakeagent.fdb_remove_tun('context', self.lvm1,
+ self.agent_ports, self.ofports)
+ expected = [
+ mock.call([self.lvms[0].mac, self.lvms[0].ip], self.ports[0].ip,
+ self.lvm1, self.ports[0].ofport),
+ mock.call([self.lvms[1].mac, self.lvms[1].ip], self.ports[1].ip,
+ self.lvm1, self.ports[1].ofport),
+ mock.call([self.lvms[2].mac, self.lvms[2].ip], self.ports[2].ip,
+ self.lvm1, self.ports[2].ofport),
+ ]
+ self.assertEqual(sorted(expected),
+ sorted(mock_del_fdb_flow.call_args_list))
+
+ def test_fdb_remove_tun_flooding_entry(self):
+ self.agent_ports[self.ports[1].ip] = [n_const.FLOODING_ENTRY]
+ with contextlib.nested(
+ mock.patch.object(self.fakeagent, 'del_fdb_flow'),
+ mock.patch.object(self.fakeagent, 'cleanup_tunnel_port'),
+ ) as (mock_del_fdb_flow, mock_cleanup_tunnel_port):
+ self.fakeagent.fdb_remove_tun('context', self.lvm1,
+ self.agent_ports, self.ofports)
+ expected = [
+ mock.call([self.lvms[0].mac, self.lvms[0].ip], self.ports[0].ip,
+ self.lvm1, self.ports[0].ofport),
+ mock.call([n_const.FLOODING_ENTRY[0], n_const.FLOODING_ENTRY[1]],
+ self.ports[1].ip, self.lvm1, self.ports[1].ofport),
+ mock.call([self.lvms[2].mac, self.lvms[2].ip], self.ports[2].ip,
+ self.lvm1, self.ports[2].ofport),
+ ]
+ self.assertEqual(sorted(expected),
+ sorted(mock_del_fdb_flow.call_args_list))
+ mock_cleanup_tunnel_port.assert_called_once_with(
+ self.ports[1].ofport, self.lvm1.network_type)
+
+ def test_fdb_remove_tun_non_existence_key_in_ofports(self):
+ del self.ofports[self.type_gre][self.ports[1].ip]
+ with mock.patch.object(
+ self.fakeagent, 'del_fdb_flow') as mock_del_fdb_flow:
+ self.fakeagent.fdb_remove_tun('context', self.lvm1,
+ self.agent_ports, self.ofports)
+ expected = [
+ mock.call([self.lvms[0].mac, self.lvms[0].ip], self.ports[0].ip,
+ self.lvm1, self.ports[0].ofport),
+ mock.call([self.lvms[2].mac, self.lvms[2].ip], self.ports[2].ip,
+ self.lvm1, self.ports[2].ofport),
+ ]
+ self.assertEqual(sorted(expected),
+ sorted(mock_del_fdb_flow.call_args_list))
+
+ def test_fdb_update(self):
+ fake__fdb_chg_ip = mock.Mock()
+ self.fakeagent._fdb_chg_ip = fake__fdb_chg_ip
+ self.fakeagent.fdb_update('context', self.upd_fdb_entry1)
+ fake__fdb_chg_ip.assert_called_once_with(
+ 'context', self.upd_fdb_entry1_val)
+
+ def test_fdb_update_non_existence_method(self):
+ self.assertRaises(NotImplementedError,
+ self.fakeagent.fdb_update,
+ 'context', self.upd_fdb_entry1)
# @author: Fumihiko Kakuma, VA Linux Systems Japan K.K.
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
+import collections
import contextlib
import mock
from neutron.agent.linux import ip_lib
from neutron.agent.linux import utils
+from neutron.common import constants as n_const
from neutron.openstack.common import importutils
from neutron.plugins.common import constants as p_const
from neutron.plugins.openvswitch.common import constants
'FixedIntervalLoopingCall',
new=MockFixedIntervalLoopingCall)):
self.agent = self.mod_agent.OFANeutronAgent(self.ryuapp, **kwargs)
- self.agent.tun_br = _mk_test_br('tun_br')
- self.datapath = mock.Mock()
- self.ofparser = mock.Mock()
- self.agent.phys_brs['phys-net1'] = _mk_test_br('phys_br1')
- self.agent.phys_ofports['phys-net1'] = 777
- self.agent.int_ofports['phys-net1'] = 666
- self.datapath.ofparser = self.ofparser
- self.ofparser.OFPMatch = mock.Mock()
- self.ofparser.OFPMatch.return_value = mock.Mock()
- self.ofparser.OFPFlowMod = mock.Mock()
- self.ofparser.OFPFlowMod.return_value = mock.Mock()
- self.agent.int_br.ofparser = self.ofparser
- self.agent.int_br.datapath = _mk_test_dp('int_br')
self.agent.sg_agent = mock.Mock()
+ self.int_dp = _mk_test_dp('int_br')
+ self.agent.int_br.ofparser = self.int_dp.ofproto_parser
+ self.agent.int_br.datapath = self.int_dp
+ self.agent.tun_br = _mk_test_br('tun_br')
+ self.agent.phys_brs['phys-net1'] = _mk_test_br('phys_br1')
+ self.agent.phys_ofports['phys-net1'] = 777
+ self.agent.int_ofports['phys-net1'] = 666
+ self.datapath = _mk_test_dp('phys_br')
+
+ def _create_tunnel_port_name(self, tunnel_ip, tunnel_type):
+ tunnel_ip_hex = '%08x' % netaddr.IPAddress(tunnel_ip, version=4)
+ return '%s-%s' % (tunnel_type, tunnel_ip_hex)
def _mock_port_bound(self, ofport=None, new_local_vlan=None,
old_local_vlan=None):
port = mock.Mock()
port.ofport = ofport
net_uuid = 'my-net-uuid'
+ ofp = self.agent.int_br.datapath.ofproto
+ ofpp = self.agent.int_br.datapath.ofproto_parser
+ expected_msg = ofpp.OFPFlowMod(
+ self.agent.int_br.datapath,
+ match=ofpp.OFPMatch(in_port=port.ofport),
+ table_id=ofp.OFPTT_ALL,
+ command=ofp.OFPFC_DELETE,
+ out_group=ofp.OFPG_ANY,
+ out_port=ofp.OFPP_ANY
+ )
if old_local_vlan is not None:
self.agent.local_vlan_map[net_uuid] = (
self.mod_agent.LocalVLANMapping(
set_ovs_db_func.assert_called_once_with(
"Port", mock.ANY, "tag", str(new_local_vlan))
if ofport != -1:
- ryu_send_msg_func.assert_called_once_with(
- self.ofparser.OFPFlowMod.return_value)
+ ryu_send_msg_func.assert_called_once_with(expected_msg)
else:
self.assertFalse(ryu_send_msg_func.called)
else:
def _test_port_dead(self, cur_tag=None):
port = mock.Mock()
port.ofport = 1
+ ofpp = self.agent.int_br.datapath.ofproto_parser
+ expected_msg = ofpp.OFPFlowMod(
+ self.agent.int_br.datapath,
+ priority=2,
+ match=ofpp.OFPMatch(in_port=port.ofport)
+ )
with contextlib.nested(
mock.patch.object(self.mod_agent.OVSBridge,
'set_db_attribute', return_value=True),
else:
set_ovs_db_func.assert_called_once_with(
"Port", mock.ANY, "tag", str(self.mod_agent.DEAD_VLAN_TAG))
- ryu_send_msg_func.assert_called_once_with(
- self.ofparser.OFPFlowMod.return_value)
+ ryu_send_msg_func.assert_called_once_with(expected_msg)
def test_port_dead(self):
self._test_port_dead()
)
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")
+ self.assertFalse(clean_tun_fn.called)
recl_fn.assert_called_with("123")
def test_port_update(self):
self.agent.port_unbound("vif3", "netuid12345")
self.assertEqual(reclvl_fn.call_count, 2)
+ def _prepare_l2_pop_ofports(self):
+ LVM = collections.namedtuple('LVM', 'net, vlan, segid, ip')
+ self.lvms = [LVM(net='net1', vlan=11, segid='21', ip='1.1.1.1'),
+ LVM(net='net2', vlan=12, segid='22', ip='2.2.2.2')]
+ self.tunnel_type = 'gre'
+ self.tun_name1 = self._create_tunnel_port_name(self.lvms[0].ip,
+ self.tunnel_type)
+ self.tun_name2 = self._create_tunnel_port_name(self.lvms[1].ip,
+ self.tunnel_type)
+ lvm1 = mock.Mock()
+ lvm1.network_type = self.tunnel_type
+ lvm1.vlan = self.lvms[0].vlan
+ lvm1.segmentation_id = self.lvms[0].segid
+ lvm1.tun_ofports = set(['1'])
+ lvm2 = mock.Mock()
+ lvm2.network_type = self.tunnel_type
+ lvm2.vlan = self.lvms[1].vlan
+ lvm2.segmentation_id = self.lvms[1].segid
+ lvm2.tun_ofports = set(['1', '2'])
+ self.agent.local_vlan_map = {self.lvms[0].net: lvm1,
+ self.lvms[1].net: lvm2}
+ self.agent.tun_br_ofports = {self.tunnel_type:
+ {self.lvms[0].ip: '1',
+ self.lvms[1].ip: '2'}}
+
+ def test_fdb_ignore_network(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {'net3': {}}
+ with contextlib.nested(
+ mock.patch.object(self.agent, 'ryu_send_msg'),
+ mock.patch.object(self.agent, '_setup_tunnel_port'),
+ mock.patch.object(self.agent, 'cleanup_tunnel_port')
+ ) as (ryu_send_msg_fn, add_tun_fn, clean_tun_fn):
+ self.agent.fdb_add(None, fdb_entry)
+ self.assertFalse(add_tun_fn.called)
+ self.agent.fdb_remove(None, fdb_entry)
+ self.assertFalse(clean_tun_fn.called)
+ self.assertFalse(ryu_send_msg_fn.called)
+
+ def test_fdb_ignore_self(self):
+ self._prepare_l2_pop_ofports()
+ self.agent.local_ip = 'agent_ip'
+ fdb_entry = {self.lvms[1].net:
+ {'network_type': self.tunnel_type,
+ '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 = {self.lvms[0].net:
+ {'network_type': self.tunnel_type,
+ 'segment_id': 'tun1',
+ 'ports':
+ {self.lvms[1].ip:
+ [['mac', 'ip'],
+ n_const.FLOODING_ENTRY]}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent, 'ryu_send_msg'),
+ mock.patch.object(self.agent.tun_br, '_setup_tunnel_port'),
+ ) as (ryu_send_msg_fn, add_tun_fn):
+ add_tun_fn.return_value = '2'
+ self.agent.fdb_add(None, fdb_entry)
+ self.assertEqual(ryu_send_msg_fn.call_count, 2)
+
+ def test_fdb_del_flows(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {self.lvms[1].net:
+ {'network_type': self.tunnel_type,
+ 'segment_id': 'tun2',
+ 'ports':
+ {self.lvms[1].ip:
+ [['mac', 'ip'],
+ n_const.FLOODING_ENTRY]}}}
+ with mock.patch.object(self.agent,
+ 'ryu_send_msg') as ryu_send_msg_fn:
+ self.agent.fdb_remove(None, fdb_entry)
+ self.assertEqual(ryu_send_msg_fn.call_count, 3)
+
+ def test_fdb_add_port(self):
+ self._prepare_l2_pop_ofports()
+ tunnel_ip = '10.10.10.10'
+ tun_name = self._create_tunnel_port_name(tunnel_ip,
+ self.tunnel_type)
+ fdb_entry = {self.lvms[0].net:
+ {'network_type': self.tunnel_type,
+ 'segment_id': 'tun1',
+ 'ports': {self.lvms[0].ip: [['mac', 'ip']]}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent, 'ryu_send_msg'),
+ mock.patch.object(self.agent, '_setup_tunnel_port')
+ ) as (ryu_send_msg_fn, add_tun_fn):
+ self.agent.fdb_add(None, fdb_entry)
+ self.assertFalse(add_tun_fn.called)
+ fdb_entry[self.lvms[0].net]['ports'][tunnel_ip] = [['mac', 'ip']]
+ self.agent.fdb_add(None, fdb_entry)
+ add_tun_fn.assert_called_with(
+ tun_name, tunnel_ip, self.tunnel_type)
+
+ def test_fdb_del_port(self):
+ self._prepare_l2_pop_ofports()
+ fdb_entry = {self.lvms[1].net:
+ {'network_type': self.tunnel_type,
+ 'segment_id': 'tun2',
+ 'ports': {self.lvms[1].ip: [n_const.FLOODING_ENTRY]}}}
+ with contextlib.nested(
+ mock.patch.object(self.agent, 'ryu_send_msg'),
+ mock.patch.object(self.agent.tun_br, 'delete_port')
+ ) as (ryu_send_msg_fn, del_port_fn):
+ self.agent.fdb_remove(None, fdb_entry)
+ del_port_fn.assert_called_once_with(self.tun_name2)
+
+ def test_recl_lv_port_to_preserve(self):
+ self._prepare_l2_pop_ofports()
+ 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(self.lvms[0].net)
+ self.assertFalse(clean_tun_fn.called)
+
+ def test_recl_lv_port_to_remove(self):
+ self._prepare_l2_pop_ofports()
+ self.agent.enable_tunneling = True
+ with contextlib.nested(
+ mock.patch.object(self.agent.tun_br, 'delete_port'),
+ mock.patch.object(self.agent, 'ryu_send_msg')
+ ) as (del_port_fn, ryu_send_msg_fn):
+ self.agent.reclaim_local_vlan(self.lvms[1].net)
+ del_port_fn.assert_called_once_with(self.tun_name2)
+
def test_daemon_loop_uses_polling_manager(self):
with mock.patch(
'neutron.agent.linux.polling.get_polling_manager'
constants.DEFAULT_OVSDBMON_RESPAWN)
mock_loop.assert_called_once_with(polling_manager=fake_pm.__enter__())
- def test_setup_tunnel_port_error_negative(self):
+ def test__setup_tunnel_port_error_negative(self):
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
return_value='-1'),
mock.patch.object(self.mod_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_error_fn):
- ofport = self.agent.setup_tunnel_port(
+ ofport = self.agent._setup_tunnel_port(
'gre-1', 'remote_ip', p_const.TYPE_GRE)
add_tunnel_port_fn.assert_called_once_with(
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
{'type': p_const.TYPE_GRE, 'ip': 'remote_ip'})
self.assertEqual(ofport, 0)
- def test_setup_tunnel_port_error_not_int(self):
+ def test__setup_tunnel_port_error_not_int(self):
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
return_value=None),
mock.patch.object(self.mod_agent.LOG, 'exception'),
mock.patch.object(self.mod_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_exc_fn, log_error_fn):
- ofport = self.agent.setup_tunnel_port(
+ ofport = self.agent._setup_tunnel_port(
'gre-1', 'remote_ip', p_const.TYPE_GRE)
add_tunnel_port_fn.assert_called_once_with(
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
{'type': p_const.TYPE_GRE, 'ip': 'remote_ip'})
self.assertEqual(ofport, 0)
- def _create_tunnel_port_name(self, tunnel_ip, tunnel_type):
- tunnel_ip_hex = '%08x' % netaddr.IPAddress(tunnel_ip, version=4)
- return '%s-%s' % (tunnel_type, tunnel_ip_hex)
-
- def test_tunnel_sync_with_valid_ip_address_and_gre_type(self):
- tunnel_ip = '100.101.102.103'
- self.agent.tunnel_types = ['gre']
- tun_name = self._create_tunnel_port_name(tunnel_ip,
- self.agent.tunnel_types[0])
- fake_tunnel_details = {'tunnels': [{'ip_address': tunnel_ip}]}
- with contextlib.nested(
- mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync',
- return_value=fake_tunnel_details),
- mock.patch.object(self.agent, 'setup_tunnel_port')
- ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn):
- self.agent.tunnel_sync()
- expected_calls = [mock.call(tun_name, tunnel_ip,
- self.agent.tunnel_types[0])]
- setup_tunnel_port_fn.assert_has_calls(expected_calls)
-
- def test_tunnel_sync_with_valid_ip_address_and_vxlan_type(self):
- tunnel_ip = '100.101.31.15'
+ def test_tunnel_sync(self):
+ self.agent.local_ip = 'agent_ip'
+ self.agent.context = 'fake_context'
self.agent.tunnel_types = ['vxlan']
- tun_name = self._create_tunnel_port_name(tunnel_ip,
- self.agent.tunnel_types[0])
- fake_tunnel_details = {'tunnels': [{'ip_address': tunnel_ip}]}
- with contextlib.nested(
- mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync',
- return_value=fake_tunnel_details),
- mock.patch.object(self.agent, 'setup_tunnel_port')
- ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn):
+ with mock.patch.object(
+ self.agent.plugin_rpc, 'tunnel_sync'
+ ) as tunnel_sync_rpc_fn:
self.agent.tunnel_sync()
- expected_calls = [mock.call(tun_name, tunnel_ip,
- self.agent.tunnel_types[0])]
- setup_tunnel_port_fn.assert_has_calls(expected_calls)
-
- def test_tunnel_sync_invalid_ip_address(self):
- tunnel_ip = '100.100.100.100'
- self.agent.tunnel_types = ['vxlan']
- tun_name = self._create_tunnel_port_name(tunnel_ip,
- self.agent.tunnel_types[0])
- fake_tunnel_details = {'tunnels': [{'ip_address': '300.300.300.300'},
- {'ip_address': tunnel_ip}]}
- with contextlib.nested(
- mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync',
- return_value=fake_tunnel_details),
- mock.patch.object(self.agent, 'setup_tunnel_port')
- ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn):
- self.agent.tunnel_sync()
- setup_tunnel_port_fn.assert_called_once_with(
- tun_name, tunnel_ip, self.agent.tunnel_types[0])
-
- def test_tunnel_update(self):
- tunnel_ip = '10.10.10.10'
- self.agent.tunnel_types = ['gre']
- tun_name = self._create_tunnel_port_name(tunnel_ip,
- self.agent.tunnel_types[0])
- kwargs = {'tunnel_ip': tunnel_ip,
- 'tunnel_type': self.agent.tunnel_types[0]}
- self.agent.setup_tunnel_port = mock.Mock()
- self.agent.enable_tunneling = True
- self.agent.l2_pop = False
- self.agent.tunnel_update(context=None, **kwargs)
- expected_calls = [mock.call(tun_name, tunnel_ip,
- self.agent.tunnel_types[0])]
- self.agent.setup_tunnel_port.assert_has_calls(expected_calls)
+ tunnel_sync_rpc_fn.assert_called_once_with(
+ self.agent.context,
+ self.agent.local_ip,
+ self.agent.tunnel_types[0])
def test__provision_local_vlan_inbound_for_tunnel(self):
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
return_value='6'),
mock.patch.object(self.agent.tun_br, "add_flow")
) as (add_tun_port_fn, add_flow_fn):
- self.agent.setup_tunnel_port('portname', '1.2.3.4', 'vxlan')
+ self.agent._setup_tunnel_port('portname', '1.2.3.4', 'vxlan')
self.assertTrue(add_tun_port_fn.called)
def test_port_unbound(self):
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, '_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)
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'),
+ 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)
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')
+ 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)
constants.DEFAULT_OVSDBMON_RESPAWN)
mock_loop.assert_called_once_with(polling_manager=mock.ANY)
- def test_setup_tunnel_port_error_negative(self):
+ def test__setup_tunnel_port_error_negative(self):
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
return_value='-1'),
mock.patch.object(ovs_neutron_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_error_fn):
- ofport = self.agent.setup_tunnel_port(
+ ofport = self.agent._setup_tunnel_port(
'gre-1', 'remote_ip', p_const.TYPE_GRE)
add_tunnel_port_fn.assert_called_once_with(
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
{'type': p_const.TYPE_GRE, 'ip': 'remote_ip'})
self.assertEqual(ofport, 0)
- def test_setup_tunnel_port_error_not_int(self):
+ def test__setup_tunnel_port_error_not_int(self):
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
return_value=None),
mock.patch.object(ovs_neutron_agent.LOG, 'exception'),
mock.patch.object(ovs_neutron_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_exc_fn, log_error_fn):
- ofport = self.agent.setup_tunnel_port(
+ ofport = self.agent._setup_tunnel_port(
'gre-1', 'remote_ip', p_const.TYPE_GRE)
add_tunnel_port_fn.assert_called_once_with(
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
{'type': p_const.TYPE_GRE, 'ip': 'remote_ip'})
self.assertEqual(ofport, 0)
- def test_setup_tunnel_port_error_negative_df_disabled(self):
+ def test__setup_tunnel_port_error_negative_df_disabled(self):
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
return_value='-1'),
mock.patch.object(ovs_neutron_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_error_fn):
self.agent.dont_fragment = False
- ofport = self.agent.setup_tunnel_port(
+ ofport = self.agent._setup_tunnel_port(
'gre-1', 'remote_ip', p_const.TYPE_GRE)
add_tunnel_port_fn.assert_called_once_with(
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync',
return_value=fake_tunnel_details),
- mock.patch.object(self.agent, 'setup_tunnel_port')
- ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn):
+ mock.patch.object(self.agent, '_setup_tunnel_port')
+ ) as (tunnel_sync_rpc_fn, _setup_tunnel_port_fn):
self.agent.tunnel_types = ['gre']
self.agent.tunnel_sync()
expected_calls = [mock.call('gre-42', '100.101.102.103', 'gre')]
- setup_tunnel_port_fn.assert_has_calls(expected_calls)
+ _setup_tunnel_port_fn.assert_has_calls(expected_calls)
def test_tunnel_sync_with_ml2_plugin(self):
fake_tunnel_details = {'tunnels': [{'ip_address': '100.101.31.15'}]}
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync',
return_value=fake_tunnel_details),
- mock.patch.object(self.agent, 'setup_tunnel_port')
- ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn):
+ mock.patch.object(self.agent, '_setup_tunnel_port')
+ ) as (tunnel_sync_rpc_fn, _setup_tunnel_port_fn):
self.agent.tunnel_types = ['vxlan']
self.agent.tunnel_sync()
expected_calls = [mock.call('vxlan-64651f0f',
'100.101.31.15', 'vxlan')]
- setup_tunnel_port_fn.assert_has_calls(expected_calls)
+ _setup_tunnel_port_fn.assert_has_calls(expected_calls)
def test_tunnel_sync_invalid_ip_address(self):
fake_tunnel_details = {'tunnels': [{'ip_address': '300.300.300.300'},
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc, 'tunnel_sync',
return_value=fake_tunnel_details),
- mock.patch.object(self.agent, 'setup_tunnel_port')
- ) as (tunnel_sync_rpc_fn, setup_tunnel_port_fn):
+ mock.patch.object(self.agent, '_setup_tunnel_port')
+ ) as (tunnel_sync_rpc_fn, _setup_tunnel_port_fn):
self.agent.tunnel_types = ['vxlan']
self.agent.tunnel_sync()
- setup_tunnel_port_fn.assert_called_once_with('vxlan-64646464',
- '100.100.100.100',
- 'vxlan')
+ _setup_tunnel_port_fn.assert_called_once_with('vxlan-64646464',
+ '100.100.100.100',
+ 'vxlan')
def test_tunnel_update(self):
kwargs = {'tunnel_ip': '10.10.10.10',
'tunnel_type': 'gre'}
- self.agent.setup_tunnel_port = mock.Mock()
+ self.agent._setup_tunnel_port = mock.Mock()
self.agent.enable_tunneling = True
self.agent.tunnel_types = ['gre']
self.agent.l2_pop = False
self.agent.tunnel_update(context=None, **kwargs)
expected_calls = [mock.call('gre-0a0a0a0a', '10.10.10.10', 'gre')]
- self.agent.setup_tunnel_port.assert_has_calls(expected_calls)
+ self.agent._setup_tunnel_port.assert_has_calls(expected_calls)
def test_ovs_restart(self):
reply2 = {'current': set(['tap0']),