OFAgent: Implement arp responder
This is step 2 implementation of OFAgent l2-population. This handles an arp packet responding locally to an arp request in such a way that sends an arp request as a packet-in message to controller and builds and sends an arp reply packet. Currently this only supports tunnel. Implements: blueprint ofagent-l2pop Change-Id: Ida714f30c0f02c54dda3402c0dbf6047bc182b22
This commit is contained in:
parent
8307be7a9e
commit
9652d2e076
@ -136,6 +136,22 @@ class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin):
|
||||
'''
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def setup_entry_for_arp_reply(self, action, local_vid, mac_address,
|
||||
ip_address):
|
||||
'''Operate the ARP respond information.
|
||||
|
||||
Do operation of arp respond information for an action
|
||||
In ovs do adding or removing flow entry to edit an arp reply.
|
||||
|
||||
:param action: an action to operate for arp respond infomation.
|
||||
"add" or "remove"
|
||||
:param local_vid: id in local VLAN map of network's ARP entry.
|
||||
:param mac_address: MAC string value.
|
||||
:param ip_address: IP string value.
|
||||
'''
|
||||
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)
|
||||
@ -180,3 +196,43 @@ class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin):
|
||||
raise NotImplementedError()
|
||||
|
||||
getattr(self, method)(context, values)
|
||||
|
||||
@log.log
|
||||
def fdb_chg_ip_tun(self, context, fdb_entries, local_ip, local_vlan_map):
|
||||
'''fdb update when an IP of a port is updated.
|
||||
|
||||
The ML2 l2-pop mechanism driver sends an fdb update rpc message when an
|
||||
IP of a port is updated.
|
||||
|
||||
:param context: RPC context.
|
||||
:param fdb_entries: fdb dicts that contain all mac/IP informations per
|
||||
agent and network.
|
||||
{'net1':
|
||||
{'agent_ip':
|
||||
{'before': [[mac, ip]],
|
||||
'after': [[mac, ip]]
|
||||
}
|
||||
}
|
||||
'net2':
|
||||
...
|
||||
}
|
||||
:param local_ip: local IP address of this agent.
|
||||
:local_vlan_map: local VLAN map of network.
|
||||
'''
|
||||
|
||||
for network_id, agent_ports in fdb_entries.items():
|
||||
lvm = local_vlan_map.get(network_id)
|
||||
if not lvm:
|
||||
continue
|
||||
|
||||
for agent_ip, state in agent_ports.items():
|
||||
if agent_ip == local_ip:
|
||||
continue
|
||||
|
||||
after = state.get('after')
|
||||
for mac, ip in after:
|
||||
self.setup_entry_for_arp_reply('add', lvm.vlan, mac, ip)
|
||||
|
||||
before = state.get('before')
|
||||
for mac, ip in before:
|
||||
self.setup_entry_for_arp_reply('remove', lvm.vlan, mac, ip)
|
||||
|
190
neutron/plugins/ofagent/agent/arp_lib.py
Normal file
190
neutron/plugins/ofagent/agent/arp_lib.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.
|
||||
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
|
||||
|
||||
from ryu.app.ofctl import api as ryu_api
|
||||
from ryu.lib import dpid as dpid_lib
|
||||
from ryu.lib.packet import arp
|
||||
from ryu.lib.packet import ethernet
|
||||
from ryu.lib.packet import packet
|
||||
from ryu.lib.packet import vlan
|
||||
from ryu.ofproto import ether
|
||||
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ArpLib(object):
|
||||
|
||||
def __init__(self, ryuapp):
|
||||
"""Constructor.
|
||||
|
||||
Define the internal table mapped an ip and a mac in a network.
|
||||
self._arp_tbl:
|
||||
{network1: {ip_addr: mac, ...},
|
||||
network2: {ip_addr: mac, ...},
|
||||
...,
|
||||
}
|
||||
|
||||
:param ryuapp: object of the ryu app.
|
||||
"""
|
||||
self.ryuapp = ryuapp
|
||||
self._arp_tbl = {}
|
||||
|
||||
def _send_arp_reply(self, datapath, port, pkt):
|
||||
LOG.debug("packet-out %s", pkt)
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
pkt.serialize()
|
||||
data = pkt.data
|
||||
actions = [ofpp.OFPActionOutput(port=port)]
|
||||
out = ofpp.OFPPacketOut(datapath=datapath,
|
||||
buffer_id=ofp.OFP_NO_BUFFER,
|
||||
in_port=ofp.OFPP_CONTROLLER,
|
||||
actions=actions,
|
||||
data=data)
|
||||
ryu_api.send_msg(self.ryuapp, out)
|
||||
|
||||
def _add_flow_to_avoid_unknown_packet(self, datapath, match):
|
||||
LOG.debug("add flow to avoid an unknown packet from packet-in")
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
instructions = [ofpp.OFPInstructionGotoTable(
|
||||
table_id=constants.FLOOD_TO_TUN)]
|
||||
out = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
command=ofp.OFPFC_ADD,
|
||||
idle_timeout=5,
|
||||
priority=20,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
ryu_api.send_msg(self.ryuapp, out)
|
||||
|
||||
def _send_unknown_packet(self, msg, in_port, out_port):
|
||||
LOG.debug("unknown packet-out in-port %(in_port)s "
|
||||
"out-port %(out_port)s msg %(msg)s",
|
||||
{'in_port': in_port, 'out_port': out_port, 'msg': msg})
|
||||
datapath = msg.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
data = None
|
||||
if msg.buffer_id == ofp.OFP_NO_BUFFER:
|
||||
data = msg.data
|
||||
actions = [ofpp.OFPActionOutput(port=out_port)]
|
||||
out = ofpp.OFPPacketOut(datapath=datapath,
|
||||
buffer_id=msg.buffer_id,
|
||||
in_port=in_port,
|
||||
actions=actions,
|
||||
data=data)
|
||||
ryu_api.send_msg(self.ryuapp, out)
|
||||
|
||||
def _respond_arp(self, datapath, port, arptbl,
|
||||
pkt_ethernet, pkt_vlan, pkt_arp):
|
||||
if pkt_arp.opcode != arp.ARP_REQUEST:
|
||||
LOG.debug("unknown arp op %s", pkt_arp.opcode)
|
||||
return False
|
||||
ip_addr = pkt_arp.dst_ip
|
||||
hw_addr = arptbl.get(ip_addr)
|
||||
if hw_addr is None:
|
||||
LOG.debug("unknown arp request %s", ip_addr)
|
||||
return False
|
||||
LOG.debug("responding arp request %(ip_addr)s -> %(hw_addr)s",
|
||||
{'ip_addr': ip_addr, 'hw_addr': hw_addr})
|
||||
pkt = packet.Packet()
|
||||
pkt.add_protocol(ethernet.ethernet(ethertype=pkt_ethernet.ethertype,
|
||||
dst=pkt_ethernet.src,
|
||||
src=hw_addr))
|
||||
pkt.add_protocol(vlan.vlan(cfi=pkt_vlan.cfi,
|
||||
ethertype=pkt_vlan.ethertype,
|
||||
pcp=pkt_vlan.pcp,
|
||||
vid=pkt_vlan.vid))
|
||||
pkt.add_protocol(arp.arp(opcode=arp.ARP_REPLY,
|
||||
src_mac=hw_addr,
|
||||
src_ip=ip_addr,
|
||||
dst_mac=pkt_arp.src_mac,
|
||||
dst_ip=pkt_arp.src_ip))
|
||||
self._send_arp_reply(datapath, port, pkt)
|
||||
return True
|
||||
|
||||
def add_arp_table_entry(self, network, ip, mac):
|
||||
LOG.debug("added arp table entry: "
|
||||
"network %(network)s ip %(ip)s mac %(mac)s",
|
||||
{'network': network, 'ip': ip, 'mac': mac})
|
||||
if network in self._arp_tbl:
|
||||
self._arp_tbl[network][ip] = mac
|
||||
else:
|
||||
self._arp_tbl[network] = {ip: mac}
|
||||
|
||||
def del_arp_table_entry(self, network, ip):
|
||||
LOG.debug("deleted arp table entry: network %(network)s ip %(ip)s",
|
||||
{'network': network, 'ip': ip})
|
||||
del self._arp_tbl[network][ip]
|
||||
if not self._arp_tbl[network]:
|
||||
del self._arp_tbl[network]
|
||||
|
||||
def packet_in_handler(self, ev):
|
||||
"""Check a packet-in message.
|
||||
|
||||
Build and output an arp reply if a packet-in message is
|
||||
an arp packet.
|
||||
"""
|
||||
msg = ev.msg
|
||||
LOG.debug("packet-in msg %s", msg)
|
||||
datapath = msg.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
port = msg.match['in_port']
|
||||
pkt = packet.Packet(msg.data)
|
||||
LOG.info(_("packet-in dpid %(dpid)s in_port %(port)s pkt %(pkt)s"),
|
||||
{'dpid': dpid_lib.dpid_to_str(datapath.id),
|
||||
'port': port, 'pkt': pkt})
|
||||
pkt_vlan = None
|
||||
pkt_arp = None
|
||||
pkt_ethernet = pkt.get_protocol(ethernet.ethernet)
|
||||
if not pkt_ethernet:
|
||||
LOG.info(_("non-ethernet packet"))
|
||||
else:
|
||||
pkt_vlan = pkt.get_protocol(vlan.vlan)
|
||||
if not pkt_vlan:
|
||||
LOG.info(_("non-vlan packet"))
|
||||
if pkt_vlan:
|
||||
network = pkt_vlan.vid
|
||||
pkt_arp = pkt.get_protocol(arp.arp)
|
||||
if not pkt_arp:
|
||||
LOG.info(_("drop non-arp packet"))
|
||||
return
|
||||
else:
|
||||
# drop an unknown packet.
|
||||
LOG.info(_("drop unknown packet"))
|
||||
return
|
||||
|
||||
arptbl = self._arp_tbl.get(network)
|
||||
if arptbl:
|
||||
if self._respond_arp(datapath, port, arptbl,
|
||||
pkt_ethernet, pkt_vlan, pkt_arp):
|
||||
return
|
||||
else:
|
||||
LOG.info(_("unknown network %s"), network)
|
||||
# add a flow to skip a packet-in to a controller.
|
||||
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
|
||||
vlan_vid=network | ofp.OFPVID_PRESENT,
|
||||
arp_op=arp.ARP_REQUEST,
|
||||
arp_tpa=pkt_arp.dst_ip)
|
||||
self._add_flow_to_avoid_unknown_packet(datapath, match)
|
||||
# send an unknown arp packet to the table.
|
||||
self._send_unknown_packet(msg, port, ofp.OFPP_TABLE)
|
@ -23,7 +23,11 @@ import netaddr
|
||||
from oslo.config import cfg
|
||||
from ryu.app.ofctl import api as ryu_api
|
||||
from ryu.base import app_manager
|
||||
from ryu.controller import handler
|
||||
from ryu.controller import ofp_event
|
||||
from ryu.lib import hub
|
||||
from ryu.lib.packet import arp
|
||||
from ryu.ofproto import ether
|
||||
from ryu.ofproto import ofproto_v1_3 as ryu_ofp13
|
||||
|
||||
from neutron.agent import l2population_rpc
|
||||
@ -41,6 +45,7 @@ from neutron import context
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common import loopingcall
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.ofagent.agent import arp_lib
|
||||
from neutron.plugins.ofagent.agent import ports
|
||||
from neutron.plugins.ofagent.common import config # noqa
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
@ -132,8 +137,11 @@ class OFASecurityGroupAgent(sg_rpc.SecurityGroupAgentRpcMixin):
|
||||
class OFANeutronAgentRyuApp(app_manager.RyuApp):
|
||||
OFP_VERSIONS = [ryu_ofp13.OFP_VERSION]
|
||||
|
||||
def start(self):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OFANeutronAgentRyuApp, self).__init__(*args, **kwargs)
|
||||
self.arplib = arp_lib.ArpLib(self)
|
||||
|
||||
def start(self):
|
||||
super(OFANeutronAgentRyuApp, self).start()
|
||||
return hub.spawn(self._agent_main, self)
|
||||
|
||||
@ -160,6 +168,16 @@ class OFANeutronAgentRyuApp(app_manager.RyuApp):
|
||||
LOG.info(_("Agent initialized successfully, now running... "))
|
||||
agent.daemon_loop()
|
||||
|
||||
@handler.set_ev_cls(ofp_event.EventOFPPacketIn, handler.MAIN_DISPATCHER)
|
||||
def _packet_in_handler(self, ev):
|
||||
self.arplib.packet_in_handler(ev)
|
||||
|
||||
def add_arp_table_entry(self, network, ip, mac):
|
||||
self.arplib.add_arp_table_entry(network, ip, mac)
|
||||
|
||||
def del_arp_table_entry(self, network, ip):
|
||||
self.arplib.del_arp_table_entry(network, ip)
|
||||
|
||||
|
||||
class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
sg_rpc.SecurityGroupAgentRpcCallbackMixin,
|
||||
@ -391,6 +409,8 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
lvm.tun_ofports.add(ofport)
|
||||
self._add_fdb_flooding_flow(lvm)
|
||||
else:
|
||||
self.ryuapp.add_arp_table_entry(
|
||||
lvm.vlan, port_info[1], port_info[0])
|
||||
match = ofpp.OFPMatch(
|
||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
|
||||
eth_dst=port_info[0])
|
||||
@ -427,6 +447,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
else:
|
||||
self.ryuapp.del_arp_table_entry(lvm.vlan, port_info[1])
|
||||
match = ofpp.OFPMatch(
|
||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
|
||||
eth_dst=port_info[0])
|
||||
@ -438,6 +459,18 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def setup_entry_for_arp_reply(self, action, local_vid, mac_address,
|
||||
ip_address):
|
||||
if action == 'add':
|
||||
self.ryuapp.add_arp_table_entry(local_vid, ip_address, mac_address)
|
||||
elif action == 'remove':
|
||||
self.ryuapp.del_arp_table_entry(local_vid, ip_address)
|
||||
|
||||
def _fdb_chg_ip(self, context, fdb_entries):
|
||||
LOG.debug("update chg_ip received")
|
||||
self.fdb_chg_ip_tun(
|
||||
context, fdb_entries, self.local_ip, self.local_vlan_map)
|
||||
|
||||
def _provision_local_vlan_inbound_for_tunnel(self, lvid, network_type,
|
||||
segmentation_id):
|
||||
br = self.tun_br
|
||||
@ -792,6 +825,22 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath, priority=0)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_output_arp_packet_to_controller(self, br):
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
|
||||
arp_op=arp.ARP_REQUEST)
|
||||
actions = [ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
|
||||
msg = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
priority=10,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_goto_table_ucast_unicast(self, br):
|
||||
match = br.ofparser.OFPMatch(eth_dst=('00:00:00:00:00:00',
|
||||
'01:00:00:00:00:00'))
|
||||
@ -799,6 +848,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
table_id=constants.UCAST_TO_TUN)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
@ -810,6 +860,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
table_id=constants.FLOOD_TO_TUN)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
@ -879,6 +930,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
self._tun_br_sort_incoming_traffic_depend_in_port(self.tun_br)
|
||||
self._tun_br_output_arp_packet_to_controller(self.tun_br)
|
||||
self._tun_br_goto_table_ucast_unicast(self.tun_br)
|
||||
self._tun_br_goto_table_flood_broad_multi_cast(self.tun_br)
|
||||
self._tun_br_set_table_tun_by_tunnel_type(self.tun_br)
|
||||
|
@ -369,7 +369,7 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
actions="strip_vlan,set_tunnel:%s,"
|
||||
"output:%s" % (lvm.segmentation_id, ofports))
|
||||
else:
|
||||
self._set_arp_responder('add', lvm.vlan, port_info[0],
|
||||
self.setup_entry_for_arp_reply('add', lvm.vlan, port_info[0],
|
||||
port_info[1])
|
||||
if not self.dvr_agent.is_dvr_router_interface(port_info[1]):
|
||||
self.tun_br.add_flow(table=constants.UCAST_TO_TUN,
|
||||
@ -395,83 +395,43 @@ class OVSNeutronAgent(n_rpc.RpcCallback,
|
||||
self.tun_br.delete_flows(table=constants.FLOOD_TO_TUN,
|
||||
dl_vlan=lvm.vlan)
|
||||
else:
|
||||
self._set_arp_responder('remove', lvm.vlan, port_info[0],
|
||||
self.setup_entry_for_arp_reply('remove', lvm.vlan, port_info[0],
|
||||
port_info[1])
|
||||
self.tun_br.delete_flows(table=constants.UCAST_TO_TUN,
|
||||
dl_vlan=lvm.vlan,
|
||||
dl_dst=port_info[0])
|
||||
|
||||
def _fdb_chg_ip(self, context, fdb_entries):
|
||||
'''fdb update when an IP of a port is updated.
|
||||
LOG.debug("update chg_ip received")
|
||||
self.fdb_chg_ip_tun(
|
||||
context, fdb_entries, self.local_ip, self.local_vlan_map)
|
||||
|
||||
The ML2 l2-pop mechanism driver send an fdb update rpc message when an
|
||||
IP of a port is updated.
|
||||
|
||||
:param context: RPC context.
|
||||
:param fdb_entries: fdb dicts that contain all mac/IP informations per
|
||||
agent and network.
|
||||
{'net1':
|
||||
{'agent_ip':
|
||||
{'before': [[mac, ip]],
|
||||
'after': [[mac, ip]]
|
||||
}
|
||||
}
|
||||
'net2':
|
||||
...
|
||||
}
|
||||
'''
|
||||
LOG.debug(_("update chg_ip received"))
|
||||
|
||||
# TODO(ethuleau): Use OVS defer apply flows for all rules will be an
|
||||
# interesting improvement here. But actually, OVS lib defer apply flows
|
||||
# methods doesn't ensure the add flows will be applied before delete.
|
||||
for network_id, agent_ports in fdb_entries.items():
|
||||
lvm = self.local_vlan_map.get(network_id)
|
||||
if not lvm:
|
||||
continue
|
||||
|
||||
for agent_ip, state in agent_ports.items():
|
||||
if agent_ip == self.local_ip:
|
||||
continue
|
||||
|
||||
after = state.get('after')
|
||||
for mac, ip in after:
|
||||
self._set_arp_responder('add', lvm.vlan, mac, ip)
|
||||
|
||||
before = state.get('before')
|
||||
for mac, ip in before:
|
||||
self._set_arp_responder('remove', lvm.vlan, mac, ip)
|
||||
|
||||
def _set_arp_responder(self, action, lvid, mac_str, ip_str):
|
||||
def setup_entry_for_arp_reply(self, action, local_vid, mac_address,
|
||||
ip_address):
|
||||
'''Set the ARP respond entry.
|
||||
|
||||
When the l2 population mechanism driver and OVS supports to edit ARP
|
||||
fields, a table (ARP_RESPONDER) to resolve ARP locally is added to the
|
||||
tunnel bridge.
|
||||
|
||||
:param action: add or remove ARP entry.
|
||||
:param lvid: local VLAN map of network's ARP entry.
|
||||
:param mac_str: MAC string value.
|
||||
:param ip_str: IP string value.
|
||||
'''
|
||||
if not self.arp_responder_enabled:
|
||||
return
|
||||
|
||||
mac = netaddr.EUI(mac_str, dialect=netaddr.mac_unix)
|
||||
ip = netaddr.IPAddress(ip_str)
|
||||
mac = netaddr.EUI(mac_address, dialect=netaddr.mac_unix)
|
||||
ip = netaddr.IPAddress(ip_address)
|
||||
|
||||
if action == 'add':
|
||||
actions = constants.ARP_RESPONDER_ACTIONS % {'mac': mac, 'ip': ip}
|
||||
self.tun_br.add_flow(table=constants.ARP_RESPONDER,
|
||||
priority=1,
|
||||
proto='arp',
|
||||
dl_vlan=lvid,
|
||||
dl_vlan=local_vid,
|
||||
nw_dst='%s' % ip,
|
||||
actions=actions)
|
||||
elif action == 'remove':
|
||||
self.tun_br.delete_flows(table=constants.ARP_RESPONDER,
|
||||
proto='arp',
|
||||
dl_vlan=lvid,
|
||||
dl_vlan=local_vid,
|
||||
nw_dst='%s' % ip)
|
||||
else:
|
||||
LOG.warning(_('Action %s not supported'), action)
|
||||
|
@ -40,6 +40,10 @@ class FakeNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin):
|
||||
def cleanup_tunnel_port(self, tun_ofport, tunnel_type):
|
||||
pass
|
||||
|
||||
def setup_entry_for_arp_reply(self, action, local_vid, mac_address,
|
||||
ip_address):
|
||||
pass
|
||||
|
||||
|
||||
class TestL2populationRpcCallBackTunnelMixinBase(base.BaseTestCase):
|
||||
|
||||
|
@ -188,3 +188,46 @@ class TestL2populationRpcCallBackTunnelMixin(
|
||||
self.assertRaises(NotImplementedError,
|
||||
self.fakeagent.fdb_update,
|
||||
'context', self.upd_fdb_entry1)
|
||||
|
||||
def test__fdb_chg_ip(self):
|
||||
m_setup_entry_for_arp_reply = mock.Mock()
|
||||
self.fakeagent.setup_entry_for_arp_reply = m_setup_entry_for_arp_reply
|
||||
self.fakeagent.fdb_chg_ip_tun('context', self.upd_fdb_entry1_val,
|
||||
self.local_ip, self.local_vlan_map1)
|
||||
expected = [
|
||||
mock.call('remove', self.lvm1.vlan, self.lvms[0].mac,
|
||||
self.lvms[0].ip),
|
||||
mock.call('add', self.lvm1.vlan, self.lvms[1].mac,
|
||||
self.lvms[1].ip),
|
||||
mock.call('remove', self.lvm1.vlan, self.lvms[0].mac,
|
||||
self.lvms[0].ip),
|
||||
mock.call('add', self.lvm1.vlan, self.lvms[1].mac,
|
||||
self.lvms[1].ip),
|
||||
mock.call('remove', self.lvm2.vlan, self.lvms[0].mac,
|
||||
self.lvms[0].ip),
|
||||
mock.call('add', self.lvm2.vlan, self.lvms[2].mac,
|
||||
self.lvms[2].ip),
|
||||
]
|
||||
m_setup_entry_for_arp_reply.assert_has_calls(expected, any_order=True)
|
||||
|
||||
def test__fdb_chg_ip_no_lvm(self):
|
||||
m_setup_entry_for_arp_reply = mock.Mock()
|
||||
self.fakeagent.setup_entry_for_arp_reply = m_setup_entry_for_arp_reply
|
||||
self.fakeagent.fdb_chg_ip_tun(
|
||||
'context', self.upd_fdb_entry1, self.local_ip, {})
|
||||
self.assertFalse(m_setup_entry_for_arp_reply.call_count)
|
||||
|
||||
def test__fdb_chg_ip_ip_is_local_ip(self):
|
||||
upd_fdb_entry_val = {
|
||||
self.lvms[0].net: {
|
||||
self.local_ip: {
|
||||
'before': [[self.lvms[0].mac, self.lvms[0].ip]],
|
||||
'after': [[self.lvms[1].mac, self.lvms[1].ip]],
|
||||
},
|
||||
},
|
||||
}
|
||||
m_setup_entry_for_arp_reply = mock.Mock()
|
||||
self.fakeagent.setup_entry_for_arp_reply = m_setup_entry_for_arp_reply
|
||||
self.fakeagent.fdb_chg_ip_tun('context', upd_fdb_entry_val,
|
||||
self.local_ip, self.local_vlan_map1)
|
||||
self.assertFalse(m_setup_entry_for_arp_reply.call_count)
|
||||
|
@ -98,8 +98,24 @@ class _Mod(object):
|
||||
def patch_fake_oflib_of():
|
||||
ryu_mod = mock.Mock()
|
||||
ryu_base_mod = ryu_mod.base
|
||||
ryu_ctrl_mod = ryu_mod.controller
|
||||
handler = _Mod('ryu.controller.handler')
|
||||
handler.set_ev_cls = mock.Mock()
|
||||
ofp_event = _Mod('ryu.controller.ofp_event')
|
||||
ryu_ctrl_mod.handler = handler
|
||||
ryu_ctrl_mod.ofp_event = ofp_event
|
||||
ryu_lib_mod = ryu_mod.lib
|
||||
ryu_lib_hub = ryu_lib_mod.hub
|
||||
ryu_packet_mod = ryu_lib_mod.packet
|
||||
packet = _Mod('ryu.lib.packet.packet')
|
||||
arp = _Mod('ryu.lib.packet.arp')
|
||||
ethernet = _Mod('ryu.lib.packet.ethernet')
|
||||
vlan = _Mod('ryu.lib.packet.vlan')
|
||||
ryu_packet_mod.packet = packet
|
||||
packet.Packet = mock.Mock()
|
||||
ryu_packet_mod.arp = arp
|
||||
ryu_packet_mod.ethernet = ethernet
|
||||
ryu_packet_mod.vlan = vlan
|
||||
ryu_ofproto_mod = ryu_mod.ofproto
|
||||
ofp = _Mod('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = _Mod('ryu.ofproto.ofproto_v1_3_parser')
|
||||
@ -110,8 +126,18 @@ def patch_fake_oflib_of():
|
||||
ryu_ofctl_api = ryu_app_ofctl_mod.api
|
||||
modules = {'ryu': ryu_mod,
|
||||
'ryu.base': ryu_base_mod,
|
||||
'ryu.controller': ryu_ctrl_mod,
|
||||
'ryu.controller.handler': handler,
|
||||
'ryu.controller.handler.set_ev_cls': handler.set_ev_cls,
|
||||
'ryu.controller.ofp_event': ofp_event,
|
||||
'ryu.lib': ryu_lib_mod,
|
||||
'ryu.lib.hub': ryu_lib_hub,
|
||||
'ryu.lib.packet': ryu_packet_mod,
|
||||
'ryu.lib.packet.packet': packet,
|
||||
'ryu.lib.packet.packet.Packet': packet.Packet,
|
||||
'ryu.lib.packet.arp': arp,
|
||||
'ryu.lib.packet.ethernet': ethernet,
|
||||
'ryu.lib.packet.vlan': vlan,
|
||||
'ryu.ofproto': ryu_ofproto_mod,
|
||||
'ryu.ofproto.ofproto_v1_3': ofp,
|
||||
'ryu.ofproto.ofproto_v1_3_parser': ofpp,
|
||||
|
65
neutron/tests/unit/ofagent/ofa_test_base.py
Normal file
65
neutron/tests/unit/ofagent/ofa_test_base.py
Normal file
@ -0,0 +1,65 @@
|
||||
# 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.
|
||||
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit.ofagent import fake_oflib
|
||||
|
||||
|
||||
class OFAAgentTestBase(base.BaseTestCase):
|
||||
|
||||
_AGENT_NAME = 'neutron.plugins.ofagent.agent.ofa_neutron_agent'
|
||||
|
||||
def setUp(self):
|
||||
self.fake_oflib_of = fake_oflib.patch_fake_oflib_of()
|
||||
self.fake_oflib_of.start()
|
||||
self.addCleanup(self.fake_oflib_of.stop)
|
||||
self.mod_agent = importutils.import_module(self._AGENT_NAME)
|
||||
super(OFAAgentTestBase, self).setUp()
|
||||
self.ryuapp = mock.Mock()
|
||||
|
||||
def setup_config(self):
|
||||
cfg.CONF.set_default('firewall_driver',
|
||||
'neutron.agent.firewall.NoopFirewallDriver',
|
||||
group='SECURITYGROUP')
|
||||
cfg.CONF.register_cli_opts([
|
||||
cfg.StrOpt('ofp-listen-host', default='',
|
||||
help='openflow listen host'),
|
||||
cfg.IntOpt('ofp-tcp-listen-port', default=6633,
|
||||
help='openflow tcp listen port')
|
||||
])
|
||||
cfg.CONF.set_override('root_helper', 'fake_helper', group='AGENT')
|
||||
|
||||
def _mk_test_dp(self, name):
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
dp = mock.Mock()
|
||||
dp.ofproto = ofp
|
||||
dp.ofproto_parser = ofpp
|
||||
dp.__repr__ = mock.Mock(return_value=name)
|
||||
return dp
|
||||
|
||||
def _mk_test_br(self, name):
|
||||
dp = self._mk_test_dp(name)
|
||||
br = mock.Mock()
|
||||
br.datapath = dp
|
||||
br.ofproto = dp.ofproto
|
||||
br.ofparser = dp.ofproto_parser
|
||||
return br
|
309
neutron/tests/unit/ofagent/test_arp_lib.py
Normal file
309
neutron/tests/unit/ofagent/test_arp_lib.py
Normal file
@ -0,0 +1,309 @@
|
||||
# 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
|
||||
import contextlib
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.tests.unit.ofagent import ofa_test_base
|
||||
|
||||
|
||||
_OFALIB_NAME = 'neutron.plugins.ofagent.agent.arp_lib'
|
||||
|
||||
|
||||
class OFAAgentTestCase(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(OFAAgentTestCase, self).setUp()
|
||||
|
||||
Net = collections.namedtuple('Net', 'net, mac, ip')
|
||||
self.nets = [Net(net=10, mac='11:11:11:44:55:66', ip='10.1.2.20'),
|
||||
Net(net=10, mac='11:11:11:44:55:67', ip='10.1.2.21'),
|
||||
Net(net=20, mac='22:22:22:44:55:66', ip='10.2.2.20')]
|
||||
|
||||
self.packet_mod = mock.Mock()
|
||||
self.proto_ethernet_mod = mock.Mock()
|
||||
self.proto_vlan_mod = mock.Mock()
|
||||
self.proto_vlan_mod.vid = self.nets[0].net
|
||||
self.proto_arp_mod = mock.Mock()
|
||||
self.fake_get_protocol = mock.Mock(return_value=self.proto_vlan_mod)
|
||||
self.packet_mod.get_protocol = self.fake_get_protocol
|
||||
self.fake_add_protocol = mock.Mock()
|
||||
self.packet_mod.add_protocol = self.fake_add_protocol
|
||||
self.arp = importutils.import_module('ryu.lib.packet.arp')
|
||||
self.ethernet = importutils.import_module('ryu.lib.packet.ethernet')
|
||||
self.vlan = importutils.import_module('ryu.lib.packet.vlan')
|
||||
mock.patch('ryu.lib.packet.packet.Packet',
|
||||
return_value=self.packet_mod).start()
|
||||
|
||||
self.ryuapp = 'ryuapp'
|
||||
self.inport = '1'
|
||||
self.ev = mock.Mock()
|
||||
self.datapath = self._mk_test_dp('tun_br')
|
||||
self.ofproto = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
self.ofpp = mock.Mock()
|
||||
self.datapath.ofproto = self.ofproto
|
||||
self.datapath.ofproto_parser = self.ofpp
|
||||
self.OFPActionOutput = mock.Mock()
|
||||
self.OFPActionOutput.return_value = 'OFPActionOutput'
|
||||
self.ofpp.OFPActionOutput = self.OFPActionOutput
|
||||
self.msg = mock.Mock()
|
||||
self.msg.datapath = self.datapath
|
||||
self.msg.buffer_id = self.ofproto.OFP_NO_BUFFER
|
||||
self.msg_data = 'test_message_data'
|
||||
self.msg.data = self.msg_data
|
||||
self.ev.msg = self.msg
|
||||
self.msg.match = {'in_port': self.inport}
|
||||
|
||||
|
||||
class TestArpLib(OFAAgentTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestArpLib, self).setUp()
|
||||
|
||||
self.mod_arplib = importutils.import_module(_OFALIB_NAME)
|
||||
self.arplib = self.mod_arplib.ArpLib(self.ryuapp)
|
||||
self.packet_mod.get_protocol = self._fake_get_protocol
|
||||
self._fake_get_protocol_ethernet = True
|
||||
self._fake_get_protocol_vlan = True
|
||||
self._fake_get_protocol_arp = True
|
||||
|
||||
def test__send_unknown_packet_no_buffer(self):
|
||||
in_port = 3
|
||||
out_port = self.ofproto.OFPP_TABLE
|
||||
self.msg.buffer_id = self.ofproto.OFP_NO_BUFFER
|
||||
self.arplib._send_unknown_packet(self.msg, in_port, out_port)
|
||||
actions = [self.ofpp.OFPActionOutput(self.ofproto.OFPP_TABLE, 0)]
|
||||
self.ofpp.OFPPacketOut.assert_called_once_with(
|
||||
datapath=self.datapath,
|
||||
buffer_id=self.msg.buffer_id,
|
||||
in_port=in_port,
|
||||
actions=actions,
|
||||
data=self.msg_data)
|
||||
|
||||
def test__send_unknown_packet_existence_buffer(self):
|
||||
in_port = 3
|
||||
out_port = self.ofproto.OFPP_TABLE
|
||||
self.msg.buffer_id = 256
|
||||
self.arplib._send_unknown_packet(self.msg, in_port, out_port)
|
||||
actions = [self.ofpp.OFPActionOutput(self.ofproto.OFPP_TABLE, 0)]
|
||||
self.ofpp.OFPPacketOut.assert_called_once_with(
|
||||
datapath=self.datapath,
|
||||
buffer_id=self.msg.buffer_id,
|
||||
in_port=in_port,
|
||||
actions=actions,
|
||||
data=None)
|
||||
|
||||
def test__respond_arp(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
port = 3
|
||||
arptbl = self.arplib._arp_tbl[self.nets[0].net]
|
||||
pkt_ethernet = self.ethernet
|
||||
pkt_vlan = self.vlan
|
||||
pkt_arp = self.arp
|
||||
pkt_arp.opcode = self.arp.ARP_REQUEST
|
||||
pkt_arp.dst_ip = self.nets[0].ip
|
||||
with mock.patch.object(
|
||||
self.arplib, '_send_arp_reply'
|
||||
) as send_arp_rep_fn:
|
||||
self.assertTrue(
|
||||
self.arplib._respond_arp(self.datapath, port, arptbl,
|
||||
pkt_ethernet, pkt_vlan, pkt_arp))
|
||||
ethernet_ethernet = self.ethernet.ethernet(
|
||||
ethertype=pkt_ethernet.ethertype,
|
||||
dst=pkt_ethernet.src,
|
||||
src=self.nets[0].mac)
|
||||
vlan_vlan = self.vlan.vlan(cfi=pkt_vlan.cfi,
|
||||
ethertype=pkt_vlan.ethertype,
|
||||
pcp=pkt_vlan.pcp,
|
||||
vid=pkt_vlan.vid)
|
||||
arp_arp = self.arp.arp(opcode=self.arp.ARP_REPLY,
|
||||
src_mac=self.nets[0].mac,
|
||||
src_ip=pkt_arp.dst_ip,
|
||||
dst_mac=pkt_arp.src_mac,
|
||||
dst_ip=pkt_arp.src_ip)
|
||||
self.fake_add_protocol.assert_has_calls([mock.call(ethernet_ethernet),
|
||||
mock.call(vlan_vlan),
|
||||
mock.call(arp_arp)])
|
||||
send_arp_rep_fn.assert_called_once_with(
|
||||
self.datapath, port, self.packet_mod)
|
||||
|
||||
def _test__respond_arp(self, pkt_arp):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
port = 3
|
||||
arptbl = self.arplib._arp_tbl[self.nets[0].net]
|
||||
pkt_ethernet = mock.Mock()
|
||||
pkt_vlan = mock.Mock()
|
||||
self.assertFalse(
|
||||
self.arplib._respond_arp(self.datapath, port, arptbl,
|
||||
pkt_ethernet, pkt_vlan, pkt_arp))
|
||||
|
||||
def test__respond_arp_non_arp_req(self):
|
||||
pkt_arp = mock.Mock()
|
||||
pkt_arp.opcode = self.arp.ARP_REPLY
|
||||
self._test__respond_arp(pkt_arp)
|
||||
|
||||
def test__respond_arp_ip_not_found_in_arptable(self):
|
||||
pkt_arp = mock.Mock()
|
||||
pkt_arp.opcode = self.arp.ARP_REQUEST
|
||||
pkt_arp.dst_ip = self.nets[1].ip
|
||||
self._test__respond_arp(pkt_arp)
|
||||
|
||||
def test_add_arp_table_entry(self):
|
||||
self.arplib.add_arp_table_entry(self.nets[0].net,
|
||||
self.nets[0].ip, self.nets[0].mac)
|
||||
self.assertEqual(
|
||||
self.arplib._arp_tbl,
|
||||
{self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}})
|
||||
|
||||
def test_add_arp_table_entry_multiple_net(self):
|
||||
self.arplib.add_arp_table_entry(self.nets[0].net,
|
||||
self.nets[0].ip, self.nets[0].mac)
|
||||
self.arplib.add_arp_table_entry(self.nets[2].net,
|
||||
self.nets[2].ip, self.nets[2].mac)
|
||||
self.assertEqual(
|
||||
self.arplib._arp_tbl,
|
||||
{self.nets[0].net: {self.nets[0].ip: self.nets[0].mac},
|
||||
self.nets[2].net: {self.nets[2].ip: self.nets[2].mac}})
|
||||
|
||||
def test_add_arp_table_entry_multiple_ip(self):
|
||||
self.arplib.add_arp_table_entry(self.nets[0].net,
|
||||
self.nets[0].ip, self.nets[0].mac)
|
||||
self.arplib.add_arp_table_entry(self.nets[0].net,
|
||||
self.nets[1].ip, self.nets[1].mac)
|
||||
self.assertEqual(
|
||||
self.arplib._arp_tbl,
|
||||
{self.nets[0].net: {self.nets[0].ip: self.nets[0].mac,
|
||||
self.nets[1].ip: self.nets[1].mac}})
|
||||
|
||||
def test_del_arp_table_entry(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
self.arplib.del_arp_table_entry(self.nets[0].net, self.nets[0].ip)
|
||||
self.assertEqual(self.arplib._arp_tbl, {})
|
||||
|
||||
def test_del_arp_table_entry_multiple_net(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac},
|
||||
self.nets[2].net: {self.nets[2].ip: self.nets[2].mac}}
|
||||
self.arplib.del_arp_table_entry(self.nets[0].net, self.nets[0].ip)
|
||||
self.assertEqual(
|
||||
self.arplib._arp_tbl,
|
||||
{self.nets[2].net: {self.nets[2].ip: self.nets[2].mac}})
|
||||
|
||||
def test_del_arp_table_entry_multiple_ip(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac,
|
||||
self.nets[1].ip: self.nets[1].mac}}
|
||||
self.arplib.del_arp_table_entry(self.nets[0].net, self.nets[1].ip)
|
||||
self.assertEqual(
|
||||
self.arplib._arp_tbl,
|
||||
{self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}})
|
||||
|
||||
def _fake_get_protocol(self, net_type):
|
||||
if net_type == self.ethernet.ethernet:
|
||||
if self._fake_get_protocol_ethernet:
|
||||
return self.proto_ethernet_mod
|
||||
else:
|
||||
return
|
||||
if net_type == self.vlan.vlan:
|
||||
if self._fake_get_protocol_vlan:
|
||||
return self.proto_vlan_mod
|
||||
else:
|
||||
return
|
||||
if net_type == self.arp.arp:
|
||||
if self._fake_get_protocol_arp:
|
||||
return self.proto_arp_mod
|
||||
else:
|
||||
return
|
||||
|
||||
def test_packet_in_handler(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.arplib, '_respond_arp',
|
||||
return_value=True),
|
||||
mock.patch.object(self.arplib,
|
||||
'_add_flow_to_avoid_unknown_packet'),
|
||||
mock.patch.object(self.arplib,
|
||||
'_send_unknown_packet'),
|
||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
||||
self.arplib.packet_in_handler(self.ev)
|
||||
self.assertFalse(add_flow_fn.call_count)
|
||||
self.assertFalse(send_unknown_pk_fn.call_count)
|
||||
res_arp_fn.assert_called_once_with(
|
||||
self.datapath, self.inport,
|
||||
self.arplib._arp_tbl[self.nets[0].net],
|
||||
self.proto_ethernet_mod, self.proto_vlan_mod, self.proto_arp_mod)
|
||||
|
||||
def _test_packet_in_handler(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.arplib, '_respond_arp',
|
||||
return_value=True),
|
||||
mock.patch.object(self.arplib,
|
||||
'_add_flow_to_avoid_unknown_packet'),
|
||||
mock.patch.object(self.arplib,
|
||||
'_send_unknown_packet'),
|
||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
||||
self.arplib.packet_in_handler(self.ev)
|
||||
self.assertFalse(add_flow_fn.call_count)
|
||||
self.assertFalse(send_unknown_pk_fn.call_count)
|
||||
self.assertFalse(res_arp_fn.call_count)
|
||||
|
||||
def test_packet_in_handler_non_ethernet(self):
|
||||
self._fake_get_protocol_ethernet = False
|
||||
self._test_packet_in_handler()
|
||||
|
||||
def test_packet_in_handler_non_vlan(self):
|
||||
self._fake_get_protocol_vlan = False
|
||||
self._test_packet_in_handler()
|
||||
|
||||
def test_packet_in_handler_non_arp(self):
|
||||
self._fake_get_protocol_arp = False
|
||||
self._test_packet_in_handler()
|
||||
|
||||
def test_packet_in_handler_unknown_network(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.arplib, '_respond_arp',
|
||||
return_value=False),
|
||||
mock.patch.object(self.arplib,
|
||||
'_add_flow_to_avoid_unknown_packet'),
|
||||
mock.patch.object(self.arplib,
|
||||
'_send_unknown_packet'),
|
||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
||||
self.arplib.packet_in_handler(self.ev)
|
||||
add_flow_fn.assert_called_once_with(
|
||||
self.datapath,
|
||||
self.datapath.ofproto_parser.OFPMatch(
|
||||
eth_type=self.ethernet.ETH_TYPE_ARP,
|
||||
vlan_vid=self.proto_vlan_mod.vid |
|
||||
self.datapath.ofproto.OFPVID_PRESENT,
|
||||
arp_op=self.arp.ARP_REQUEST,
|
||||
arp_tpa=self.proto_arp_mod.dst_ip))
|
||||
send_unknown_pk_fn.assert_called_once_with(
|
||||
self.ev.msg, self.msg.match['in_port'],
|
||||
self.datapath.ofproto.OFPP_TABLE)
|
||||
res_arp_fn.assert_called_once_with(
|
||||
self.datapath, self.inport,
|
||||
self.arplib._arp_tbl[self.nets[0].net],
|
||||
self.proto_ethernet_mod, self.proto_vlan_mod, self.proto_arp_mod)
|
@ -32,12 +32,10 @@ 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
|
||||
from neutron.tests import base
|
||||
from neutron.tests.unit.ofagent import fake_oflib
|
||||
from neutron.tests.unit.ofagent import ofa_test_base
|
||||
|
||||
|
||||
NOTIFIER = ('neutron.plugins.ml2.rpc.AgentNotifierApi')
|
||||
OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0"
|
||||
|
||||
|
||||
def _mock_port(is_neutron=True, normalized_name=None):
|
||||
@ -48,30 +46,7 @@ def _mock_port(is_neutron=True, normalized_name=None):
|
||||
return p
|
||||
|
||||
|
||||
class OFAAgentTestCase(base.BaseTestCase):
|
||||
|
||||
_AGENT_NAME = 'neutron.plugins.ofagent.agent.ofa_neutron_agent'
|
||||
|
||||
def setUp(self):
|
||||
super(OFAAgentTestCase, self).setUp()
|
||||
self.fake_oflib_of = fake_oflib.patch_fake_oflib_of().start()
|
||||
self.mod_agent = importutils.import_module(self._AGENT_NAME)
|
||||
self.ryuapp = mock.Mock()
|
||||
|
||||
def setup_config(self):
|
||||
cfg.CONF.set_default('firewall_driver',
|
||||
'neutron.agent.firewall.NoopFirewallDriver',
|
||||
group='SECURITYGROUP')
|
||||
cfg.CONF.register_cli_opts([
|
||||
cfg.StrOpt('ofp-listen-host', default='',
|
||||
help='openflow listen host'),
|
||||
cfg.IntOpt('ofp-tcp-listen-port', default=6633,
|
||||
help='openflow tcp listen port')
|
||||
])
|
||||
cfg.CONF.set_override('root_helper', 'fake_helper', group='AGENT')
|
||||
|
||||
|
||||
class CreateAgentConfigMap(OFAAgentTestCase):
|
||||
class CreateAgentConfigMap(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
def test_create_agent_config_map_succeeds(self):
|
||||
self.assertTrue(self.mod_agent.create_agent_config_map(cfg.CONF))
|
||||
@ -116,7 +91,7 @@ class CreateAgentConfigMap(OFAAgentTestCase):
|
||||
[p_const.TYPE_GRE, p_const.TYPE_VXLAN])
|
||||
|
||||
|
||||
class TestOFANeutronAgentOVSBridge(OFAAgentTestCase):
|
||||
class TestOFANeutronAgentOVSBridge(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOFANeutronAgentOVSBridge, self).setUp()
|
||||
@ -209,7 +184,7 @@ class TestOFANeutronAgentOVSBridge(OFAAgentTestCase):
|
||||
self.ovs.setup_ofp()
|
||||
|
||||
|
||||
class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOFANeutronAgent, self).setUp()
|
||||
@ -229,23 +204,6 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
def start(self, interval=0):
|
||||
self.f()
|
||||
|
||||
def _mk_test_dp(name):
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
dp = mock.Mock()
|
||||
dp.ofproto = ofp
|
||||
dp.ofproto_parser = ofpp
|
||||
dp.__repr__ = lambda _self: name
|
||||
return dp
|
||||
|
||||
def _mk_test_br(name):
|
||||
dp = _mk_test_dp(name)
|
||||
br = mock.Mock()
|
||||
br.datapath = dp
|
||||
br.ofproto = dp.ofproto
|
||||
br.ofparser = dp.ofproto_parser
|
||||
return br
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.mod_agent.OFANeutronAgent,
|
||||
'setup_integration_br',
|
||||
@ -264,14 +222,14 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
self.agent = self.mod_agent.OFANeutronAgent(self.ryuapp, **kwargs)
|
||||
|
||||
self.agent.sg_agent = mock.Mock()
|
||||
self.int_dp = _mk_test_dp('int_br')
|
||||
self.int_dp = self._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.tun_br = self._mk_test_br('tun_br')
|
||||
self.agent.phys_brs['phys-net1'] = self._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')
|
||||
self.datapath = self._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)
|
||||
@ -796,6 +754,44 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
self.agent.fdb_remove(None, fdb_entry)
|
||||
del_port_fn.assert_called_once_with(self.tun_name2)
|
||||
|
||||
def test_add_arp_table_entry(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
fdb_entry = {self.lvms[0].net:
|
||||
{'network_type': self.tunnel_type,
|
||||
'segment_id': 'tun1',
|
||||
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
||||
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
||||
with mock.patch.multiple(self.agent,
|
||||
ryu_send_msg=mock.DEFAULT,
|
||||
setup_tunnel_port=mock.DEFAULT):
|
||||
self.agent.fdb_add(None, fdb_entry)
|
||||
calls = [
|
||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
||||
'ip1', 'mac1'),
|
||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
||||
'ip2', 'mac2')
|
||||
]
|
||||
self.ryuapp.add_arp_table_entry.assert_has_calls(calls)
|
||||
|
||||
def test_del_arp_table_entry(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
fdb_entry = {self.lvms[0].net:
|
||||
{'network_type': self.tunnel_type,
|
||||
'segment_id': 'tun1',
|
||||
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
||||
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
||||
with mock.patch.multiple(self.agent,
|
||||
ryu_send_msg=mock.DEFAULT,
|
||||
setup_tunnel_port=mock.DEFAULT):
|
||||
self.agent.fdb_remove(None, fdb_entry)
|
||||
calls = [
|
||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
||||
'ip1'),
|
||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
||||
'ip2')
|
||||
]
|
||||
self.ryuapp.del_arp_table_entry.assert_has_calls(calls)
|
||||
|
||||
def test_recl_lv_port_to_preserve(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
self.agent.enable_tunneling = True
|
||||
@ -1119,8 +1115,23 @@ class TestOFANeutronAgent(OFAAgentTestCase):
|
||||
_get_ports.assert_called_once_with('hoge')
|
||||
self.assertEqual(set(names), result)
|
||||
|
||||
def test_setup_tunnel_br(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.int_br,
|
||||
'add_patch_port', return_value='1'),
|
||||
mock.patch.object(self.agent.tun_br,
|
||||
'add_patch_port', return_value='2'),
|
||||
mock.patch.object(self.mod_agent, 'OVSBridge',
|
||||
return_value=self.agent.tun_br),
|
||||
mock.patch.object(self.agent,
|
||||
'_tun_br_output_arp_packet_to_controller')
|
||||
) as (int_add_patch_port, tun_add_patch_port,
|
||||
ovs_br_class, tun_output_ctrl):
|
||||
self.agent.setup_tunnel_br(cfg.CONF.OVS.tunnel_bridge)
|
||||
tun_output_ctrl.assert_called_once_with(self.agent.tun_br)
|
||||
|
||||
class AncillaryBridgesTest(OFAAgentTestCase):
|
||||
|
||||
class AncillaryBridgesTest(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(AncillaryBridgesTest, self).setUp()
|
||||
|
Loading…
Reference in New Issue
Block a user