OFAgent: Share codes of l2-population in OVS agent
This is step 1 implementation of OFAgent l2-population.
OFAgent partially uses codes in OVS agent on implementation of l2-population.
We share these codes adding mixin class for OVS to l2-population rpc.
We use a ryu library instead of executing a ovs-vsctl command
and on OFAgent l2-population is no longer optional.
Also a function of local arp responder will be implemented on step 2.
related commit: b6133c35dd
Partially implements: blueprint ofagent-l2pop
Change-Id: I99a2adfd380a9fefe34c53e0dabc21d8cf5936cc
This commit is contained in:
parent
3c037a5869
commit
322fa884ea
@ -22,11 +22,22 @@ import abc
|
||||
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):
|
||||
@ -54,3 +65,118 @@ class L2populationRpcCallBackMixin(object):
|
||||
@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)
|
||||
|
@ -20,4 +20,5 @@
|
||||
from neutron.common import constants
|
||||
|
||||
SUPPORTED_AGENT_TYPES = [constants.AGENT_TYPE_OVS,
|
||||
constants.AGENT_TYPE_LINUXBRIDGE]
|
||||
constants.AGENT_TYPE_LINUXBRIDGE,
|
||||
constants.AGENT_TYPE_OFA]
|
||||
|
@ -26,6 +26,7 @@ from ryu.base import app_manager
|
||||
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
|
||||
@ -161,13 +162,16 @@ class OFANeutronAgentRyuApp(app_manager.RyuApp):
|
||||
|
||||
|
||||
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
|
||||
@ -178,8 +182,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
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.
|
||||
@ -208,7 +211,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
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,
|
||||
@ -216,7 +218,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
'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}
|
||||
|
||||
@ -291,8 +293,8 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
# 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)
|
||||
@ -341,46 +343,101 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
# 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 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 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 _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)
|
||||
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 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 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):
|
||||
br = self.tun_br
|
||||
@ -404,11 +461,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
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)
|
||||
|
||||
@ -588,6 +640,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
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)
|
||||
@ -1015,7 +1070,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
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,
|
||||
@ -1044,36 +1099,45 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
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)
|
||||
return ofport
|
||||
|
||||
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
|
||||
@ -1245,19 +1309,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
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})
|
||||
@ -1428,7 +1482,6 @@ def create_agent_config_map(config):
|
||||
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,
|
||||
)
|
||||
|
||||
|
@ -97,7 +97,7 @@ class OVSSecurityGroupAgent(sg_rpc.SecurityGroupAgentRpcMixin):
|
||||
|
||||
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.
|
||||
|
||||
@ -348,61 +348,35 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
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)
|
||||
@ -422,7 +396,7 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
"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:
|
||||
@ -436,8 +410,6 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
# 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])
|
||||
@ -486,15 +458,6 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
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.
|
||||
|
||||
@ -1111,7 +1074,7 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
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,
|
||||
@ -1150,6 +1113,16 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
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():
|
||||
@ -1393,9 +1366,8 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
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})
|
||||
|
140
neutron/tests/unit/agent/l2population_rpc_base.py
Normal file
140
neutron/tests/unit/agent/l2population_rpc_base.py
Normal file
@ -0,0 +1,140 @@
|
||||
# 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}
|
190
neutron/tests/unit/agent/test_l2population_rpc.py
Normal file
190
neutron/tests/unit/agent/test_l2population_rpc.py
Normal file
@ -0,0 +1,190 @@
|
||||
# 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)
|
@ -18,6 +18,7 @@
|
||||
# @author: Fumihiko Kakuma, VA Linux Systems Japan K.K.
|
||||
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
|
||||
import mock
|
||||
@ -27,6 +28,7 @@ import testtools
|
||||
|
||||
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
|
||||
@ -258,27 +260,36 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
'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(
|
||||
@ -296,8 +307,7 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
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:
|
||||
@ -316,6 +326,12 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
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),
|
||||
@ -331,8 +347,7 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
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()
|
||||
@ -570,14 +585,17 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
)
|
||||
|
||||
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):
|
||||
@ -655,6 +673,146 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
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'
|
||||
@ -671,13 +829,13 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
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,
|
||||
@ -687,14 +845,14 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
{'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,
|
||||
@ -707,72 +865,18 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
{'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:
|
||||
|
@ -934,7 +934,7 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
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):
|
||||
@ -978,7 +978,7 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
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)
|
||||
@ -1018,7 +1018,7 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
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)
|
||||
@ -1088,7 +1088,7 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
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)
|
||||
@ -1201,13 +1201,13 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
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,
|
||||
@ -1217,14 +1217,14 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
{'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,
|
||||
@ -1237,14 +1237,14 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
{'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,
|
||||
@ -1260,25 +1260,25 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
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'},
|
||||
@ -1286,24 +1286,24 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
||||
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']),
|
||||
|
Loading…
Reference in New Issue
Block a user