ofagent: Vendor code decomposition

networking-ofagent is now available on StackForge.
(http://git.openstack.org/cgit/stackforge/networking-ofagent)

Partially-implements: blueprint core-vendor-decomposition
Closes-Bug: #1412653
Change-Id: I8a5bd10a346df5ec726635c47f18bb5c472823ed
This commit is contained in:
YAMAMOTO Takashi 2015-01-26 16:41:24 +09:00
parent 816efd30bb
commit af4de72054
26 changed files with 2 additions and 3091 deletions

View File

@ -411,7 +411,7 @@ The following chart captures the following aspects:
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
| networking-odl_ | ml2,l3,lb,fw | yes | no | [C] | Kilo |
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
| networking-ofagent_ | ml2 | yes | no | [B] | |
| networking-ofagent_ | ml2 | yes | no | [C] | Kilo |
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+
| networking-ovs-dpdk_ | | | | | |
+-------------------------------+-----------------------+-----------+------------------+---------+--------------+

View File

@ -1,16 +0,0 @@
# neutron-rootwrap command filters for nodes on which
# neutron-ofagent-agent is expected to control network
#
# This file should be owned by (and only-writeable by) the root user
# format seems to be
# cmd-name: filter-name, raw-command, user, args
[Filters]
# ovs_lib
ovs-vsctl: CommandFilter, ovs-vsctl, root
# ip_lib
ip: IpFilter, ip, root
ip_exec: IpNetnsExecFilter, ip, root

View File

@ -1,57 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# All Rights Reserved.
#
# Based on openvswitch mechanism driver.
#
# Copyright (c) 2013 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.agent import securitygroups_rpc
from neutron.common import constants
from neutron.extensions import portbindings
from neutron.openstack.common import log
from neutron.plugins.common import constants as p_constants
from neutron.plugins.ml2.drivers import mech_agent
LOG = log.getLogger(__name__)
class OfagentMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
"""Attach to networks using ofagent L2 agent.
The OfagentMechanismDriver integrates the ml2 plugin with the
ofagent L2 agent. Port binding with this driver requires the
ofagent agent to be running on the port's host, and that agent
to have connectivity to at least one segment of the port's
network.
"""
def __init__(self):
sg_enabled = securitygroups_rpc.is_firewall_enabled()
vif_details = {portbindings.CAP_PORT_FILTER: sg_enabled,
portbindings.OVS_HYBRID_PLUG: sg_enabled}
super(OfagentMechanismDriver, self).__init__(
constants.AGENT_TYPE_OFA,
portbindings.VIF_TYPE_OVS,
vif_details)
def get_allowed_network_types(self, agent):
return (agent['configurations'].get('tunnel_types', []) +
[p_constants.TYPE_LOCAL, p_constants.TYPE_FLAT,
p_constants.TYPE_VLAN])
def get_mappings(self, agent):
return dict(agent['configurations'].get('interface_mappings', {}))

View File

@ -1,100 +0,0 @@
This directory includes agent for OpenFlow Agent mechanism driver.
# -- Installation
For how to install/set up ML2 mechanism driver for OpenFlow Agent, please refer to
https://github.com/osrg/ryu/wiki/OpenStack
# -- Notes for updating from Icehouce
OVS.bridge_mappings is deprecated for ofagent. It was removed in Kilo.
Please use AGENT.physical_interface_mappings instead.
To mimic an existing setup with bridge_mapping, you can create
a veth pair, link one side of it to the bridge, and then specify
the other side in physical_interface_mappings.
For example, if you have the following:
[OVS]
bridge_mappings=public:br-ex
You can do:
# ip link add int-public type veth peer name phy-public
# ip link set int-public up
# ip link set phy-public up
# ovs-vsctl add-port br-ex phy-public
and then replace the bridge_mappings with:
[AGENT]
physical_interface_mappings=public:int-public
After Icehouce, most of the functionality have been folded into
a single bridge, the integration bridge. (aka. br-int)
The integration bridge is the only bridge which would have an
OpenFlow connection to the embedded controller in ofagent now.
- ofagent no longer uses a separate bridge for tunneling.
Please remove br-tun if you have one.
# ovs-vsctl del-br br-tun
- ofagent no longer acts as an OpenFlow controller for physical bridges.
Please remove set-controller configuration from your physical bridges.
# ovs-vsctl del-controller ${PHYSICAL_BRIDGE}
The support of ancillary bridges has been removed after Icehouce.
While you can still use these bridges to provide connectivity,
neutron-ofagent-agent no longer reports port state changes (up/down)
for these bridges. If it is a problem for you, please consider
tweaking your configuration to avoid using ancillary bridges.
We recommend to use a provider network instead as the following:
- Make l3-agent external_network_bridge configuration empty.
eg.
[DEFAULT]
external_network_bridge=
- (Re-)create a network (and subnet) for public connectivity with
a flat provider network.
eg.
neutron net-create $PUBLIC_NETWORK -- \
--router:external=True \
--provider:network_type:flat \
--provider:physical_network=$PUBLIC_PHYSICAL_NETWORK
- Associate your neutron router to the above network.
eg.
neutron router-gateway-clear $ROUTER_ID
neutron router-gateway-set $ROUTER_ID $PUBLIC_NETWORK
- Add the corresponding entry to bridge_mappings.
eg.
[OVS]
bridge_mappings=$PUBLIC_PHYSICAL_NETWORK:$PUBLIC_BRIDGE
The port naming scheme for ofagent has been changed after Icehouce.
If you are using security groups, you should switch firewall_driver
accordingly.
From:
[securitygroup]
firewall_driver=neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver
To:
[securitygroup]
firewall_driver=neutron.agent.linux.iptables_firewall.IptablesFirewallDriver
# -- Ryu General
For general Ryu stuff, please refer to
http://osrg.github.io/ryu/
Ryu is available at github
git://github.com/osrg/ryu.git
https://github.com/osrg/ryu
The mailing is at
ryu-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/ryu-devel
Enjoy!

View File

@ -1,187 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
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 neutron.common import log
from neutron.i18n import _LI
from neutron.openstack.common import log as logging
import neutron.plugins.ofagent.agent.metadata as meta
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 = {}
self.br = None
def set_bridge(self, br):
self.br = br
@log.log
def _send_arp_reply(self, datapath, port, 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)
@log.log
def _send_unknown_packet(self, msg, in_port, out_port):
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))
if pkt_vlan:
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
@log.log
def add_arp_table_entry(self, network, ip, mac):
if network in self._arp_tbl:
self._arp_tbl[network][ip] = mac
else:
self._arp_tbl[network] = {ip: mac}
@log.log
def del_arp_table_entry(self, network, ip):
if network not in self._arp_tbl:
LOG.debug("removal of unknown network %s", network)
return
if self._arp_tbl[network].pop(ip, None) is None:
LOG.debug("removal of unknown ip %s", ip)
return
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
if self.br is None:
LOG.info(_LI("No bridge is set"))
return
if self.br.datapath.id != datapath.id:
LOG.info(_LI("Unknown bridge %(dpid)s ours %(ours)s"),
{"dpid": datapath.id, "ours": self.br.datapath.id})
return
ofp = datapath.ofproto
port = msg.match['in_port']
metadata = msg.match.get('metadata')
# NOTE(yamamoto): Ryu packet library can raise various exceptions
# on a corrupted packet.
try:
pkt = packet.Packet(msg.data)
except Exception as e:
LOG.debug("Unparsable packet: got exception %s", e)
return
LOG.debug("packet-in dpid %(dpid)s in_port %(port)s pkt %(pkt)s",
{'dpid': dpid_lib.dpid_to_str(datapath.id),
'port': port, 'pkt': pkt})
if metadata is None:
LOG.info(_LI("drop non tenant packet"))
return
network = metadata & meta.NETWORK_MASK
pkt_ethernet = pkt.get_protocol(ethernet.ethernet)
if not pkt_ethernet:
LOG.debug("drop non-ethernet packet")
return
pkt_vlan = pkt.get_protocol(vlan.vlan)
pkt_arp = pkt.get_protocol(arp.arp)
if not pkt_arp:
LOG.debug("drop non-arp 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(_LI("unknown network %s"), network)
# add a flow to skip a packet-in to a controller.
self.br.arp_passthrough(network=network, tpa=pkt_arp.dst_ip)
# send an unknown arp packet to the table.
self._send_unknown_packet(msg, port, ofp.OFPP_TABLE)

View File

@ -1,19 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
LOCAL_VLAN_MIN = 1
LOCAL_VLAN_MAX = 0xfff
LOCAL_VLAN_MASK = 0xfff

View File

@ -1,407 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
OpenFlow1.3 flow table for OFAgent
* requirements
** plain OpenFlow 1.3. no vendor extensions.
* legends
xxx: network id (agent internal use)
yyy: segment id (vlan id, gre key, ...)
a,b,c: tunnel port (tun_ofports, map[net_id].tun_ofports)
i,j,k: vm port (map[net_id].vif_ports[vif_id].ofport)
x,y,z: physical port (int_ofports)
N: tunnel type (0 for TYPE_GRE, 1 for TYPE_xxx, ...)
iii: unknown ip address
uuu: unicast l2 address
* tables (in order)
CHECK_IN_PORT
TUNNEL_IN+N
PHYS_IN
LOCAL_IN
ARP_PASSTHROUGH
ARP_RESPONDER
TUNNEL_OUT
LOCAL_OUT
PHYS_OUT
TUNNEL_FLOOD+N
PHYS_FLOOD
LOCAL_FLOOD
* CHECK_IN_PORT
for each vm ports:
// check_in_port_add_local_port, check_in_port_delete_port
in_port=i, write_metadata(LOCAL|xxx),goto(LOCAL_IN)
TYPE_GRE
for each tunnel ports:
// check_in_port_add_tunnel_port, check_in_port_delete_port
in_port=a, goto(TUNNEL_IN+N)
TYPE_VLAN
for each networks ports:
// provision_tenant_physnet, reclaim_tenant_physnet
in_port=x,vlan_vid=present|yyy, write_metadata(xxx),goto(PHYS_IN)
TYPE_FLAT
// provision_tenant_physnet, reclaim_tenant_physnet
in_port=x, write_metadata(xxx),goto(PHYS_IN)
default drop
* TUNNEL_IN+N (per tunnel types) tunnel -> network
for each networks:
// provision_tenant_tunnel, reclaim_tenant_tunnel
tun_id=yyy, write_metadata(xxx),goto(TUNNEL_OUT)
default drop
* PHYS_IN
default goto(TUNNEL_OUT)
* LOCAL_IN
default goto(next_table)
* ARP_PASSTHROUGH
for each unknown tpa:
// arp_passthrough
arp,arp_op=request,metadata=xxx,tpa=iii, idle_timeout=5, goto(TUNNEL_OUT)
default goto(next_table)
* ARP_RESPONDER
arp,arp_op=request, output:controller
default goto(next_table)
* TUNNEL_OUT
TYPE_GRE
// !FLOODING_ENTRY
// install_tunnel_output, delete_tunnel_output
metadata=LOCAL|xxx,eth_dst=uuu set_tunnel(yyy),output:a
default goto(next table)
* LOCAL_OUT
for each known destinations:
// local_out_add_port, local_out_delete_port
metadata=xxx,eth_dst=uuu output:i
default goto(next table)
* PHYS_OUT
NOTE(yamamoto): currently this table is always empty.
default goto(next table)
* TUNNEL_FLOOD+N. (per tunnel types)
network -> tunnel/vlan
output to tunnel/physical ports
"next table" might be LOCAL_OUT
TYPE_GRE
for each networks:
// FLOODING_ENTRY
// install_tunnel_output, delete_tunnel_output
metadata=LOCAL|xxx, set_tunnel(yyy),output:a,b,c,goto(next table)
default goto(next table)
* PHYS_FLOOD
TYPE_VLAN
for each networks:
// provision_tenant_physnet, reclaim_tenant_physnet
metadata=LOCAL|xxx, push_vlan:0x8100,set_field:present|yyy->vlan_vid,
output:x,pop_vlan,goto(next table)
TYPE_FLAT
for each networks:
// provision_tenant_physnet, reclaim_tenant_physnet
metadata=LOCAL|xxx, output:x,goto(next table)
default goto(next table)
* LOCAL_FLOOD
for each networks:
// local_flood_update, local_flood_delete
metadata=xxx, output:i,j,k
or
metadata=xxx,eth_dst=broadcast, output:i,j,k
default drop
* references
** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic
*** we use metadata instead of "internal" VLANs
*** we don't want to use NX learn action
"""
from ryu.lib.packet import arp
from ryu.ofproto import ether
from neutron.plugins.common import constants as p_const
import neutron.plugins.ofagent.agent.metadata as meta
from neutron.plugins.ofagent.agent import ofswitch
from neutron.plugins.ofagent.agent import tables
class OFAgentIntegrationBridge(ofswitch.OpenFlowSwitch):
"""ofagent br-int specific logic."""
def setup_default_table(self):
self.delete_flows()
self.install_default_drop(tables.CHECK_IN_PORT)
for t in tables.TUNNEL_IN.values():
self.install_default_drop(t)
self.install_default_goto(tables.PHYS_IN, tables.TUNNEL_OUT)
self.install_default_goto_next(tables.LOCAL_IN)
self.install_default_goto_next(tables.ARP_PASSTHROUGH)
self.install_arp_responder(tables.ARP_RESPONDER)
self.install_default_goto_next(tables.TUNNEL_OUT)
self.install_default_goto_next(tables.LOCAL_OUT)
self.install_default_goto_next(tables.PHYS_OUT)
for t in tables.TUNNEL_FLOOD.values():
self.install_default_goto_next(t)
self.install_default_goto_next(tables.PHYS_FLOOD)
self.install_default_drop(tables.LOCAL_FLOOD)
def install_arp_responder(self, table_id):
(dp, ofp, ofpp) = self._get_dp()
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(dp,
table_id=table_id,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
self.install_default_goto_next(table_id)
def install_tunnel_output(self, table_id,
network, segmentation_id,
ports, goto_next, **additional_matches):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network, meta.LOCAL),
**additional_matches)
actions = [ofpp.OFPActionSetField(tunnel_id=segmentation_id)]
actions += [ofpp.OFPActionOutput(port=p) for p in ports]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
if goto_next:
instructions += [
ofpp.OFPInstructionGotoTable(table_id=table_id + 1),
]
msg = ofpp.OFPFlowMod(dp,
table_id=table_id,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def delete_tunnel_output(self, table_id,
network, **additional_matches):
(dp, _ofp, ofpp) = self._get_dp()
self.delete_flows(table_id=table_id,
metadata=meta.mk_metadata(network, meta.LOCAL),
**additional_matches)
def provision_tenant_tunnel(self, network_type, network, segmentation_id):
(dp, _ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(tunnel_id=segmentation_id)
metadata = meta.mk_metadata(network)
instructions = [
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
metadata_mask=metadata[1]),
ofpp.OFPInstructionGotoTable(table_id=tables.TUNNEL_OUT),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.TUNNEL_IN[network_type],
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def reclaim_tenant_tunnel(self, network_type, network, segmentation_id):
table_id = tables.TUNNEL_IN[network_type]
self.delete_flows(table_id=table_id, tunnel_id=segmentation_id)
def provision_tenant_physnet(self, network_type, network,
segmentation_id, phys_port):
"""for vlan and flat."""
assert(network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT])
(dp, ofp, ofpp) = self._get_dp()
# inbound
metadata = meta.mk_metadata(network)
instructions = [
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
metadata_mask=metadata[1])
]
if network_type == p_const.TYPE_VLAN:
vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
match = ofpp.OFPMatch(in_port=phys_port, vlan_vid=vlan_vid)
actions = [ofpp.OFPActionPopVlan()]
instructions += [ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS, actions)]
else:
match = ofpp.OFPMatch(in_port=phys_port)
instructions += [ofpp.OFPInstructionGotoTable(table_id=tables.PHYS_IN)]
msg = ofpp.OFPFlowMod(dp,
priority=1,
table_id=tables.CHECK_IN_PORT,
match=match,
instructions=instructions)
self._send_msg(msg)
# outbound
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network, meta.LOCAL))
if network_type == p_const.TYPE_VLAN:
actions = [
ofpp.OFPActionPushVlan(),
ofpp.OFPActionSetField(vlan_vid=vlan_vid),
]
else:
actions = []
actions += [ofpp.OFPActionOutput(port=phys_port)]
if network_type == p_const.TYPE_VLAN:
actions += [ofpp.OFPActionPopVlan()]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
ofpp.OFPInstructionGotoTable(table_id=tables.PHYS_FLOOD + 1),
]
msg = ofpp.OFPFlowMod(dp,
priority=1,
table_id=tables.PHYS_FLOOD,
match=match,
instructions=instructions)
self._send_msg(msg)
def reclaim_tenant_physnet(self, network_type, network,
segmentation_id, phys_port):
(_dp, ofp, _ofpp) = self._get_dp()
vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
if network_type == p_const.TYPE_VLAN:
self.delete_flows(table_id=tables.CHECK_IN_PORT,
in_port=phys_port, vlan_vid=vlan_vid)
else:
self.delete_flows(table_id=tables.CHECK_IN_PORT,
in_port=phys_port)
self.delete_flows(table_id=tables.PHYS_FLOOD,
metadata=meta.mk_metadata(network))
def check_in_port_add_tunnel_port(self, network_type, port):
(dp, _ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(in_port=port)
instructions = [
ofpp.OFPInstructionGotoTable(
table_id=tables.TUNNEL_IN[network_type])
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.CHECK_IN_PORT,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def check_in_port_add_local_port(self, network, port):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(in_port=port)
metadata = meta.mk_metadata(network, meta.LOCAL)
instructions = [
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
metadata_mask=metadata[1]),
ofpp.OFPInstructionGotoTable(table_id=tables.LOCAL_IN),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.CHECK_IN_PORT,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def check_in_port_delete_port(self, port):
self.delete_flows(table_id=tables.CHECK_IN_PORT, in_port=port)
def local_flood_update(self, network, ports, flood_unicast):
(dp, ofp, ofpp) = self._get_dp()
match_all = ofpp.OFPMatch(metadata=meta.mk_metadata(network))
match_multicast = ofpp.OFPMatch(metadata=meta.mk_metadata(network),
eth_dst=('01:00:00:00:00:00',
'01:00:00:00:00:00'))
if flood_unicast:
match_add = match_all
match_del = match_multicast
else:
match_add = match_multicast
match_del = match_all
actions = [ofpp.OFPActionOutput(port=p) for p in ports]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.LOCAL_FLOOD,
priority=1,
match=match_add,
instructions=instructions)
self._send_msg(msg)
self.delete_flows(table_id=tables.LOCAL_FLOOD, strict=True,
priority=1, match=match_del)
def local_flood_delete(self, network):
self.delete_flows(table_id=tables.LOCAL_FLOOD,
metadata=meta.mk_metadata(network))
def local_out_add_port(self, network, port, mac):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network), eth_dst=mac)
actions = [ofpp.OFPActionOutput(port=port)]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.LOCAL_OUT,
priority=1,
match=match,
instructions=instructions)
self._send_msg(msg)
def local_out_delete_port(self, network, mac):
self.delete_flows(table_id=tables.LOCAL_OUT,
metadata=meta.mk_metadata(network), eth_dst=mac)
def arp_passthrough(self, network, tpa):
(dp, ofp, ofpp) = self._get_dp()
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network),
eth_type=ether.ETH_TYPE_ARP,
arp_op=arp.ARP_REQUEST,
arp_tpa=tpa)
instructions = [
ofpp.OFPInstructionGotoTable(table_id=tables.TUNNEL_OUT)]
msg = ofpp.OFPFlowMod(dp,
table_id=tables.ARP_PASSTHROUGH,
priority=1,
idle_timeout=5,
match=match,
instructions=instructions)
self._send_msg(msg)

View File

@ -1,38 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from ryu.lib import hub
hub.patch()
import sys
from oslo_config import cfg
from ryu.base.app_manager import AppManager
from ryu import cfg as ryu_cfg
from neutron.common import config as common_config
def main():
common_config.init(sys.argv[1:])
# the following check is a transitional workaround to make this work
# with different versions of ryu.
# TODO(yamamoto) remove this later
if ryu_cfg.CONF is not cfg.CONF:
ryu_cfg.CONF(project='ryu', args=[])
common_config.setup_logging()
AppManager.run_apps(['neutron.plugins.ofagent.agent.ofa_neutron_agent'])

View File

@ -1,26 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.plugins.ofagent.agent import constants as const
# metadata mask
NETWORK_MASK = const.LOCAL_VLAN_MASK
LOCAL = 0x10000 # the packet came from local vm ports
def mk_metadata(network, flags=0):
return (flags | network, flags | NETWORK_MASK)

View File

@ -1,78 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from ryu.app.ofctl import api as ofctl_api
class OpenFlowSwitch(object):
def __init__(self, *args, **kwargs):
super(OpenFlowSwitch, self).__init__(*args, **kwargs)
self._dp = None
# logically app doesn't belong here. just for convenience.
self._app = None
def set_dp(self, dp):
self._dp = dp
def set_app(self, app):
self._app = app
def _get_dp(self):
"""a convenient method for openflow message composers"""
dp = self._dp
return (dp, dp.ofproto, dp.ofproto_parser)
def _send_msg(self, msg):
return ofctl_api.send_msg(self._app, msg)
def delete_flows(self, table_id=None, strict=False, priority=0,
match=None, **match_kwargs):
(dp, ofp, ofpp) = self._get_dp()
if table_id is None:
table_id = ofp.OFPTT_ALL
if match is None:
match = ofpp.OFPMatch(**match_kwargs)
if strict:
cmd = ofp.OFPFC_DELETE_STRICT
else:
cmd = ofp.OFPFC_DELETE
msg = ofpp.OFPFlowMod(dp,
command=cmd,
table_id=table_id,
match=match,
priority=priority,
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY)
self._send_msg(msg)
def install_default_drop(self, table_id):
(dp, _ofp, ofpp) = self._get_dp()
msg = ofpp.OFPFlowMod(dp,
table_id=table_id,
priority=0)
self._send_msg(msg)
def install_default_goto(self, table_id, dest_table_id):
(dp, _ofp, ofpp) = self._get_dp()
instructions = [ofpp.OFPInstructionGotoTable(table_id=dest_table_id)]
msg = ofpp.OFPFlowMod(dp,
table_id=table_id,
priority=0,
instructions=instructions)
self._send_msg(msg)
def install_default_goto_next(self, table_id):
self.install_default_goto(table_id, table_id + 1)

View File

@ -1,90 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.common import constants as n_const
class OFPort(object):
def __init__(self, port_name, ofport):
self.port_name = port_name
self.ofport = ofport
@classmethod
def from_ofp_port(cls, ofp_port):
"""Convert from ryu OFPPort."""
return cls(port_name=ofp_port.name, ofport=ofp_port.port_no)
PORT_NAME_LEN = 14
PORT_NAME_PREFIXES = [
n_const.TAP_DEVICE_PREFIX, # common cases, including ovs_use_veth=True
"qvo", # nova hybrid interface driver
"qr-", # l3-agent INTERNAL_DEV_PREFIX (ovs_use_veth=False)
"qg-", # l3-agent EXTERNAL_DEV_PREFIX (ovs_use_veth=False)
]
def _is_neutron_port(name):
"""Return True if the port name looks like a neutron port."""
if len(name) != PORT_NAME_LEN:
return False
for pref in PORT_NAME_PREFIXES:
if name.startswith(pref):
return True
return False
def get_normalized_port_name(interface_id):
"""Convert from neutron device id (uuid) to "normalized" port name.
This needs to be synced with ML2 plugin's _device_to_port_id().
An assumption: The switch uses an OS's interface name as the
corresponding OpenFlow port name.
NOTE(yamamoto): While it's true for Open vSwitch, it isn't
necessarily true everywhere. For example, LINC uses something
like "LogicalSwitch0-Port2".
NOTE(yamamoto): The actual prefix might be different. For example,
with the hybrid interface driver, it's "qvo". However, we always
use "tap" prefix throughout the agent and plugin for simplicity.
Some care should be taken when talking to the switch.
"""
return (n_const.TAP_DEVICE_PREFIX + interface_id)[0:PORT_NAME_LEN]
def _normalize_port_name(name):
"""Normalize port name.
See comments in _get_ofport_name.
"""
for pref in PORT_NAME_PREFIXES:
if name.startswith(pref):
return n_const.TAP_DEVICE_PREFIX + name[len(pref):]
return name
class Port(OFPort):
def __init__(self, *args, **kwargs):
super(Port, self).__init__(*args, **kwargs)
self.vif_mac = None
def is_neutron_port(self):
"""Return True if the port looks like a neutron port."""
return _is_neutron_port(self.port_name)
def normalized_port_name(self):
return _normalize_port_name(self.port_name)

View File

@ -1,62 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from neutron.plugins.common import constants as p_const
def _seq():
"""Yield sequential numbers starting from 0."""
i = 0
while True:
yield i
i += 1
_table_id_gen = _seq()
def _table_id():
"""A simple table id allocator."""
return _table_id_gen.next()
# Supported tunnel types.
TUNNEL_TYPES = [
p_const.TYPE_GRE,
p_const.TYPE_VXLAN,
]
# Reversed version of TUNNEL_TYPES.
TUNNEL_TYPE_IDX = dict((t, TUNNEL_TYPES.index(t)) for t in TUNNEL_TYPES)
# We use sequential table ids starting from 0.
# We don't hardcode values here to avoid manual reassignments eg. when adding
# a new tunnel type.
# See a big comment in flows.py for how each tables are used.
CHECK_IN_PORT = _table_id()
TUNNEL_IN = {}
for t in TUNNEL_TYPES:
TUNNEL_IN[t] = _table_id()
PHYS_IN = _table_id()
LOCAL_IN = _table_id()
ARP_PASSTHROUGH = _table_id()
ARP_RESPONDER = _table_id()
TUNNEL_OUT = _table_id()
LOCAL_OUT = _table_id()
PHYS_OUT = _table_id()
TUNNEL_FLOOD = {}
for t in TUNNEL_TYPES:
TUNNEL_FLOOD[t] = _table_id()
PHYS_FLOOD = _table_id()
LOCAL_FLOOD = _table_id()

View File

@ -1,36 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from neutron.agent.common import config
from neutron.plugins.openvswitch.common import config as ovs_config
agent_opts = [
cfg.IntOpt('get_datapath_retry_times', default=60,
help=_("Number of seconds to retry acquiring "
"an Open vSwitch datapath")),
cfg.ListOpt('physical_interface_mappings',
default=[],
help=_("List of <physical_network>:<physical_interface>")),
]
cfg.CONF.register_opts(ovs_config.ovs_opts, 'OVS')
cfg.CONF.register_opts(ovs_config.agent_opts, 'AGENT')
cfg.CONF.register_opts(agent_opts, 'AGENT')
config.register_agent_state_opts_helper(cfg.CONF)

View File

@ -1,98 +0,0 @@
# Copyright (c) 2014 OpenStack Foundation
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_config import cfg
from neutron.common import constants
from neutron.extensions import portbindings
from neutron.plugins.ml2.drivers import mech_ofagent
from neutron.tests.unit.ml2 import _test_mech_agent as base
class OfagentMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
VIF_TYPE = portbindings.VIF_TYPE_OVS
VIF_DETAILS = {portbindings.CAP_PORT_FILTER: True,
portbindings.OVS_HYBRID_PLUG: True}
AGENT_TYPE = constants.AGENT_TYPE_OFA
GOOD_MAPPINGS = {'fake_physical_network': 'fake_interface'}
GOOD_TUNNEL_TYPES = ['gre', 'vxlan']
GOOD_CONFIGS = {'interface_mappings': GOOD_MAPPINGS,
'tunnel_types': GOOD_TUNNEL_TYPES}
BAD_MAPPINGS = {'wrong_physical_network': 'wrong_interface'}
BAD_TUNNEL_TYPES = ['bad_tunnel_type']
BAD_CONFIGS = {'interface_mappings': BAD_MAPPINGS,
'tunnel_types': BAD_TUNNEL_TYPES}
AGENTS = [{'alive': True,
'configurations': GOOD_CONFIGS,
'host': 'host'}]
AGENTS_DEAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'dead_host'}]
AGENTS_BAD = [{'alive': False,
'configurations': GOOD_CONFIGS,
'host': 'bad_host_1'},
{'alive': True,
'configurations': BAD_CONFIGS,
'host': 'bad_host_2'}]
def setUp(self):
super(OfagentMechanismBaseTestCase, self).setUp()
self.driver = mech_ofagent.OfagentMechanismDriver()
self.driver.initialize()
class OfagentMechanismSGDisabledBaseTestCase(OfagentMechanismBaseTestCase):
VIF_DETAILS = {portbindings.CAP_PORT_FILTER: False,
portbindings.OVS_HYBRID_PLUG: False}
def setUp(self):
cfg.CONF.set_override('enable_security_group',
False,
group='SECURITYGROUP')
super(OfagentMechanismSGDisabledBaseTestCase, self).setUp()
class OfagentMechanismGenericTestCase(OfagentMechanismBaseTestCase,
base.AgentMechanismGenericTestCase):
pass
class OfagentMechanismLocalTestCase(OfagentMechanismBaseTestCase,
base.AgentMechanismLocalTestCase):
pass
class OfagentMechanismFlatTestCase(OfagentMechanismBaseTestCase,
base.AgentMechanismFlatTestCase):
pass
class OfagentMechanismVlanTestCase(OfagentMechanismBaseTestCase,
base.AgentMechanismVlanTestCase):
pass
class OfagentMechanismGreTestCase(OfagentMechanismBaseTestCase,
base.AgentMechanismGreTestCase):
pass
class OfagentMechanismSGDisabledLocalTestCase(
OfagentMechanismSGDisabledBaseTestCase,
base.AgentMechanismLocalTestCase):
pass

View File

@ -1,150 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
class _Eq(object):
def __eq__(self, other):
return repr(self) == repr(other)
def __ne__(self, other):
return not self.__eq__(other)
class _Value(_Eq):
def __or__(self, b):
return _Op('|', self, b)
def __ror__(self, a):
return _Op('|', a, self)
class _SimpleValue(_Value):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class _Op(_Value):
def __init__(self, op, a, b):
self.op = op
self.a = a
self.b = b
def __repr__(self):
return '%s%s%s' % (self.a, self.op, self.b)
def _mkcls(name):
class Cls(_Eq):
_name = name
def __init__(self, *args, **kwargs):
self._args = args
self._kwargs = kwargs
self._hist = []
def __getattr__(self, name):
return self._kwargs[name]
def __repr__(self):
args = map(repr, self._args)
kwargs = sorted(['%s=%s' % (x, y) for x, y in
self._kwargs.items()])
return '%s(%s)' % (self._name, ', '.join(args + kwargs))
return Cls
class _Mod(object):
_cls_cache = {}
def __init__(self, name):
self._name = name
def __getattr__(self, name):
fullname = '%s.%s' % (self._name, name)
if '_' in name: # constants are named like OFPxxx_yyy_zzz
return _SimpleValue(fullname)
try:
return self._cls_cache[fullname]
except KeyError:
pass
cls = _mkcls(fullname)
self._cls_cache[fullname] = cls
return cls
def __repr__(self):
return 'Mod(%s)' % (self._name,)
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
ether = _Mod('ryu.ofproto.ether')
ofp = _Mod('ryu.ofproto.ofproto_v1_3')
ofpp = _Mod('ryu.ofproto.ofproto_v1_3_parser')
ryu_ofproto_mod.ether = ether
ryu_ofproto_mod.ofproto_v1_3 = ofp
ryu_ofproto_mod.ofproto_v1_3_parser = ofpp
ryu_app_mod = ryu_mod.app
ryu_app_ofctl_mod = ryu_app_mod.ofctl
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.ether': ether,
'ryu.ofproto.ofproto_v1_3': ofp,
'ryu.ofproto.ofproto_v1_3_parser': ofpp,
'ryu.app': ryu_app_mod,
'ryu.app.ofctl': ryu_app_ofctl_mod,
'ryu.app.ofctl.api': ryu_ofctl_api}
return mock.patch.dict('sys.modules', modules)

View File

@ -1,71 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_config import cfg
from oslo_utils import importutils
from neutron.tests import base
from neutron.tests.unit.ofagent import fake_oflib
class OFATestBase(base.BaseTestCase):
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)
super(OFATestBase, self).setUp()
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
class OFAAgentTestBase(OFATestBase):
_AGENT_NAME = 'neutron.plugins.ofagent.agent.ofa_neutron_agent'
def setUp(self):
super(OFAAgentTestBase, self).setUp()
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')
])
super(OFATestBase, self).setup_config()

View File

@ -1,334 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import collections
import contextlib
import copy
import mock
from oslo_utils import importutils
import neutron.plugins.ofagent.agent.metadata as meta
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 = 999
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,
'metadata': meta.LOCAL | self.nets[0].net}
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
self.br = mock.Mock(datapath=self.datapath)
self.arplib.set_bridge(self.br)
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_unknown_network(self):
self.arplib._arp_tbl = {
100: {"192.0.2.1": "fa:16:3e:e2:37:37"},
}
orig = copy.deepcopy(self.arplib._arp_tbl)
self.arplib.del_arp_table_entry(200, "192.0.2.1")
self.assertEqual(orig, self.arplib._arp_tbl)
def test_del_arp_table_entry_unknown_ip(self):
self.arplib._arp_tbl = {
100: {"192.0.2.1": "fa:16:3e:e2:37:37"},
}
orig = copy.deepcopy(self.arplib._arp_tbl)
self.arplib.del_arp_table_entry(100, "192.0.2.9")
self.assertEqual(orig, 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.br,
'arp_passthrough'),
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 if self._fake_get_protocol_vlan else None,
self.proto_arp_mod)
def _test_packet_in_handler_drop(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.br, 'arp_passthrough'),
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(self):
self._test_packet_in_handler()
def test_packet_in_handler_non_ethernet(self):
self._fake_get_protocol_ethernet = False
self._test_packet_in_handler_drop()
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_drop()
def test_packet_in_handler_corrupted(self):
mock.patch('ryu.lib.packet.packet.Packet',
side_effect=ValueError).start()
self._test_packet_in_handler_drop()
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.br, 'arp_passthrough'),
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(
network=self.nets[0].net,
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)

View File

@ -1,384 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_utils import importutils
import neutron.plugins.ofagent.agent.metadata as meta
from neutron.tests.unit.ofagent import ofa_test_base
class TestOFAgentFlows(ofa_test_base.OFATestBase):
_MOD = 'neutron.plugins.ofagent.agent.flows'
def setUp(self):
super(TestOFAgentFlows, self).setUp()
self.mod = importutils.import_module(self._MOD)
self.br = self.mod.OFAgentIntegrationBridge()
self.br.set_dp(self._mk_test_dp("dp"))
def test_setup_default_table(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.setup_default_table()
(dp, ofp, ofpp) = br._get_dp()
arp = importutils.import_module('ryu.lib.packet.arp')
ether = importutils.import_module('ryu.ofproto.ether')
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(), out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, priority=0, table_id=ofp.OFPTT_ALL)),
call(ofpp.OFPFlowMod(dp, priority=0, table_id=0)),
call(ofpp.OFPFlowMod(dp, priority=0, table_id=1)),
call(ofpp.OFPFlowMod(dp, priority=0, table_id=2)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=7)],
priority=0, table_id=3)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=5)],
priority=0, table_id=4)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=6)],
priority=0, table_id=5)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
[ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)])],
match=ofpp.OFPMatch(arp_op=arp.ARP_REQUEST,
eth_type=ether.ETH_TYPE_ARP), priority=1, table_id=6)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=7)],
priority=0, table_id=6)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=8)],
priority=0, table_id=7)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=9)],
priority=0, table_id=8)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=10)],
priority=0, table_id=9)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=11)],
priority=0, table_id=10)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=12)],
priority=0, table_id=11)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=13)],
priority=0, table_id=12)),
call(ofpp.OFPFlowMod(dp, priority=0, table_id=13)),
]
sendmsg.assert_has_calls(expected_calls, any_order=True)
def test_install_arp_responder(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.install_arp_responder(table_id=99)
(dp, ofp, ofpp) = br._get_dp()
arp = importutils.import_module('ryu.lib.packet.arp')
ether = importutils.import_module('ryu.ofproto.ether')
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
[ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)])],
match=ofpp.OFPMatch(arp_op=arp.ARP_REQUEST,
eth_type=ether.ETH_TYPE_ARP), priority=1, table_id=99)),
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=100)],
priority=0, table_id=99)),
]
sendmsg.assert_has_calls(expected_calls)
def test_install_tunnel_output(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.install_tunnel_output(table_id=110, network=111,
segmentation_id=112, ports=[113, 114],
goto_next=True)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(
ofpp.OFPFlowMod(
dp,
instructions=[
ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS,
[
ofpp.OFPActionSetField(tunnel_id=112),
ofpp.OFPActionOutput(port=113),
ofpp.OFPActionOutput(port=114)
]
),
ofpp.OFPInstructionGotoTable(table_id=111)
],
match=ofpp.OFPMatch(
metadata=meta.mk_metadata(111, meta.LOCAL)
),
priority=1,
table_id=110
)
)
]
sendmsg.assert_has_calls(expected_calls)
def test_delete_tunnel_output(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.delete_tunnel_output(table_id=110, network=111)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(
ofpp.OFPFlowMod(
dp,
command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(
metadata=meta.mk_metadata(111, meta.LOCAL)
),
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, priority=0, table_id=110
)
)
]
sendmsg.assert_has_calls(expected_calls)
def test_provision_tenant_tunnel(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.provision_tenant_tunnel(network_type="gre", network=150,
segmentation_id=151)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionWriteMetadata(metadata=150,
metadata_mask=meta.NETWORK_MASK),
ofpp.OFPInstructionGotoTable(table_id=7)],
match=ofpp.OFPMatch(tunnel_id=151), priority=1, table_id=1))
]
sendmsg.assert_has_calls(expected_calls)
def test_reclaim_tenant_tunnel(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.reclaim_tenant_tunnel(network_type="gre", network=150,
segmentation_id=151)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(tunnel_id=151), out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, priority=0, table_id=1))
]
sendmsg.assert_has_calls(expected_calls)
def test_provision_tenant_physnet(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.provision_tenant_physnet(network_type="vlan", network=150,
segmentation_id=151, phys_port=99)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionWriteMetadata(metadata=150,
metadata_mask=meta.NETWORK_MASK),
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
ofpp.OFPActionPopVlan()]),
ofpp.OFPInstructionGotoTable(table_id=3)],
match=ofpp.OFPMatch(in_port=99,
vlan_vid=151 | ofp.OFPVID_PRESENT),
priority=1, table_id=0)),
call(
ofpp.OFPFlowMod(
dp,
instructions=[
ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS,
[
ofpp.OFPActionPushVlan(),
ofpp.OFPActionSetField(
vlan_vid=151 | ofp.OFPVID_PRESENT
),
ofpp.OFPActionOutput(port=99),
ofpp.OFPActionPopVlan()
]
),
ofpp.OFPInstructionGotoTable(table_id=13)
],
match=ofpp.OFPMatch(
metadata=meta.mk_metadata(150, meta.LOCAL)
),
priority=1,
table_id=12
)
)
]
sendmsg.assert_has_calls(expected_calls)
def test_reclaim_tenant_physnet(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.reclaim_tenant_physnet(network_type="vlan", network=150,
segmentation_id=151, phys_port=99)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(in_port=99,
vlan_vid=151 | ofp.OFPVID_PRESENT),
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=0,
table_id=0)),
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(metadata=meta.mk_metadata(150)),
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=0,
table_id=12))
]
sendmsg.assert_has_calls(expected_calls)
def test_check_in_port_add_tunnel_port(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.check_in_port_add_tunnel_port(network_type="gre", port=99)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp,
instructions=[ofpp.OFPInstructionGotoTable(table_id=1)],
match=ofpp.OFPMatch(in_port=99), priority=1, table_id=0))
]
sendmsg.assert_has_calls(expected_calls)
def test_check_in_port_add_local_port(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.check_in_port_add_local_port(network=123, port=99)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp,
instructions=[
ofpp.OFPInstructionWriteMetadata(
metadata=meta.LOCAL | 123,
metadata_mask=meta.LOCAL | meta.NETWORK_MASK),
ofpp.OFPInstructionGotoTable(table_id=4)],
match=ofpp.OFPMatch(in_port=99), priority=1, table_id=0))
]
sendmsg.assert_has_calls(expected_calls)
def test_check_in_port_delete_port(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.check_in_port_delete_port(port=99)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(in_port=99), out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, priority=0, table_id=0))
]
sendmsg.assert_has_calls(expected_calls)
def test_local_flood_update(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.local_flood_update(network=1234, ports=[1, 2, 3],
flood_unicast=True)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp,
instructions=[ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS, [
ofpp.OFPActionOutput(port=1),
ofpp.OFPActionOutput(port=2),
ofpp.OFPActionOutput(port=3)])],
match=ofpp.OFPMatch(metadata=meta.mk_metadata(1234)),
priority=1, table_id=13)),
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE_STRICT,
match=ofpp.OFPMatch(
eth_dst=('01:00:00:00:00:00', '01:00:00:00:00:00'),
metadata=meta.mk_metadata(1234)),
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=1,
table_id=13))
]
sendmsg.assert_has_calls(expected_calls)
def test_local_flood_delete(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.local_flood_delete(network=1234)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(metadata=meta.mk_metadata(1234)),
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=0,
table_id=13))
]
sendmsg.assert_has_calls(expected_calls)
def test_local_out_add_port(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.local_out_add_port(network=1234, port=7,
mac='12:34:56:78:9a:bc')
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
[ofpp.OFPActionOutput(port=7)])],
match=ofpp.OFPMatch(eth_dst="12:34:56:78:9a:bc",
metadata=meta.mk_metadata(1234)), priority=1, table_id=8))
]
sendmsg.assert_has_calls(expected_calls)
def test_local_out_delete_port(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.local_out_delete_port(network=1234, mac='12:34:56:78:9a:bc')
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(eth_dst="12:34:56:78:9a:bc",
metadata=meta.mk_metadata(1234)), out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, priority=0, table_id=8))
]
sendmsg.assert_has_calls(expected_calls)
def test_arp_passthrough(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.arp_passthrough(network=1234, tpa='192.0.2.1')
(dp, ofp, ofpp) = br._get_dp()
arp = importutils.import_module('ryu.lib.packet.arp')
ether = importutils.import_module('ryu.ofproto.ether')
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, idle_timeout=5,
instructions=[ofpp.OFPInstructionGotoTable(table_id=7)],
match=ofpp.OFPMatch(arp_op=arp.ARP_REQUEST,
arp_tpa="192.0.2.1", eth_type=ether.ETH_TYPE_ARP,
metadata=meta.mk_metadata(1234)), priority=1, table_id=5))
]
sendmsg.assert_has_calls(expected_calls)

View File

@ -1,798 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 Fumihiko Kakuma <kakuma at valinux co jp>
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Based on test for openvswitch agent(test_ovs_neutron_agent.py).
#
# Copyright (c) 2012 OpenStack Foundation.
#
# 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.
import collections
import contextlib
import copy
import mock
import netaddr
from oslo_config import cfg
from oslo_utils import importutils
import testtools
from neutron.agent.linux import ovs_lib
from neutron.common import constants as n_const
from neutron.plugins.common import constants as p_const
from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc
from neutron.tests.unit.ofagent import ofa_test_base
NOTIFIER = ('neutron.plugins.ml2.rpc.AgentNotifierApi')
FLOODING_ENTRY = l2pop_rpc.PortInfo(*n_const.FLOODING_ENTRY)
def _mock_port(is_neutron=True, normalized_name=None):
p = mock.Mock()
p.is_neutron_port.return_value = is_neutron
if normalized_name:
p.normalized_port_name.return_value = normalized_name
return p
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))
def test_create_agent_config_map_fails_for_invalid_tunnel_config(self):
# An ip address is required for tunneling but there is no default,
# verify this for both gre and vxlan tunnels.
cfg.CONF.set_override('tunnel_types', [p_const.TYPE_GRE],
group='AGENT')
with testtools.ExpectedException(ValueError):
self.mod_agent.create_agent_config_map(cfg.CONF)
cfg.CONF.set_override('tunnel_types', [p_const.TYPE_VXLAN],
group='AGENT')
with testtools.ExpectedException(ValueError):
self.mod_agent.create_agent_config_map(cfg.CONF)
def test_create_agent_config_map_fails_no_local_ip(self):
# An ip address is required for tunneling but there is no default
cfg.CONF.set_override('tunnel_types', [p_const.TYPE_VXLAN],
group='AGENT')
with testtools.ExpectedException(ValueError):
self.mod_agent.create_agent_config_map(cfg.CONF)
def test_create_agent_config_map_fails_for_invalid_tunnel_type(self):
cfg.CONF.set_override('tunnel_types', ['foobar'], group='AGENT')
with testtools.ExpectedException(ValueError):
self.mod_agent.create_agent_config_map(cfg.CONF)
def test_create_agent_config_map_multiple_tunnel_types(self):
cfg.CONF.set_override('local_ip', '10.10.10.10', group='OVS')
cfg.CONF.set_override('tunnel_types', [p_const.TYPE_GRE,
p_const.TYPE_VXLAN], group='AGENT')
cfgmap = self.mod_agent.create_agent_config_map(cfg.CONF)
self.assertEqual(cfgmap['tunnel_types'],
[p_const.TYPE_GRE, p_const.TYPE_VXLAN])
class TestOFANeutronAgentBridge(ofa_test_base.OFAAgentTestBase):
def setUp(self):
super(TestOFANeutronAgentBridge, self).setUp()
self.br_name = 'bridge1'
self.ovs = self.mod_agent.Bridge(
self.br_name, self.ryuapp)
def test_find_datapath_id(self):
with mock.patch.object(self.ovs, 'get_datapath_id',
return_value='12345'):
self.ovs.find_datapath_id()
self.assertEqual(self.ovs.datapath_id, '12345')
def _fake_get_datapath(self, app, datapath_id):
if self.ovs.retry_count >= 2:
datapath = mock.Mock()
datapath.ofproto_parser = mock.Mock()
return datapath
self.ovs.retry_count += 1
return None
def test_get_datapath_normal(self):
self.ovs.retry_count = 0
with mock.patch.object(self.mod_agent.ryu_api, 'get_datapath',
new=self._fake_get_datapath):
self.ovs.datapath_id = '0x64'
self.ovs.get_datapath(retry_max=4)
self.assertEqual(self.ovs.retry_count, 2)
def test_get_datapath_retry_out_by_default_time(self):
cfg.CONF.set_override('get_datapath_retry_times', 3, group='AGENT')
with mock.patch.object(self.mod_agent.ryu_api, 'get_datapath',
return_value=None) as mock_get_datapath:
with testtools.ExpectedException(SystemExit):
self.ovs.datapath_id = '0x64'
self.ovs.get_datapath(retry_max=3)
self.assertEqual(mock_get_datapath.call_count, 3)
def test_get_datapath_retry_out_by_specified_time(self):
with mock.patch.object(self.mod_agent.ryu_api, 'get_datapath',
return_value=None) as mock_get_datapath:
with testtools.ExpectedException(SystemExit):
self.ovs.datapath_id = '0x64'
self.ovs.get_datapath(retry_max=2)
self.assertEqual(mock_get_datapath.call_count, 2)
def test_setup_ofp_default_par(self):
with contextlib.nested(
mock.patch.object(self.ovs, 'set_protocols'),
mock.patch.object(self.ovs, 'set_controller'),
mock.patch.object(self.ovs, 'find_datapath_id'),
mock.patch.object(self.ovs, 'get_datapath'),
) as (mock_set_protocols, mock_set_controller,
mock_find_datapath_id, mock_get_datapath):
self.ovs.setup_ofp()
mock_set_protocols.assert_called_with('OpenFlow13')
mock_set_controller.assert_called_with(['tcp:127.0.0.1:6633'])
mock_get_datapath.assert_called_with(
cfg.CONF.AGENT.get_datapath_retry_times)
self.assertEqual(mock_find_datapath_id.call_count, 1)
def test_setup_ofp_specify_par(self):
controller_names = ['tcp:192.168.10.10:1234', 'tcp:172.17.16.20:5555']
with contextlib.nested(
mock.patch.object(self.ovs, 'set_protocols'),
mock.patch.object(self.ovs, 'set_controller'),
mock.patch.object(self.ovs, 'find_datapath_id'),
mock.patch.object(self.ovs, 'get_datapath'),
) as (mock_set_protocols, mock_set_controller,
mock_find_datapath_id, mock_get_datapath):
self.ovs.setup_ofp(controller_names=controller_names,
protocols='OpenFlow133',
retry_max=11)
mock_set_protocols.assert_called_with('OpenFlow133')
mock_set_controller.assert_called_with(controller_names)
mock_get_datapath.assert_called_with(11)
self.assertEqual(mock_find_datapath_id.call_count, 1)
def test_setup_ofp_with_except(self):
with contextlib.nested(
mock.patch.object(self.ovs, 'set_protocols',
side_effect=RuntimeError),
mock.patch.object(self.ovs, 'set_controller'),
mock.patch.object(self.ovs, 'find_datapath_id'),
mock.patch.object(self.ovs, 'get_datapath'),
) as (mock_set_protocols, mock_set_controller,
mock_find_datapath_id, mock_get_datapath):
with testtools.ExpectedException(SystemExit):
self.ovs.setup_ofp()
class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
def setUp(self):
super(TestOFANeutronAgent, self).setUp()
notifier_p = mock.patch(NOTIFIER)
notifier_cls = notifier_p.start()
self.notifier = mock.Mock()
notifier_cls.return_value = self.notifier
kwargs = self.mod_agent.create_agent_config_map(cfg.CONF)
class MockFixedIntervalLoopingCall(object):
def __init__(self, f):
self.f = f
def start(self, interval=0):
self.f()
with contextlib.nested(
mock.patch.object(self.mod_agent.OFANeutronAgent,
'setup_integration_br',
return_value=mock.Mock()),
mock.patch.object(self.mod_agent.Bridge,
'get_local_port_mac',
return_value='00:00:00:00:00:01'),
mock.patch('neutron.agent.linux.utils.get_interface_mac',
return_value='00:00:00:00:00:01'),
mock.patch('neutron.openstack.common.loopingcall.'
'FixedIntervalLoopingCall',
new=MockFixedIntervalLoopingCall)):
self.agent = self.mod_agent.OFANeutronAgent(self.ryuapp, **kwargs)
self.agent.sg_agent = mock.Mock()
self.int_dp = self._mk_test_dp('int_br')
self.agent.int_br = self._mk_test_br('int_br')
self.agent.int_br.set_dp(self.int_dp)
self.agent.int_ofports['phys-net1'] = 666
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_scan_ports(self, port_set=None, registered_ports=None,
updated_ports=None, port_tags_dict=None):
port_tags_dict = port_tags_dict or {}
with contextlib.nested(
mock.patch.object(self.agent, '_get_ofport_names',
return_value=port_set),
mock.patch.object(self.agent.int_br, 'get_port_tag_dict',
return_value=port_tags_dict)
):
return self.agent.scan_ports(registered_ports, updated_ports)
def test_scan_ports_returns_current_only_for_unchanged_ports(self):
vif_port_set = set([1, 3])
registered_ports = set([1, 3])
expected = {'current': vif_port_set}
actual = self.mock_scan_ports(vif_port_set, registered_ports)
self.assertEqual(expected, actual)
def test_scan_ports_returns_port_changes(self):
vif_port_set = set([1, 3])
registered_ports = set([1, 2])
expected = dict(current=vif_port_set, added=set([3]), removed=set([2]))
actual = self.mock_scan_ports(vif_port_set, registered_ports)
self.assertEqual(expected, actual)
def _test_scan_ports_with_updated_ports(self, updated_ports):
vif_port_set = set([1, 3, 4])
registered_ports = set([1, 2, 4])
expected = dict(current=vif_port_set, added=set([3]),
removed=set([2]), updated=set([4]))
actual = self.mock_scan_ports(vif_port_set, registered_ports,
updated_ports)
self.assertEqual(expected, actual)
def test_scan_ports_finds_known_updated_ports(self):
self._test_scan_ports_with_updated_ports(set([4]))
def test_scan_ports_ignores_unknown_updated_ports(self):
# the port '5' was not seen on current ports. Hence it has either
# never been wired or already removed and should be ignored
self._test_scan_ports_with_updated_ports(set([4, 5]))
def test_scan_ports_ignores_updated_port_if_removed(self):
vif_port_set = set([1, 3])
registered_ports = set([1, 2])
updated_ports = set([1, 2])
expected = dict(current=vif_port_set, added=set([3]),
removed=set([2]), updated=set([1]))
actual = self.mock_scan_ports(vif_port_set, registered_ports,
updated_ports)
self.assertEqual(expected, actual)
def test_scan_ports_no_vif_changes_returns_updated_port_only(self):
vif_port_set = set([1, 2, 3])
registered_ports = set([1, 2, 3])
updated_ports = set([2])
expected = dict(current=vif_port_set, updated=set([2]))
actual = self.mock_scan_ports(vif_port_set, registered_ports,
updated_ports)
self.assertEqual(expected, actual)
def test_treat_devices_added_returns_true_for_missing_device(self):
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc, 'get_device_details',
side_effect=Exception()),
mock.patch.object(self.agent, '_get_ports',
return_value=[_mock_port(True, 'xxx')])):
self.assertTrue(self.agent.treat_devices_added_or_updated(['xxx']))
def _mock_treat_devices_added_updated(self, details, port, all_ports,
func_name):
"""Mock treat devices added or updated.
:param details: the details to return for the device
:param port: port name to process
:param all_ports: the port that _get_ports return
:param func_name: the function that should be called
:returns: whether the named function was called
"""
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc, 'get_device_details',
return_value=details),
mock.patch.object(self.agent, '_get_ports',
return_value=all_ports),
mock.patch.object(self.agent.plugin_rpc, 'update_device_up'),
mock.patch.object(self.agent.plugin_rpc, 'update_device_down'),
mock.patch.object(self.agent, func_name)
) as (get_dev_fn, _get_ports, upd_dev_up, upd_dev_down, func):
self.assertFalse(self.agent.treat_devices_added_or_updated([port]))
_get_ports.assert_called_once_with(self.agent.int_br)
return func.called
def test_treat_devices_added_updated_ignores_invalid_ofport(self):
port_name = 'hoge'
p1 = _mock_port(True, port_name)
p1.ofport = -1
self.assertFalse(self._mock_treat_devices_added_updated(
mock.MagicMock(), port_name, [p1], 'port_dead'))
def test_treat_devices_added_updated_marks_unknown_port_as_dead(self):
port_name = 'hoge'
p1 = _mock_port(True, port_name)
p1.ofport = 1
self.assertTrue(self._mock_treat_devices_added_updated(
mock.MagicMock(), port_name, [p1], 'port_dead'))
def test_treat_devices_added_does_not_process_missing_port(self):
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc, 'get_device_details'),
mock.patch.object(self.agent.int_br, 'get_vif_port_by_id',
return_value=None)
) as (get_dev_fn, get_vif_func):
self.assertFalse(get_dev_fn.called)
def test_treat_devices_added_updated_updates_known_port(self):
port_name = 'tapd3315981-0b'
p1 = _mock_port(False)
p2 = _mock_port(True, port_name)
ports = [p1, p2]
details = mock.MagicMock()
details.__contains__.side_effect = lambda x: True
self.assertTrue(self._mock_treat_devices_added_updated(
details, port_name, ports, 'treat_vif_port'))
def test_treat_devices_added_updated_put_port_down(self):
fake_details_dict = {'admin_state_up': False,
'port_id': 'xxx',
'device': 'xxx',
'network_id': 'yyy',
'physical_network': 'foo',
'segmentation_id': 'bar',
'network_type': 'baz'}
with contextlib.nested(
mock.patch.object(self.agent.plugin_rpc, 'get_device_details',
return_value=fake_details_dict),
mock.patch.object(self.agent, '_get_ports',
return_value=[_mock_port(True, 'xxx')]),
mock.patch.object(self.agent.plugin_rpc, 'update_device_up'),
mock.patch.object(self.agent.plugin_rpc, 'update_device_down'),
mock.patch.object(self.agent, 'treat_vif_port')
) as (get_dev_fn, _get_ports, upd_dev_up,
upd_dev_down, treat_vif_port):
self.assertFalse(self.agent.treat_devices_added_or_updated(
['xxx']))
self.assertTrue(treat_vif_port.called)
self.assertTrue(upd_dev_down.called)
_get_ports.assert_called_once_with(self.agent.int_br)
def test_treat_devices_removed_returns_true_for_missing_device(self):
with mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
side_effect=Exception()):
self.assertTrue(self.agent.treat_devices_removed([{}]))
def _mock_treat_devices_removed(self, port_exists):
details = dict(exists=port_exists)
with mock.patch.object(self.agent.plugin_rpc, 'update_device_down',
return_value=details):
with mock.patch.object(self.agent, 'port_unbound') as port_unbound:
self.assertFalse(self.agent.treat_devices_removed([{}]))
self.assertTrue(port_unbound.called)
def test_treat_devices_removed_unbinds_port(self):
self._mock_treat_devices_removed(True)
def test_treat_devices_removed_ignores_missing_port(self):
self._mock_treat_devices_removed(False)
def _test_process_network_ports(self, port_info):
with contextlib.nested(
mock.patch.object(self.agent.sg_agent, "setup_port_filters"),
mock.patch.object(self.agent, "treat_devices_added_or_updated",
return_value=False),
mock.patch.object(self.agent, "treat_devices_removed",
return_value=False)
) as (setup_port_filters, device_added_updated, device_removed):
self.assertFalse(self.agent.process_network_ports(port_info))
setup_port_filters.assert_called_once_with(
port_info['added'], port_info.get('updated', set()))
device_added_updated.assert_called_once_with(
port_info['added'] | port_info.get('updated', set()))
device_removed.assert_called_once_with(port_info['removed'])
def test_process_network_ports(self):
self._test_process_network_ports(
{'current': set(['tap0']),
'removed': set(['eth0']),
'added': set(['eth1'])})
def test_process_network_port_with_updated_ports(self):
self._test_process_network_ports(
{'current': set(['tap0', 'tap1']),
'updated': set(['tap1', 'eth1']),
'removed': set(['eth0']),
'added': set(['eth1'])})
def test_report_state(self):
with mock.patch.object(self.agent.state_rpc,
"report_state") as report_st:
self.agent.int_br_device_count = 5
self.agent._report_state()
report_st.assert_called_with(self.agent.context,
self.agent.agent_state)
self.assertNotIn("start_flag", self.agent.agent_state)
self.assertEqual(
self.agent.agent_state["configurations"]["devices"],
self.agent.int_br_device_count
)
def test_port_update(self):
port = {"id": "b1981919-f516-11e3-a8f4-08606e7f74e7",
"network_id": "124",
"admin_state_up": False}
self.agent.port_update("unused_context",
port=port,
network_type="vlan",
segmentation_id="1",
physical_network="physnet")
self.assertEqual(set(['tapb1981919-f5']), self.agent.updated_ports)
def test_setup_physical_interfaces(self):
with mock.patch.object(self.agent.int_br, "add_port") as add_port_fn:
add_port_fn.return_value = "111"
self.agent.setup_physical_interfaces({"physnet1": "eth1"})
add_port_fn.assert_called_once_with("eth1")
self.assertEqual(111, self.agent.int_ofports["physnet1"])
def test_port_unbound(self):
with contextlib.nested(
mock.patch.object(self.agent, "reclaim_local_vlan"),
mock.patch.object(self.agent, "get_net_uuid",
return_value="netuid12345"),
) as (reclvl_fn, _):
self.agent.enable_tunneling = True
lvm = mock.Mock()
lvm.network_type = "gre"
lvm.vif_ports = {"vif1": mock.Mock()}
self.agent.local_vlan_map["netuid12345"] = lvm
self.agent.port_unbound("vif1")
self.assertTrue(reclvl_fn.called)
def _prepare_l2_pop_ofports(self, network_type=None):
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)
if network_type is None:
network_type = self.tunnel_type
lvm1 = mock.Mock()
lvm1.network_type = network_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 = network_type
lvm2.vlan = self.lvms[1].vlan
lvm2.segmentation_id = self.lvms[1].segid
lvm2.tun_ofports = set([1, 2])
self.agent.tunnel_types = [self.tunnel_type]
self.agent.local_vlan_map = {self.lvms[0].net: lvm1,
self.lvms[1].net: lvm2}
self.agent.tun_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, '_setup_tunnel_port'),
mock.patch.object(self.agent, 'cleanup_tunnel_port')
) as (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)
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':
[l2pop_rpc.PortInfo('mac', 'ip'),
FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent.ryuapp, "add_arp_table_entry"),
mock.patch.object(self.agent.ryuapp, "del_arp_table_entry"),
) as (add_fn, del_fn):
self.agent.fdb_add(None, copy.deepcopy(fdb_entry))
add_fn.assert_called_once_with(12, 'ip', 'mac')
self.assertFalse(del_fn.called)
self.agent.fdb_remove(None, fdb_entry)
add_fn.assert_called_once_with(12, 'ip', 'mac')
del_fn.assert_called_once_with(12, 'ip')
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:
[l2pop_rpc.PortInfo('mac', 'ip'),
FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent, '_setup_tunnel_port'),
mock.patch.object(self.agent.int_br, 'install_tunnel_output'),
mock.patch.object(self.agent.int_br, 'delete_tunnel_output'),
) as (add_tun_fn, install_fn, delete_fn):
add_tun_fn.return_value = 2
self.agent.fdb_add(None, fdb_entry)
self.assertEqual(2, install_fn.call_count)
expected_calls = [
mock.call(7, 11, 21, set([2]), eth_dst='mac', goto_next=False),
mock.call(10, 11, 21, set([1, 2]), goto_next=True)
]
install_fn.assert_has_calls(expected_calls)
self.assertFalse(delete_fn.called)
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:
[l2pop_rpc.PortInfo('mac', 'ip'),
FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent.int_br, 'install_tunnel_output'),
mock.patch.object(self.agent.int_br, 'delete_tunnel_output'),
) as (install_fn, delete_fn):
self.agent.fdb_remove(None, fdb_entry)
install_fn.assert_called_once_with(10, 12, 22, set([1]),
goto_next=True)
delete_fn.assert_called_once_with(7, 12, eth_dst='mac')
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: [l2pop_rpc.PortInfo('mac',
'ip')]}}}
with mock.patch.object(self.agent, '_setup_tunnel_port') as 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] = [
l2pop_rpc.PortInfo('mac', 'ip')]
self.agent.fdb_add(None, fdb_entry)
add_tun_fn.assert_called_with(
self.agent.int_br, 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: [FLOODING_ENTRY]}}}
with mock.patch.object(self.agent.int_br,
'delete_port') as del_port_fn:
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: [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac1', 'ip1')],
self.lvms[1].ip: [
l2pop_rpc.PortInfo('mac2', 'ip2')],
'192.0.2.1': [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac3', 'ip3')]}}}
with mock.patch.object(self.agent,
'setup_tunnel_port') as setup_tun_fn:
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,
any_order=True)
setup_tun_fn.assert_called_once_with(self.agent.int_br,
'192.0.2.1', 'gre')
def _test_add_arp_table_entry_non_tunnel(self, network_type):
self._prepare_l2_pop_ofports(network_type=network_type)
fdb_entry = {self.lvms[0].net:
{'network_type': network_type,
'segment_id': 'tun1',
'ports': {self.lvms[0].ip: [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac1', 'ip1')],
self.lvms[1].ip: [
l2pop_rpc.PortInfo('mac2', 'ip2')],
'192.0.2.1': [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac3', 'ip3')]}}}
with mock.patch.object(self.agent,
'setup_tunnel_port') as setup_tun_fn:
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,
any_order=True)
self.assertFalse(setup_tun_fn.called)
def test_add_arp_table_entry_vlan(self):
self._test_add_arp_table_entry_non_tunnel('vlan')
def test_add_arp_table_entry_flat(self):
self._test_add_arp_table_entry_non_tunnel('flat')
def test_add_arp_table_entry_local(self):
self._test_add_arp_table_entry_non_tunnel('local')
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: [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac1', 'ip1')],
self.lvms[1].ip: [
l2pop_rpc.PortInfo('mac2', 'ip2')],
'192.0.2.1': [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac3', 'ip3')]}}}
with mock.patch.object(self.agent,
'cleanup_tunnel_port') as cleanup_tun_fn:
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,
any_order=True)
cleanup_tun_fn.assert_called_once_with(self.agent.int_br, 1, 'gre')
def _test_del_arp_table_entry_non_tunnel(self, network_type):
self._prepare_l2_pop_ofports(network_type=network_type)
fdb_entry = {self.lvms[0].net:
{'network_type': network_type,
'segment_id': 'tun1',
'ports': {self.lvms[0].ip: [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac1', 'ip1')],
self.lvms[1].ip: [
l2pop_rpc.PortInfo('mac2', 'ip2')],
'192.0.2.1': [
FLOODING_ENTRY,
l2pop_rpc.PortInfo('mac3', 'ip3')]}}}
with mock.patch.object(self.agent,
'cleanup_tunnel_port') as cleanup_tun_fn:
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,
any_order=True)
self.assertFalse(cleanup_tun_fn.called)
def test_del_arp_table_entry_vlan(self):
self._test_del_arp_table_entry_non_tunnel('vlan')
def test_del_arp_table_entry_flat(self):
self._test_del_arp_table_entry_non_tunnel('flat')
def test_del_arp_table_entry_local(self):
self._test_del_arp_table_entry_non_tunnel('local')
def test_recl_lv_port_to_preserve(self):
self._prepare_l2_pop_ofports()
self.agent.enable_tunneling = True
with mock.patch.object(
self.agent.int_br, 'delete_port'
) as del_port_fn:
self.agent.reclaim_local_vlan(self.lvms[0].net)
self.assertFalse(del_port_fn.called)
def test_recl_lv_port_to_remove(self):
self._prepare_l2_pop_ofports()
self.agent.enable_tunneling = True
with mock.patch.object(self.agent.int_br,
'delete_port') as del_port_fn:
self.agent.reclaim_local_vlan(self.lvms[1].net)
del_port_fn.assert_called_once_with(self.tun_name2)
def test__setup_tunnel_port_error_negative(self):
with contextlib.nested(
mock.patch.object(self.agent.int_br, 'add_tunnel_port',
return_value=ovs_lib.INVALID_OFPORT),
mock.patch.object(self.mod_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_error_fn):
ofport = self.agent._setup_tunnel_port(
self.agent.int_br, '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,
self.agent.vxlan_udp_port, self.agent.dont_fragment)
log_error_fn.assert_called_once_with(
_("Failed to set-up %(type)s tunnel port to %(ip)s"),
{'type': p_const.TYPE_GRE, 'ip': 'remote_ip'})
self.assertEqual(ofport, 0)
def test_setup_tunnel_port_returns_zero_for_failed_port_add(self):
with mock.patch.object(self.agent.int_br, 'add_tunnel_port',
return_value=ovs_lib.INVALID_OFPORT):
result = self.agent._setup_tunnel_port(self.agent.int_br, 'gre-1',
'remote_ip',
p_const.TYPE_GRE)
self.assertEqual(0, result)
def test_tunnel_sync(self):
self.agent.local_ip = 'agent_ip'
self.agent.context = 'fake_context'
self.agent.tunnel_types = ['vxlan']
self.agent.host = cfg.CONF.host
with mock.patch.object(
self.agent.plugin_rpc, 'tunnel_sync'
) as tunnel_sync_rpc_fn:
self.agent.tunnel_sync()
tunnel_sync_rpc_fn.assert_called_once_with(
self.agent.context,
self.agent.local_ip,
self.agent.tunnel_types[0],
self.agent.host)
def test__get_ports(self):
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
reply = [ofpp.OFPPortDescStatsReply(body=[ofpp.OFPPort(name='hoge',
port_no=8)])]
sendmsg = mock.Mock(return_value=reply)
self.mod_agent.ryu_api.send_msg = sendmsg
result = self.agent._get_ports(self.agent.int_br)
result = list(result) # convert generator to list.
self.assertEqual(1, len(result))
self.assertEqual('hoge', result[0].port_name)
self.assertEqual(8, result[0].ofport)
expected_msg = ofpp.OFPPortDescStatsRequest(
datapath=self.agent.int_br.datapath)
sendmsg.assert_has_calls([mock.call(app=self.agent.ryuapp,
msg=expected_msg, reply_cls=ofpp.OFPPortDescStatsReply,
reply_multi=True)])
def test__get_ofport_names(self):
names = ['p111', 'p222', 'p333']
ps = [_mock_port(True, x) for x in names]
with mock.patch.object(self.agent, '_get_ports',
return_value=ps) as _get_ports:
result = self.agent._get_ofport_names('hoge')
_get_ports.assert_called_once_with('hoge')
self.assertEqual(set(names), result)

View File

@ -1,54 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from neutron.common import constants as n_const
from neutron.plugins.ofagent.agent import ports
from neutron.tests import base
class TestOFAgentPorts(base.BaseTestCase):
def test_port(self):
name = 'foo03b9a237-0b'
p1 = ports.Port(port_name=name, ofport=999)
ryu_ofp_port = mock.Mock(port_no=999)
ryu_ofp_port.name = name
p2 = ports.Port.from_ofp_port(ofp_port=ryu_ofp_port)
self.assertEqual(p1.port_name, p2.port_name)
self.assertEqual(p1.ofport, p2.ofport)
self.assertFalse(p1.is_neutron_port())
self.assertFalse(p2.is_neutron_port())
def test_neutron_port(self):
for pref in ['qvo', 'qr-', 'qg-', n_const.TAP_DEVICE_PREFIX]:
name = pref + '03b9a237-0b'
p1 = ports.Port(port_name=name, ofport=999)
ryu_ofp_port = mock.Mock(port_no=999)
ryu_ofp_port.name = name
p2 = ports.Port.from_ofp_port(ofp_port=ryu_ofp_port)
self.assertEqual(p1.port_name, p2.port_name)
self.assertEqual(p1.ofport, p2.ofport)
self.assertTrue(p1.is_neutron_port())
self.assertTrue(p2.is_neutron_port())
self.assertTrue('tap03b9a237-0b', p1.normalized_port_name())
self.assertTrue('tap03b9a237-0b', p2.normalized_port_name())
def test_get_normalized_port_name(self):
self.assertEqual('tap03b9a237-0b',
ports.get_normalized_port_name(
'03b9a237-0b1b-11e4-b537-08606e7f74e7'))

View File

@ -1,82 +0,0 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
from oslo_utils import importutils
from neutron.tests.unit.ofagent import ofa_test_base
class TestOFAgentFlows(ofa_test_base.OFATestBase):
_MOD = 'neutron.plugins.ofagent.agent.ofswitch'
def setUp(self):
super(TestOFAgentFlows, self).setUp()
self.mod = importutils.import_module(self._MOD)
self.br = self.mod.OpenFlowSwitch()
self.br.set_dp(self._mk_test_dp("dp"))
def test_delete_flows(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.delete_flows()
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(), out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, priority=0, table_id=ofp.OFPTT_ALL)),
]
sendmsg.assert_has_calls(expected_calls)
def test_install_default_drop(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.install_default_drop(table_id=98)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, priority=0, table_id=98)),
]
sendmsg.assert_has_calls(expected_calls)
def test_install_default_goto(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.install_default_goto(table_id=98, dest_table_id=150)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=150)],
priority=0, table_id=98)),
]
sendmsg.assert_has_calls(expected_calls)
def test_install_default_goto_next(self):
br = self.br
with mock.patch.object(br, '_send_msg') as sendmsg:
br.install_default_goto_next(table_id=100)
(dp, ofp, ofpp) = br._get_dp()
call = mock.call
expected_calls = [
call(ofpp.OFPFlowMod(dp, instructions=[
ofpp.OFPInstructionGotoTable(table_id=101)],
priority=0, table_id=100)),
]
sendmsg.assert_has_calls(expected_calls)

View File

@ -38,7 +38,6 @@ data_files =
etc/neutron/rootwrap.d/l3.filters
etc/neutron/rootwrap.d/linuxbridge-plugin.filters
etc/neutron/rootwrap.d/nec-plugin.filters
etc/neutron/rootwrap.d/ofagent.filters
etc/neutron/rootwrap.d/openvswitch-plugin.filters
etc/init.d = etc/init.d/neutron-server
etc/neutron/plugins/bigswitch =
@ -113,7 +112,6 @@ console_scripts =
neutron-rootwrap = oslo_rootwrap.cmd:main
neutron-usage-audit = neutron.cmd.usage_audit:main
neutron-metering-agent = neutron.cmd.eventlet.services.metering_agent:main
neutron-ofagent-agent = neutron.plugins.ofagent.agent.main:main
neutron-sriov-nic-agent = neutron.plugins.sriovnicagent.sriov_nic_agent:main
neutron-sanity-check = neutron.cmd.sanity_check:main
neutron.core_plugins =
@ -178,7 +176,7 @@ neutron.ml2.mechanism_drivers =
cisco_apic = neutron.plugins.ml2.drivers.cisco.apic.mechanism_apic:APICMechanismDriver
l2population = neutron.plugins.ml2.drivers.l2pop.mech_driver:L2populationMechanismDriver
bigswitch = neutron.plugins.ml2.drivers.mech_bigswitch.driver:BigSwitchMechanismDriver
ofagent = neutron.plugins.ml2.drivers.mech_ofagent:OfagentMechanismDriver
ofagent = networking_ofagent.plugins.ml2.drivers.mech_ofagent:OfagentMechanismDriver
mlnx = neutron.plugins.ml2.drivers.mlnx.mech_mlnx:MlnxMechanismDriver
brocade = neutron.plugins.ml2.drivers.brocade.mechanism_brocade:BrocadeMechanism
fslsdn = neutron.plugins.ml2.drivers.freescale.mechanism_fslsdn:FslsdnMechanismDriver