ofagent: merge br-tun into br-int

Highlights:
- Deprecate br-tun.
- Reduce the use of OFPP_NORMAL action.  Use mac address info
  obtained from get_device_details and l2-pop to reduce flooding.
- Use OpenFlow metadata instead of "internal" VLANs.
  Now tenant networks are VLAN transparent.

Implements: blueprint ofagent-port-monitor
Implements: blueprint ofagent-merge-bridges
Change-Id: I21ee1c6d141863182b487e10c7bfe911b1a472ab
This commit is contained in:
YAMAMOTO Takashi 2014-04-18 15:48:20 +09:00
parent 0422f1aab7
commit 8ac9e0b2e0
15 changed files with 1293 additions and 995 deletions

View File

@ -7,6 +7,21 @@ https://github.com/osrg/ryu/wiki/OpenStack
# -- Notes for updating from Icehouce
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)

View File

@ -20,10 +20,9 @@ from ryu.lib.packet import arp
from ryu.lib.packet import ethernet
from ryu.lib.packet import packet
from ryu.lib.packet import vlan
from ryu.ofproto import ether
from neutron.openstack.common import log as logging
from neutron.plugins.openvswitch.common import constants
import neutron.plugins.ofagent.agent.metadata as meta
LOG = logging.getLogger(__name__)
@ -45,6 +44,10 @@ class ArpLib(object):
"""
self.ryuapp = ryuapp
self._arp_tbl = {}
self.br = None
def set_bridge(self, br):
self.br = br
def _send_arp_reply(self, datapath, port, pkt):
LOG.debug("packet-out %s", pkt)
@ -60,21 +63,6 @@ class ArpLib(object):
data=data)
ryu_api.send_msg(self.ryuapp, out)
def _add_flow_to_avoid_unknown_packet(self, datapath, match):
LOG.debug("add flow to avoid an unknown packet from packet-in")
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
instructions = [ofpp.OFPInstructionGotoTable(
table_id=constants.FLOOD_TO_TUN)]
out = ofpp.OFPFlowMod(datapath,
table_id=constants.PATCH_LV_TO_TUN,
command=ofp.OFPFC_ADD,
idle_timeout=5,
priority=20,
match=match,
instructions=instructions)
ryu_api.send_msg(self.ryuapp, out)
def _send_unknown_packet(self, msg, in_port, out_port):
LOG.debug("unknown packet-out in-port %(in_port)s "
"out-port %(out_port)s msg %(msg)s",
@ -109,10 +97,11 @@ class ArpLib(object):
pkt.add_protocol(ethernet.ethernet(ethertype=pkt_ethernet.ethertype,
dst=pkt_ethernet.src,
src=hw_addr))
pkt.add_protocol(vlan.vlan(cfi=pkt_vlan.cfi,
ethertype=pkt_vlan.ethertype,
pcp=pkt_vlan.pcp,
vid=pkt_vlan.vid))
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,
@ -146,31 +135,33 @@ class ArpLib(object):
msg = ev.msg
LOG.debug("packet-in msg %s", msg)
datapath = msg.datapath
if self.br is None:
LOG.info(_("No bridge is set"))
return
if self.br.datapath.id != datapath.id:
LOG.info(_("Unknown bridge %(dpid)s ours %(ours)s"),
{"dpid": datapath.id, "ours": self.br.datapath.id})
return
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
port = msg.match['in_port']
metadata = msg.match.get('metadata')
pkt = packet.Packet(msg.data)
LOG.info(_("packet-in dpid %(dpid)s in_port %(port)s pkt %(pkt)s"),
{'dpid': dpid_lib.dpid_to_str(datapath.id),
'port': port, 'pkt': pkt})
pkt_vlan = None
pkt_arp = None
if metadata is None:
LOG.info(_("drop non tenant packet"))
return
network = metadata & meta.NETWORK_MASK
pkt_ethernet = pkt.get_protocol(ethernet.ethernet)
if not pkt_ethernet:
LOG.info(_("non-ethernet packet"))
else:
pkt_vlan = pkt.get_protocol(vlan.vlan)
if not pkt_vlan:
LOG.info(_("non-vlan packet"))
if pkt_vlan:
network = pkt_vlan.vid
pkt_arp = pkt.get_protocol(arp.arp)
if not pkt_arp:
LOG.info(_("drop non-arp packet"))
return
else:
# drop an unknown packet.
LOG.info(_("drop unknown packet"))
LOG.info(_("drop non-ethernet packet"))
return
pkt_vlan = pkt.get_protocol(vlan.vlan)
pkt_arp = pkt.get_protocol(arp.arp)
if not pkt_arp:
LOG.info(_("drop non-arp packet"))
return
arptbl = self._arp_tbl.get(network)
@ -180,11 +171,8 @@ class ArpLib(object):
return
else:
LOG.info(_("unknown network %s"), network)
# add a flow to skip a packet-in to a controller.
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
vlan_vid=network | ofp.OFPVID_PRESENT,
arp_op=arp.ARP_REQUEST,
arp_tpa=pkt_arp.dst_ip)
self._add_flow_to_avoid_unknown_packet(datapath, match)
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

@ -0,0 +1,19 @@
# 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

@ -0,0 +1,407 @@
# 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

@ -0,0 +1,26 @@
# 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,4 +1,5 @@
# Copyright (C) 2014 VA Linux Systems Japan K.K.
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
# Based on openvswitch agent.
#
# Copyright 2011 VMware, Inc.
@ -26,8 +27,6 @@ from ryu.base import app_manager
from ryu.controller import handler
from ryu.controller import ofp_event
from ryu.lib import hub
from ryu.lib.packet import arp
from ryu.ofproto import ether
from ryu.ofproto import ofproto_v1_3 as ryu_ofp13
from neutron.agent import l2population_rpc
@ -45,22 +44,23 @@ from neutron.openstack.common import log as logging
from neutron.openstack.common import loopingcall
from neutron.plugins.common import constants as p_const
from neutron.plugins.ofagent.agent import arp_lib
from neutron.plugins.ofagent.agent import constants as ofa_const
from neutron.plugins.ofagent.agent import flows
from neutron.plugins.ofagent.agent import ports
from neutron.plugins.ofagent.agent import tables
from neutron.plugins.ofagent.common import config # noqa
from neutron.plugins.openvswitch.common import constants
LOG = logging.getLogger(__name__)
# A placeholder for dead vlans.
DEAD_VLAN_TAG = str(n_const.MAX_VLAN_TAG + 1)
# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
# attributes set).
class LocalVLANMapping:
def __init__(self, vlan, network_type, physical_network, segmentation_id,
vif_ports=None):
assert(isinstance(vlan, (int, long)))
if vif_ports is None:
vif_ports = {}
self.vlan = vlan
@ -77,13 +77,13 @@ class LocalVLANMapping:
self.segmentation_id))
class OVSBridge(ovs_lib.OVSBridge):
class Bridge(flows.OFAgentIntegrationBridge, ovs_lib.OVSBridge):
def __init__(self, br_name, root_helper, ryuapp):
super(OVSBridge, self).__init__(br_name, root_helper)
super(Bridge, self).__init__(br_name, root_helper)
self.datapath_id = None
self.datapath = None
self.ofparser = None
self.ryuapp = ryuapp
self.set_app(ryuapp)
def find_datapath_id(self):
self.datapath_id = self.get_datapath_id()
@ -98,7 +98,7 @@ class OVSBridge(ovs_lib.OVSBridge):
LOG.error(_('Agent terminated!: Failed to get a datapath.'))
raise SystemExit(1)
time.sleep(1)
self.ofparser = self.datapath.ofproto_parser
self.set_dp(self.datapath)
def setup_ofp(self, controller_names=None,
protocols='OpenFlow13',
@ -162,6 +162,7 @@ class OFANeutronAgentRyuApp(app_manager.RyuApp):
cfg.CONF.set_default('ip_lib_force_root', True)
agent = OFANeutronAgent(ryuapp, **agent_config)
self.arplib.set_bridge(agent.int_br)
# Start everything.
LOG.info(_("Agent initialized successfully, now running... "))
@ -196,7 +197,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
# 1.1 Support Security Group RPC
RPC_API_VERSION = '1.1'
def __init__(self, ryuapp, integ_br, tun_br, local_ip,
def __init__(self, ryuapp, integ_br, local_ip,
bridge_mappings, root_helper,
polling_interval, tunnel_types=None,
veth_mtu=None):
@ -204,7 +205,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
:param ryuapp: object of the ryu app.
:param integ_br: name of the integration bridge.
:param tun_br: name of the tunnel bridge.
:param local_ip: local IP address of this hypervisor.
:param bridge_mappings: mappings from physical network name to bridge.
:param root_helper: utility to use when running shell cmds.
@ -218,8 +218,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
self.ryuapp = ryuapp
self.veth_mtu = veth_mtu
self.root_helper = root_helper
self.available_local_vlans = set(xrange(n_const.MIN_VLAN_TAG,
n_const.MAX_VLAN_TAG))
# TODO(yamamoto): Remove this VLAN leftover
self.available_local_vlans = set(xrange(ofa_const.LOCAL_VLAN_MIN,
ofa_const.LOCAL_VLAN_MAX))
self.tunnel_types = tunnel_types or []
self.agent_state = {
'binary': 'neutron-ofa-agent',
@ -235,16 +236,16 @@ class OFANeutronAgent(n_rpc.RpcCallback,
# Keep track of int_br's device count for use by _report_state()
self.int_br_device_count = 0
self.int_br = OVSBridge(integ_br, self.root_helper, self.ryuapp)
self.int_br = Bridge(integ_br, self.root_helper, self.ryuapp)
# Stores port update notifications for processing in main loop
self.updated_ports = set()
self.setup_rpc()
self.setup_integration_br()
self.setup_physical_bridges(bridge_mappings)
self.local_vlan_map = {}
self.tun_br_ofports = {p_const.TYPE_GRE: {},
p_const.TYPE_VXLAN: {}}
self.tun_ofports = {}
for t in tables.TUNNEL_TYPES:
self.tun_ofports[t] = {}
self.polling_interval = polling_interval
self.enable_tunneling = bool(self.tunnel_types)
@ -252,8 +253,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
self.tunnel_count = 0
self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port
self.dont_fragment = cfg.CONF.AGENT.dont_fragment
if self.enable_tunneling:
self.setup_tunnel_br(tun_br)
# Security group agent support
self.sg_agent = OFASecurityGroupAgent(self.context,
@ -281,10 +280,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
LOG.warn(_("Unable to create tunnel port. Invalid remote IP: %s"),
ip_address)
def ryu_send_msg(self, msg):
result = ryu_api.send_msg(self.ryuapp, msg)
LOG.info(_("ryu send_msg() result: %s"), result)
def setup_rpc(self):
mac = self.int_br.get_local_port_mac()
self.agent_id = '%s%s' % ('ovs', (mac.replace(":", "")))
@ -346,8 +341,8 @@ class OFANeutronAgent(n_rpc.RpcCallback,
self.local_vlan_map):
agent_ports.pop(self.local_ip, None)
if len(agent_ports):
self.fdb_add_tun(context, self.tun_br, lvm, agent_ports,
self.tun_br_ofports)
self.fdb_add_tun(context, self.int_br, lvm, agent_ports,
self.tun_ofports)
def fdb_remove(self, context, fdb_entries):
LOG.debug("fdb_remove received")
@ -355,86 +350,40 @@ class OFANeutronAgent(n_rpc.RpcCallback,
self.local_vlan_map):
agent_ports.pop(self.local_ip, None)
if len(agent_ports):
self.fdb_remove_tun(context, self.tun_br, lvm, agent_ports,
self.tun_br_ofports)
def _add_fdb_flooding_flow(self, br, lvm):
datapath = br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
match = ofpp.OFPMatch(
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
actions = [ofpp.OFPActionPopVlan(),
ofpp.OFPActionSetField(
tunnel_id=int(lvm.segmentation_id))]
for tun_ofport in lvm.tun_ofports:
actions.append(ofpp.OFPActionOutput(int(tun_ofport), 0))
instructions = [ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS, actions)]
msg = ofpp.OFPFlowMod(datapath,
table_id=constants.FLOOD_TO_TUN,
command=ofp.OFPFC_ADD,
priority=1,
match=match, instructions=instructions)
self.ryu_send_msg(msg)
self.fdb_remove_tun(context, self.int_br, lvm, agent_ports,
self.tun_ofports)
def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
datapath = br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
if port_info == n_const.FLOODING_ENTRY:
lvm.tun_ofports.add(ofport)
self._add_fdb_flooding_flow(br, lvm)
br.install_tunnel_output(
tables.TUNNEL_FLOOD[lvm.network_type],
lvm.vlan, lvm.segmentation_id,
lvm.tun_ofports, goto_next=True)
else:
self.ryuapp.add_arp_table_entry(
lvm.vlan, port_info[1], port_info[0])
match = ofpp.OFPMatch(
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
eth_dst=port_info[0])
actions = [ofpp.OFPActionPopVlan(),
ofpp.OFPActionSetField(
tunnel_id=int(lvm.segmentation_id)),
ofpp.OFPActionOutput(int(ofport), 0)]
instructions = [ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS, actions)]
msg = ofpp.OFPFlowMod(datapath,
table_id=constants.UCAST_TO_TUN,
command=ofp.OFPFC_ADD,
priority=2,
match=match, instructions=instructions)
self.ryu_send_msg(msg)
br.install_tunnel_output(
tables.TUNNEL_OUT,
lvm.vlan, lvm.segmentation_id,
set([ofport]), goto_next=False, eth_dst=port_info[0])
def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
datapath = br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
if port_info == n_const.FLOODING_ENTRY:
lvm.tun_ofports.remove(ofport)
if len(lvm.tun_ofports) > 0:
self._add_fdb_flooding_flow(br, lvm)
br.install_tunnel_output(
tables.TUNNEL_FLOOD[lvm.network_type],
lvm.vlan, lvm.segmentation_id,
lvm.tun_ofports, goto_next=True)
else:
# This local vlan doesn't require any more tunelling
match = ofpp.OFPMatch(
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
msg = ofpp.OFPFlowMod(datapath,
table_id=constants.FLOOD_TO_TUN,
command=ofp.OFPFC_DELETE,
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY,
match=match)
self.ryu_send_msg(msg)
br.delete_tunnel_output(
tables.TUNNEL_FLOOD[lvm.network_type],
lvm.vlan)
else:
self.ryuapp.del_arp_table_entry(lvm.vlan, port_info[1])
match = ofpp.OFPMatch(
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
eth_dst=port_info[0])
msg = ofpp.OFPFlowMod(datapath,
table_id=constants.UCAST_TO_TUN,
command=ofp.OFPFC_DELETE,
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY,
match=match)
self.ryu_send_msg(msg)
br.delete_tunnel_output(tables.TUNNEL_OUT,
lvm.vlan, eth_dst=port_info[0])
def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address,
ip_address):
@ -445,85 +394,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
def _fdb_chg_ip(self, context, fdb_entries):
LOG.debug("update chg_ip received")
self.fdb_chg_ip_tun(context, self.tun_br, fdb_entries, self.local_ip,
self.fdb_chg_ip_tun(context, self.int_br, fdb_entries, self.local_ip,
self.local_vlan_map)
def _provision_local_vlan_inbound_for_tunnel(self, lvid, network_type,
segmentation_id):
br = self.tun_br
match = br.ofparser.OFPMatch(
tunnel_id=int(segmentation_id))
actions = [
br.ofparser.OFPActionPushVlan(),
br.ofparser.OFPActionSetField(
vlan_vid=int(lvid) | ryu_ofp13.OFPVID_PRESENT)]
instructions = [
br.ofparser.OFPInstructionActions(
ryu_ofp13.OFPIT_APPLY_ACTIONS, actions),
br.ofparser.OFPInstructionGotoTable(
table_id=constants.LEARN_FROM_TUN)]
msg = br.ofparser.OFPFlowMod(
br.datapath,
table_id=constants.TUN_TABLE[network_type],
priority=1,
match=match,
instructions=instructions)
self.ryu_send_msg(msg)
def _local_vlan_for_tunnel(self, lvid, network_type, segmentation_id):
self._provision_local_vlan_inbound_for_tunnel(lvid, network_type,
segmentation_id)
def _provision_local_vlan_outbound(self, lvid, vlan_vid, physical_network):
br = self.phys_brs[physical_network]
datapath = br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
match = ofpp.OFPMatch(in_port=int(self.phys_ofports[physical_network]),
vlan_vid=int(lvid) | ofp.OFPVID_PRESENT)
if vlan_vid == ofp.OFPVID_NONE:
actions = [ofpp.OFPActionPopVlan()]
else:
actions = [ofpp.OFPActionSetField(vlan_vid=vlan_vid)]
actions += [ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0)]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
msg = ofpp.OFPFlowMod(datapath, priority=4, match=match,
instructions=instructions)
self.ryu_send_msg(msg)
def _provision_local_vlan_inbound(self, lvid, vlan_vid, physical_network):
datapath = self.int_br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
match = ofpp.OFPMatch(in_port=int(self.int_ofports[physical_network]),
vlan_vid=vlan_vid)
if vlan_vid == ofp.OFPVID_NONE:
actions = [ofpp.OFPActionPushVlan()]
else:
actions = []
actions += [
ofpp.OFPActionSetField(vlan_vid=int(lvid) | ofp.OFPVID_PRESENT),
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
]
msg = ofpp.OFPFlowMod(datapath, priority=3, match=match,
instructions=instructions)
self.ryu_send_msg(msg)
def _local_vlan_for_flat(self, lvid, physical_network):
vlan_vid = ryu_ofp13.OFPVID_NONE
self._provision_local_vlan_outbound(lvid, vlan_vid, physical_network)
self._provision_local_vlan_inbound(lvid, vlan_vid, physical_network)
def _local_vlan_for_vlan(self, lvid, physical_network, segmentation_id):
vlan_vid = int(segmentation_id) | ryu_ofp13.OFPVID_PRESENT
self._provision_local_vlan_outbound(lvid, vlan_vid, physical_network)
self._provision_local_vlan_inbound(lvid, vlan_vid, physical_network)
def provision_local_vlan(self, net_uuid, network_type, physical_network,
segmentation_id):
"""Provisions a local VLAN.
@ -548,31 +421,25 @@ class OFANeutronAgent(n_rpc.RpcCallback,
if network_type in constants.TUNNEL_NETWORK_TYPES:
if self.enable_tunneling:
self._local_vlan_for_tunnel(lvid, network_type,
segmentation_id)
self.int_br.provision_tenant_tunnel(network_type, lvid,
segmentation_id)
else:
LOG.error(_("Cannot provision %(network_type)s network for "
"net-id=%(net_uuid)s - tunneling disabled"),
{'network_type': network_type,
'net_uuid': net_uuid})
elif network_type == p_const.TYPE_FLAT:
if physical_network in self.phys_brs:
self._local_vlan_for_flat(lvid, physical_network)
elif network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT]:
if physical_network in self.int_ofports:
phys_port = self.int_ofports[physical_network]
self.int_br.provision_tenant_physnet(network_type, lvid,
segmentation_id,
phys_port)
else:
LOG.error(_("Cannot provision flat network for "
LOG.error(_("Cannot provision %(network_type)s network for "
"net-id=%(net_uuid)s - no bridge for "
"physical_network %(physical_network)s"),
{'net_uuid': net_uuid,
'physical_network': physical_network})
elif network_type == p_const.TYPE_VLAN:
if physical_network in self.phys_brs:
self._local_vlan_for_vlan(lvid, physical_network,
segmentation_id)
else:
LOG.error(_("Cannot provision VLAN network for "
"net-id=%(net_uuid)s - no bridge for "
"physical_network %(physical_network)s"),
{'net_uuid': net_uuid,
{'network_type': network_type,
'net_uuid': net_uuid,
'physical_network': physical_network})
elif network_type == p_const.TYPE_LOCAL:
# no flows needed for local networks
@ -583,35 +450,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
{'network_type': network_type,
'net_uuid': net_uuid})
def _reclaim_local_vlan_outbound(self, lvm):
br = self.phys_brs[lvm.physical_network]
datapath = br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
match = ofpp.OFPMatch(
in_port=int(self.phys_ofports[lvm.physical_network]),
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
msg = ofpp.OFPFlowMod(datapath, table_id=ofp.OFPTT_ALL,
command=ofp.OFPFC_DELETE, out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, match=match)
self.ryu_send_msg(msg)
def _reclaim_local_vlan_inbound(self, lvm):
datapath = self.int_br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
if lvm.network_type == p_const.TYPE_FLAT:
vid = ofp.OFPVID_NONE
else: # p_const.TYPE_VLAN
vid = lvm.segmentation_id | ofp.OFPVID_PRESENT
match = ofpp.OFPMatch(
in_port=int(self.int_ofports[lvm.physical_network]),
vlan_vid=vid)
msg = ofpp.OFPFlowMod(datapath, table_id=ofp.OFPTT_ALL,
command=ofp.OFPFC_DELETE, out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY, match=match)
self.ryu_send_msg(msg)
def reclaim_local_vlan(self, net_uuid):
"""Reclaim a local VLAN.
@ -630,34 +468,16 @@ class OFANeutronAgent(n_rpc.RpcCallback,
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
if self.enable_tunneling:
match = self.tun_br.ofparser.OFPMatch(
tunnel_id=int(lvm.segmentation_id))
msg = self.tun_br.ofparser.OFPFlowMod(
self.tun_br.datapath,
table_id=constants.TUN_TABLE[lvm.network_type],
command=ryu_ofp13.OFPFC_DELETE,
out_group=ryu_ofp13.OFPG_ANY,
out_port=ryu_ofp13.OFPP_ANY,
match=match)
self.ryu_send_msg(msg)
match = self.tun_br.ofparser.OFPMatch(
vlan_vid=int(lvm.vlan) | ryu_ofp13.OFPVID_PRESENT)
msg = self.tun_br.ofparser.OFPFlowMod(
self.tun_br.datapath,
table_id=ryu_ofp13.OFPTT_ALL,
command=ryu_ofp13.OFPFC_DELETE,
out_group=ryu_ofp13.OFPG_ANY,
out_port=ryu_ofp13.OFPP_ANY,
match=match)
self.ryu_send_msg(msg)
self.int_br.reclaim_tenant_tunnel(lvm.network_type, lvm.vlan,
lvm.segmentation_id)
# Try to remove tunnel ports if not used by other networks
for ofport in lvm.tun_ofports:
self.cleanup_tunnel_port(self.tun_br, ofport,
self.cleanup_tunnel_port(self.int_br, ofport,
lvm.network_type)
elif lvm.network_type in (p_const.TYPE_FLAT, p_const.TYPE_VLAN):
if lvm.physical_network in self.phys_brs:
self._reclaim_local_vlan_outbound(lvm)
self._reclaim_local_vlan_inbound(lvm)
elif lvm.network_type in [p_const.TYPE_FLAT, p_const.TYPE_VLAN]:
phys_port = self.int_ofports[lvm.physical_network]
self.int_br.reclaim_tenant_physnet(lvm.network_type, lvm.vlan,
lvm.segmentation_id, phys_port)
elif lvm.network_type == p_const.TYPE_LOCAL:
# no flows needed for local networks
pass
@ -684,22 +504,18 @@ class OFANeutronAgent(n_rpc.RpcCallback,
self.provision_local_vlan(net_uuid, network_type,
physical_network, segmentation_id)
lvm = self.local_vlan_map[net_uuid]
lvm.vif_ports[port.normalized_port_name()] = port
# Do not bind a port if it's already bound
cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
if cur_tag != str(lvm.vlan):
self.int_br.set_db_attribute("Port", port.port_name, "tag",
str(lvm.vlan))
if port.ofport != -1:
match = self.int_br.ofparser.OFPMatch(in_port=port.ofport)
msg = self.int_br.ofparser.OFPFlowMod(
self.int_br.datapath,
table_id=ryu_ofp13.OFPTT_ALL,
command=ryu_ofp13.OFPFC_DELETE,
out_group=ryu_ofp13.OFPG_ANY,
out_port=ryu_ofp13.OFPP_ANY,
match=match)
self.ryu_send_msg(msg)
self.int_br.check_in_port_add_local_port(lvm.vlan, port.ofport)
# if any of vif mac is unknown, flood unicasts as well
flood_unicast = any(map(lambda x: x.vif_mac is None,
lvm.vif_ports.values()))
ofports = (vp.ofport for vp in lvm.vif_ports.values())
self.int_br.local_flood_update(lvm.vlan, ofports, flood_unicast)
if port.vif_mac is None:
return
self.int_br.local_out_add_port(lvm.vlan, port.ofport, port.vif_mac)
def port_unbound(self, vif_id, net_uuid=None):
"""Unbind port.
@ -718,178 +534,29 @@ class OFANeutronAgent(n_rpc.RpcCallback,
return
lvm = self.local_vlan_map[net_uuid]
lvm.vif_ports.pop(vif_id, None)
port = lvm.vif_ports.pop(vif_id, None)
self.int_br.check_in_port_delete_port(port.ofport)
if not lvm.vif_ports:
self.reclaim_local_vlan(net_uuid)
if port.vif_mac is None:
return
self.int_br.local_out_delete_port(lvm.vlan, port.vif_mac)
def port_dead(self, port):
"""Once a port has no binding, put it on the "dead vlan".
:param port: a ovs_lib.VifPort object.
"""
# Don't kill a port if it's already dead
cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
if cur_tag != DEAD_VLAN_TAG:
self.int_br.set_db_attribute("Port", port.port_name, "tag",
DEAD_VLAN_TAG)
match = self.int_br.ofparser.OFPMatch(in_port=port.ofport)
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
priority=2, match=match)
self.ryu_send_msg(msg)
pass
def setup_integration_br(self):
"""Setup the integration bridge.
Create patch ports and remove all existing flows.
:param bridge_name: the name of the integration bridge.
:returns: the integration bridge
"""
self.int_br.setup_ofp()
self.int_br.delete_port(cfg.CONF.OVS.int_peer_patch_port)
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
table_id=ryu_ofp13.OFPTT_ALL,
command=ryu_ofp13.OFPFC_DELETE,
out_group=ryu_ofp13.OFPG_ANY,
out_port=ryu_ofp13.OFPP_ANY)
self.ryu_send_msg(msg)
# switch all traffic using L2 learning
actions = [self.int_br.ofparser.OFPActionOutput(
ryu_ofp13.OFPP_NORMAL, 0)]
instructions = [self.int_br.ofparser.OFPInstructionActions(
ryu_ofp13.OFPIT_APPLY_ACTIONS,
actions)]
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
priority=1,
instructions=instructions)
self.ryu_send_msg(msg)
def _tun_br_sort_incoming_traffic_depend_in_port(self, br):
match = br.ofparser.OFPMatch(
in_port=int(self.patch_int_ofport))
instructions = [br.ofparser.OFPInstructionGotoTable(
table_id=constants.PATCH_LV_TO_TUN)]
msg = br.ofparser.OFPFlowMod(br.datapath,
priority=1,
match=match,
instructions=instructions)
self.ryu_send_msg(msg)
msg = br.ofparser.OFPFlowMod(br.datapath, priority=0)
self.ryu_send_msg(msg)
def _tun_br_output_arp_packet_to_controller(self, br):
datapath = br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
arp_op=arp.ARP_REQUEST)
actions = [ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)]
instructions = [
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
msg = ofpp.OFPFlowMod(datapath,
table_id=constants.PATCH_LV_TO_TUN,
priority=10,
match=match,
instructions=instructions)
self.ryu_send_msg(msg)
def _tun_br_goto_table_ucast_unicast(self, br):
match = br.ofparser.OFPMatch(eth_dst=('00:00:00:00:00:00',
'01:00:00:00:00:00'))
instructions = [br.ofparser.OFPInstructionGotoTable(
table_id=constants.UCAST_TO_TUN)]
msg = br.ofparser.OFPFlowMod(br.datapath,
table_id=constants.PATCH_LV_TO_TUN,
priority=0,
match=match,
instructions=instructions)
self.ryu_send_msg(msg)
def _tun_br_goto_table_flood_broad_multi_cast(self, br):
match = br.ofparser.OFPMatch(eth_dst=('01:00:00:00:00:00',
'01:00:00:00:00:00'))
instructions = [br.ofparser.OFPInstructionGotoTable(
table_id=constants.FLOOD_TO_TUN)]
msg = br.ofparser.OFPFlowMod(br.datapath,
table_id=constants.PATCH_LV_TO_TUN,
priority=0,
match=match,
instructions=instructions)
self.ryu_send_msg(msg)
def _tun_br_set_table_tun_by_tunnel_type(self, br):
for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
msg = br.ofparser.OFPFlowMod(
br.datapath,
table_id=constants.TUN_TABLE[tunnel_type],
priority=0)
self.ryu_send_msg(msg)
def _tun_br_output_patch_int(self, br):
actions = [br.ofparser.OFPActionOutput(
int(self.patch_int_ofport), 0)]
instructions = [br.ofparser.OFPInstructionActions(
ryu_ofp13.OFPIT_APPLY_ACTIONS,
actions)]
msg = br.ofparser.OFPFlowMod(br.datapath,
table_id=constants.LEARN_FROM_TUN,
priority=1,
instructions=instructions)
self.ryu_send_msg(msg)
def _tun_br_goto_table_flood_unknown_unicast(self, br):
instructions = [br.ofparser.OFPInstructionGotoTable(
table_id=constants.FLOOD_TO_TUN)]
msg = br.ofparser.OFPFlowMod(br.datapath,
table_id=constants.UCAST_TO_TUN,
priority=0,
instructions=instructions)
self.ryu_send_msg(msg)
def _tun_br_default_drop(self, br):
msg = br.ofparser.OFPFlowMod(
br.datapath,
table_id=constants.FLOOD_TO_TUN,
priority=0)
self.ryu_send_msg(msg)
def setup_tunnel_br(self, tun_br):
"""Setup the tunnel bridge.
Creates tunnel bridge, and links it to the integration bridge
using a patch port.
:param tun_br: the name of the tunnel bridge.
"""
self.tun_br = OVSBridge(tun_br, self.root_helper, self.ryuapp)
self.tun_br.reset_bridge()
self.tun_br.setup_ofp()
self.patch_tun_ofport = self.int_br.add_patch_port(
cfg.CONF.OVS.int_peer_patch_port, cfg.CONF.OVS.tun_peer_patch_port)
self.patch_int_ofport = self.tun_br.add_patch_port(
cfg.CONF.OVS.tun_peer_patch_port, cfg.CONF.OVS.int_peer_patch_port)
if int(self.patch_tun_ofport) < 0 or int(self.patch_int_ofport) < 0:
LOG.error(_("Failed to create OVS patch port. Cannot have "
"tunneling enabled on this agent, since this version "
"of OVS does not support tunnels or patch ports. "
"Agent terminated!"))
raise SystemExit(1)
msg = self.tun_br.ofparser.OFPFlowMod(self.tun_br.datapath,
table_id=ryu_ofp13.OFPTT_ALL,
command=ryu_ofp13.OFPFC_DELETE,
out_group=ryu_ofp13.OFPG_ANY,
out_port=ryu_ofp13.OFPP_ANY)
self.ryu_send_msg(msg)
self._tun_br_sort_incoming_traffic_depend_in_port(self.tun_br)
self._tun_br_output_arp_packet_to_controller(self.tun_br)
self._tun_br_goto_table_ucast_unicast(self.tun_br)
self._tun_br_goto_table_flood_broad_multi_cast(self.tun_br)
self._tun_br_set_table_tun_by_tunnel_type(self.tun_br)
self._tun_br_output_patch_int(self.tun_br)
self._tun_br_goto_table_flood_unknown_unicast(self.tun_br)
self._tun_br_default_drop(self.tun_br)
br = self.int_br
br.setup_ofp()
br.setup_default_table()
def _phys_br_prepare_create_veth(self, br, int_veth_name, phys_veth_name):
self.int_br.delete_port(int_veth_name)
@ -905,21 +572,11 @@ class OFANeutronAgent(n_rpc.RpcCallback,
phys_veth_name, physical_network, ip_wrapper):
int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name,
phys_veth_name)
self.int_ofports[physical_network] = self.int_br.add_port(int_veth)
self.phys_ofports[physical_network] = br.add_port(phys_veth)
int_br = self.int_br
self.int_ofports[physical_network] = int(int_br.add_port(int_veth))
self.phys_ofports[physical_network] = int(br.add_port(phys_veth))
return (int_veth, phys_veth)
def _phys_br_block_untranslated_traffic(self, br, physical_network):
match = self.int_br.ofparser.OFPMatch(in_port=int(
self.int_ofports[physical_network]))
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
priority=2, match=match)
self.ryu_send_msg(msg)
match = br.ofparser.OFPMatch(in_port=int(
self.phys_ofports[physical_network]))
msg = br.ofparser.OFPFlowMod(br.datapath, priority=2, match=match)
self.ryu_send_msg(msg)
def _phys_br_enable_veth_to_pass_traffic(self, int_veth, phys_veth):
# enable veth to pass traffic
int_veth.link.set_up()
@ -939,7 +596,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
phys_veth_name,
physical_network,
ip_wrapper)
self._phys_br_block_untranslated_traffic(br, physical_network)
self._phys_br_enable_veth_to_pass_traffic(int_veth, phys_veth)
def setup_physical_bridges(self, bridge_mappings):
@ -967,22 +623,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
{'physical_network': physical_network,
'bridge': bridge})
raise SystemExit(1)
br = OVSBridge(bridge, self.root_helper, self.ryuapp)
br.setup_ofp()
msg = br.ofparser.OFPFlowMod(br.datapath,
table_id=ryu_ofp13.OFPTT_ALL,
command=ryu_ofp13.OFPFC_DELETE,
out_group=ryu_ofp13.OFPG_ANY,
out_port=ryu_ofp13.OFPP_ANY)
self.ryu_send_msg(msg)
actions = [br.ofparser.OFPActionOutput(ryu_ofp13.OFPP_NORMAL, 0)]
instructions = [br.ofparser.OFPInstructionActions(
ryu_ofp13.OFPIT_APPLY_ACTIONS,
actions)]
msg = br.ofparser.OFPFlowMod(br.datapath,
priority=1,
instructions=instructions)
self.ryu_send_msg(msg)
br = Bridge(bridge, self.root_helper, self.ryuapp)
self.phys_brs[physical_network] = br
self._phys_br_patch_physical_bridge_with_integration_bridge(
@ -994,7 +635,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
port_info = {'current': cur_ports}
if updated_ports is None:
updated_ports = set()
updated_ports.update(self._find_lost_vlan_port(registered_ports))
if updated_ports:
# Some updated ports might have been removed in the
# meanwhile, and therefore should not be processed.
@ -1013,33 +653,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
port_info['removed'] = registered_ports - cur_ports
return port_info
def _find_lost_vlan_port(self, registered_ports):
"""Return ports which have lost their vlan tag.
The returned value is a set of port ids of the ports concerned by a
vlan tag loss.
"""
# TODO(yamamoto): stop using ovsdb
# an idea is to use metadata instead of tagged vlans.
# cf. blueprint ofagent-merge-bridges
port_tags = self.int_br.get_port_tag_dict()
changed_ports = set()
for lvm in self.local_vlan_map.values():
for port in registered_ports:
if (
port in lvm.vif_ports
and port in port_tags
and port_tags[port] != lvm.vlan
):
LOG.info(
_("Port '%(port_name)s' has lost "
"its vlan tag '%(vlan_tag)d'!"),
{'port_name': port,
'vlan_tag': lvm.vlan}
)
changed_ports.add(port)
return changed_ports
def treat_vif_port(self, vif_port, port_id, network_id, network_type,
physical_network, segmentation_id, admin_state_up):
if vif_port:
@ -1059,34 +672,25 @@ class OFANeutronAgent(n_rpc.RpcCallback,
LOG.debug(_("No VIF port for port %s defined on agent."), port_id)
def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type):
ofport = br.add_tunnel_port(port_name,
remote_ip,
self.local_ip,
tunnel_type,
self.vxlan_udp_port,
self.dont_fragment)
ofport_int = -1
ofport_str = br.add_tunnel_port(port_name,
remote_ip,
self.local_ip,
tunnel_type,
self.vxlan_udp_port,
self.dont_fragment)
ofport = -1
try:
ofport_int = int(ofport)
ofport = int(ofport_str)
except (TypeError, ValueError):
LOG.exception(_("ofport should have a value that can be "
"interpreted as an integer"))
if ofport_int < 0:
if ofport < 0:
LOG.error(_("Failed to set-up %(type)s tunnel port to %(ip)s"),
{'type': tunnel_type, 'ip': remote_ip})
return 0
self.tun_br_ofports[tunnel_type][remote_ip] = ofport
# Add flow in default table to resubmit to the right
# tunelling table (lvid will be set in the latter)
match = br.ofparser.OFPMatch(in_port=int(ofport))
instructions = [br.ofparser.OFPInstructionGotoTable(
table_id=constants.TUN_TABLE[tunnel_type])]
msg = br.ofparser.OFPFlowMod(br.datapath,
priority=1,
match=match,
instructions=instructions)
self.ryu_send_msg(msg)
self.tun_ofports[tunnel_type][remote_ip] = ofport
br.check_in_port_add_tunnel_port(tunnel_type, ofport)
return ofport
def setup_tunnel_port(self, br, remote_ip, network_type):
@ -1100,23 +704,14 @@ class OFANeutronAgent(n_rpc.RpcCallback,
return ofport
def _remove_tunnel_port(self, br, tun_ofport, tunnel_type):
datapath = br.datapath
ofp = datapath.ofproto
ofpp = datapath.ofproto_parser
for remote_ip, ofport in self.tun_br_ofports[tunnel_type].items():
for remote_ip, ofport in self.tun_ofports[tunnel_type].items():
if ofport == tun_ofport:
br.check_in_port_delete_port(ofport)
port_name = self._create_tunnel_port_name(tunnel_type,
remote_ip)
if port_name:
br.delete_port(port_name)
match = ofpp.OFPMatch(in_port=int(ofport))
msg = ofpp.OFPFlowMod(datapath,
command=ofp.OFPFC_DELETE,
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY,
match=match)
self.ryu_send_msg(msg)
self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
self.tun_ofports[tunnel_type].pop(remote_ip, None)
def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type):
# Check if this tunnel port is still used
@ -1154,6 +749,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
if 'port_id' in details:
LOG.info(_("Port %(device)s updated. Details: %(details)s"),
{'device': device, 'details': details})
port.vif_mac = details.get('mac_address')
self.treat_vif_port(port, details['port_id'],
details['network_id'],
details['network_type'],
@ -1344,7 +940,6 @@ def create_agent_config_map(config):
kwargs = dict(
integ_br=config.OVS.integration_bridge,
tun_br=config.OVS.tunnel_bridge,
local_ip=config.OVS.local_ip,
bridge_mappings=bridge_mappings,
root_helper=config.AGENT.root_helper,

View File

@ -0,0 +1,78 @@
# 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,4 +1,5 @@
# 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
@ -12,8 +13,6 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
class OFPort(object):
@ -77,6 +76,10 @@ def _normalize_port_name(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)

View File

@ -0,0 +1,62 @@
# 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

@ -117,8 +117,10 @@ def patch_fake_oflib_of():
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
@ -139,6 +141,7 @@ def patch_fake_oflib_of():
'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,

View File

@ -23,29 +23,13 @@ from neutron.tests import base
from neutron.tests.unit.ofagent import fake_oflib
class OFAAgentTestBase(base.BaseTestCase):
_AGENT_NAME = 'neutron.plugins.ofagent.agent.ofa_neutron_agent'
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)
self.mod_agent = importutils.import_module(self._AGENT_NAME)
super(OFAAgentTestBase, self).setUp()
self.ryuapp = mock.Mock()
def setup_config(self):
cfg.CONF.set_default('firewall_driver',
'neutron.agent.firewall.NoopFirewallDriver',
group='SECURITYGROUP')
cfg.CONF.register_cli_opts([
cfg.StrOpt('ofp-listen-host', default='',
help='openflow listen host'),
cfg.IntOpt('ofp-tcp-listen-port', default=6633,
help='openflow tcp listen port')
])
cfg.CONF.set_override('root_helper', 'fake_helper', group='AGENT')
super(OFATestBase, self).setUp()
def _mk_test_dp(self, name):
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
@ -63,3 +47,25 @@ class OFAAgentTestBase(base.BaseTestCase):
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')
])
cfg.CONF.set_override('root_helper', 'fake_helper', group='AGENT')

View File

@ -19,6 +19,7 @@ import contextlib
import mock
from neutron.openstack.common import importutils
import neutron.plugins.ofagent.agent.metadata as meta
from neutron.tests.unit.ofagent import ofa_test_base
@ -38,7 +39,7 @@ class OFAAgentTestCase(ofa_test_base.OFAAgentTestBase):
self.packet_mod = mock.Mock()
self.proto_ethernet_mod = mock.Mock()
self.proto_vlan_mod = mock.Mock()
self.proto_vlan_mod.vid = self.nets[0].net
self.proto_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
@ -67,7 +68,8 @@ class OFAAgentTestCase(ofa_test_base.OFAAgentTestBase):
self.msg_data = 'test_message_data'
self.msg.data = self.msg_data
self.ev.msg = self.msg
self.msg.match = {'in_port': self.inport}
self.msg.match = {'in_port': self.inport,
'metadata': meta.LOCAL | self.nets[0].net}
class TestArpLib(OFAAgentTestCase):
@ -81,6 +83,8 @@ class TestArpLib(OFAAgentTestCase):
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
@ -233,14 +237,14 @@ class TestArpLib(OFAAgentTestCase):
else:
return
def test_packet_in_handler(self):
def _test_packet_in_handler(self):
self.arplib._arp_tbl = {
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
with contextlib.nested(
mock.patch.object(self.arplib, '_respond_arp',
return_value=True),
mock.patch.object(self.arplib,
'_add_flow_to_avoid_unknown_packet'),
mock.patch.object(self.br,
'arp_passthrough'),
mock.patch.object(self.arplib,
'_send_unknown_packet'),
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
@ -250,16 +254,17 @@ class TestArpLib(OFAAgentTestCase):
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)
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(self):
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.arplib,
'_add_flow_to_avoid_unknown_packet'),
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):
@ -268,9 +273,12 @@ class TestArpLib(OFAAgentTestCase):
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()
self._test_packet_in_handler_drop()
def test_packet_in_handler_non_vlan(self):
self._fake_get_protocol_vlan = False
@ -278,7 +286,7 @@ class TestArpLib(OFAAgentTestCase):
def test_packet_in_handler_non_arp(self):
self._fake_get_protocol_arp = False
self._test_packet_in_handler()
self._test_packet_in_handler_drop()
def test_packet_in_handler_unknown_network(self):
self.arplib._arp_tbl = {
@ -286,20 +294,14 @@ class TestArpLib(OFAAgentTestCase):
with contextlib.nested(
mock.patch.object(self.arplib, '_respond_arp',
return_value=False),
mock.patch.object(self.arplib,
'_add_flow_to_avoid_unknown_packet'),
mock.patch.object(self.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(
self.datapath,
self.datapath.ofproto_parser.OFPMatch(
eth_type=self.ethernet.ETH_TYPE_ARP,
vlan_vid=self.proto_vlan_mod.vid |
self.datapath.ofproto.OFPVID_PRESENT,
arp_op=self.arp.ARP_REQUEST,
arp_tpa=self.proto_arp_mod.dst_ip))
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)

View File

@ -0,0 +1,351 @@
# 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.openstack.common 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)
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

@ -20,6 +20,7 @@
import collections
import contextlib
import copy
import mock
import netaddr
@ -31,7 +32,6 @@ from neutron.agent.linux import utils
from neutron.common import constants as n_const
from neutron.openstack.common import importutils
from neutron.plugins.common import constants as p_const
from neutron.plugins.openvswitch.common import constants
from neutron.tests.unit.ofagent import ofa_test_base
@ -91,13 +91,13 @@ class CreateAgentConfigMap(ofa_test_base.OFAAgentTestBase):
[p_const.TYPE_GRE, p_const.TYPE_VXLAN])
class TestOFANeutronAgentOVSBridge(ofa_test_base.OFAAgentTestBase):
class TestOFANeutronAgentBridge(ofa_test_base.OFAAgentTestBase):
def setUp(self):
super(TestOFANeutronAgentOVSBridge, self).setUp()
super(TestOFANeutronAgentBridge, self).setUp()
self.br_name = 'bridge1'
self.root_helper = 'fake_helper'
self.ovs = self.mod_agent.OVSBridge(
self.ovs = self.mod_agent.Bridge(
self.br_name, self.root_helper, self.ryuapp)
def test_find_datapath_id(self):
@ -205,7 +205,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
mock.patch.object(self.mod_agent.OFANeutronAgent,
'setup_integration_br',
return_value=mock.Mock()),
mock.patch.object(self.mod_agent.OVSBridge,
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',
@ -217,9 +217,8 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
self.agent.sg_agent = mock.Mock()
self.int_dp = self._mk_test_dp('int_br')
self.agent.int_br.ofparser = self.int_dp.ofproto_parser
self.agent.int_br.datapath = self.int_dp
self.agent.tun_br = self._mk_test_br('tun_br')
self.agent.int_br = self._mk_test_br('int_br')
self.agent.int_br.set_dp(self.int_dp)
self.agent.phys_brs['phys-net1'] = self._mk_test_br('phys_br1')
self.agent.phys_ofports['phys-net1'] = 777
self.agent.int_ofports['phys-net1'] = 666
@ -229,89 +228,6 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
tunnel_ip_hex = '%08x' % netaddr.IPAddress(tunnel_ip, version=4)
return '%s-%s' % (tunnel_type, tunnel_ip_hex)
def _mock_port_bound(self, ofport=None, new_local_vlan=None,
old_local_vlan=None):
port_name = 'tap96408df7-16'
port = _mock_port(True, port_name)
port.ofport = ofport
net_uuid = 'my-net-uuid'
ofp = self.agent.int_br.datapath.ofproto
ofpp = self.agent.int_br.datapath.ofproto_parser
expected_msg = ofpp.OFPFlowMod(
self.agent.int_br.datapath,
match=ofpp.OFPMatch(in_port=port.ofport),
table_id=ofp.OFPTT_ALL,
command=ofp.OFPFC_DELETE,
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY
)
if old_local_vlan is not None:
self.agent.local_vlan_map[net_uuid] = (
self.mod_agent.LocalVLANMapping(
old_local_vlan, None, None, None))
with contextlib.nested(
mock.patch.object(self.mod_agent.OVSBridge,
'set_db_attribute', return_value=True),
mock.patch.object(self.mod_agent.OVSBridge,
'db_get_val', return_value=str(old_local_vlan)),
mock.patch.object(self.agent, 'ryu_send_msg')
) as (set_ovs_db_func, get_ovs_db_func, ryu_send_msg_func):
self.agent.port_bound(port, net_uuid, 'local', None, None)
get_ovs_db_func.assert_called_once_with("Port", mock.ANY, "tag")
if new_local_vlan != old_local_vlan:
set_ovs_db_func.assert_called_once_with(
"Port", mock.ANY, "tag", str(new_local_vlan))
if ofport != -1:
ryu_send_msg_func.assert_called_once_with(expected_msg)
else:
self.assertFalse(ryu_send_msg_func.called)
else:
self.assertFalse(set_ovs_db_func.called)
self.assertFalse(ryu_send_msg_func.called)
self.assertTrue(self.agent.local_vlan_map[net_uuid].
vif_ports[port_name] is port)
def test_port_bound_deletes_flows_for_valid_ofport(self):
self._mock_port_bound(ofport=1, new_local_vlan=1)
def test_port_bound_ignores_flows_for_invalid_ofport(self):
self._mock_port_bound(ofport=-1, new_local_vlan=1)
def test_port_bound_does_not_rewire_if_already_bound(self):
self._mock_port_bound(ofport=-1, new_local_vlan=1, old_local_vlan=1)
def _test_port_dead(self, cur_tag=None):
port = mock.Mock()
port.ofport = 1
ofpp = self.agent.int_br.datapath.ofproto_parser
expected_msg = ofpp.OFPFlowMod(
self.agent.int_br.datapath,
priority=2,
match=ofpp.OFPMatch(in_port=port.ofport)
)
with contextlib.nested(
mock.patch.object(self.mod_agent.OVSBridge,
'set_db_attribute', return_value=True),
mock.patch.object(self.mod_agent.OVSBridge,
'db_get_val', return_value=cur_tag),
mock.patch.object(self.agent, 'ryu_send_msg')
) as (set_ovs_db_func, get_ovs_db_func, ryu_send_msg_func):
self.agent.port_dead(port)
get_ovs_db_func.assert_called_once_with("Port", mock.ANY, "tag")
if cur_tag == self.mod_agent.DEAD_VLAN_TAG:
self.assertFalse(set_ovs_db_func.called)
self.assertFalse(ryu_send_msg_func.called)
else:
set_ovs_db_func.assert_called_once_with(
"Port", mock.ANY, "tag", str(self.mod_agent.DEAD_VLAN_TAG))
ryu_send_msg_func.assert_called_once_with(expected_msg)
def test_port_dead(self):
self._test_port_dead()
def test_port_dead_with_port_already_dead(self):
self._test_port_dead(self.mod_agent.DEAD_VLAN_TAG)
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 {}
@ -373,27 +289,6 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
updated_ports)
self.assertEqual(expected, actual)
def test_update_ports_returns_lost_vlan_port(self):
port = mock.Mock(port_name='tap00000001-00', ofport=1)
lvm = self.mod_agent.LocalVLANMapping(
vlan=1, network_type='1', physical_network=None, segmentation_id=1,
vif_ports={port.port_name: port})
local_vlan_map = {'1': lvm}
port_set = set(['tap00000001-00',
'tap00000003-00'])
registered_ports = set(['tap00000001-00', 'tap00000002-00'])
port_tags_dict = {'tap00000001-00': []}
expected = dict(
added=set(['tap00000003-00']),
current=set(['tap00000001-00', 'tap00000003-00']),
removed=set(['tap00000002-00']),
updated=set(['tap00000001-00'])
)
with mock.patch.dict(self.agent.local_vlan_map, local_vlan_map):
actual = self.mock_scan_ports(
port_set, registered_ports, port_tags_dict=port_tags_dict)
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',
@ -556,11 +451,11 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
with contextlib.nested(
mock.patch.object(ip_lib, "device_exists"),
mock.patch.object(utils, "execute"),
mock.patch.object(self.mod_agent.OVSBridge, "add_port"),
mock.patch.object(self.mod_agent.OVSBridge, "delete_port"),
mock.patch.object(self.mod_agent.OVSBridge, "set_protocols"),
mock.patch.object(self.mod_agent.OVSBridge, "set_controller"),
mock.patch.object(self.mod_agent.OVSBridge, "get_datapath_id",
mock.patch.object(self.mod_agent.Bridge, "add_port"),
mock.patch.object(self.mod_agent.Bridge, "delete_port"),
mock.patch.object(self.mod_agent.Bridge, "set_protocols"),
mock.patch.object(self.mod_agent.Bridge, "set_controller"),
mock.patch.object(self.mod_agent.Bridge, "get_datapath_id",
return_value='0xa'),
mock.patch.object(self.agent.int_br, "add_port"),
mock.patch.object(self.agent.int_br, "delete_port"),
@ -592,34 +487,27 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
mock.call.add_veth('int-br-eth',
'phy-br-eth')]
parent.assert_has_calls(expected_calls, any_order=False)
self.assertEqual(self.agent.int_ofports["physnet1"],
"11")
self.assertEqual(self.agent.phys_ofports["physnet1"],
"25")
self.assertEqual(11, self.agent.int_ofports["physnet1"])
self.assertEqual(25, self.agent.phys_ofports["physnet1"])
def test_port_unbound(self):
with mock.patch.object(self.agent, "reclaim_local_vlan") as reclvl_fn:
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", "netuid12345")
self.agent.port_unbound("vif1")
self.assertTrue(reclvl_fn.called)
reclvl_fn.called = False
lvm.vif_ports = {}
self.agent.port_unbound("vif1", "netuid12345")
self.assertEqual(reclvl_fn.call_count, 2)
lvm.vif_ports = {"vif1": mock.Mock()}
self.agent.port_unbound("vif3", "netuid12345")
self.assertEqual(reclvl_fn.call_count, 2)
def _prepare_l2_pop_ofports(self):
LVM = collections.namedtuple('LVM', 'net, vlan, segid, ip')
self.lvms = [LVM(net='net1', vlan=11, segid='21', ip='1.1.1.1'),
LVM(net='net2', vlan=12, segid='22', ip='2.2.2.2')]
self.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)
@ -629,31 +517,29 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
lvm1.network_type = self.tunnel_type
lvm1.vlan = self.lvms[0].vlan
lvm1.segmentation_id = self.lvms[0].segid
lvm1.tun_ofports = set(['1'])
lvm1.tun_ofports = set([1])
lvm2 = mock.Mock()
lvm2.network_type = self.tunnel_type
lvm2.vlan = self.lvms[1].vlan
lvm2.segmentation_id = self.lvms[1].segid
lvm2.tun_ofports = set(['1', '2'])
lvm2.tun_ofports = set([1, 2])
self.agent.local_vlan_map = {self.lvms[0].net: lvm1,
self.lvms[1].net: lvm2}
self.agent.tun_br_ofports = {self.tunnel_type:
{self.lvms[0].ip: '1',
self.lvms[1].ip: '2'}}
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, 'ryu_send_msg'),
mock.patch.object(self.agent, '_setup_tunnel_port'),
mock.patch.object(self.agent, 'cleanup_tunnel_port')
) as (ryu_send_msg_fn, add_tun_fn, clean_tun_fn):
) 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)
self.assertFalse(ryu_send_msg_fn.called)
def test_fdb_ignore_self(self):
self._prepare_l2_pop_ofports()
@ -665,13 +551,16 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
{'agent_ip':
[['mac', 'ip'],
n_const.FLOODING_ENTRY]}}}
with mock.patch.object(self.agent.tun_br,
"defer_apply_on") as defer_fn:
self.agent.fdb_add(None, fdb_entry)
self.assertFalse(defer_fn.called)
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))
self.assertFalse(add_fn.called)
self.assertFalse(del_fn.called)
self.agent.fdb_remove(None, fdb_entry)
self.assertFalse(defer_fn.called)
self.assertFalse(add_fn.called)
self.assertFalse(del_fn.called)
def test_fdb_add_flows(self):
self._prepare_l2_pop_ofports()
@ -683,12 +572,19 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
[['mac', 'ip'],
n_const.FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent, 'ryu_send_msg'),
mock.patch.object(self.agent.tun_br, '_setup_tunnel_port'),
) as (ryu_send_msg_fn, add_tun_fn):
add_tun_fn.return_value = '2'
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(ryu_send_msg_fn.call_count, 2)
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()
@ -699,10 +595,14 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
{self.lvms[1].ip:
[['mac', 'ip'],
n_const.FLOODING_ENTRY]}}}
with mock.patch.object(self.agent,
'ryu_send_msg') as ryu_send_msg_fn:
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)
self.assertEqual(ryu_send_msg_fn.call_count, 3)
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()
@ -713,16 +613,13 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
{'network_type': self.tunnel_type,
'segment_id': 'tun1',
'ports': {self.lvms[0].ip: [['mac', 'ip']]}}}
with contextlib.nested(
mock.patch.object(self.agent, 'ryu_send_msg'),
mock.patch.object(self.agent, '_setup_tunnel_port')
) as (ryu_send_msg_fn, add_tun_fn):
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] = [['mac', 'ip']]
self.agent.fdb_add(None, fdb_entry)
add_tun_fn.assert_called_with(
self.agent.tun_br, tun_name, tunnel_ip, self.tunnel_type)
self.agent.int_br, tun_name, tunnel_ip, self.tunnel_type)
def test_fdb_del_port(self):
self._prepare_l2_pop_ofports()
@ -730,10 +627,8 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
{'network_type': self.tunnel_type,
'segment_id': 'tun2',
'ports': {self.lvms[1].ip: [n_const.FLOODING_ENTRY]}}}
with contextlib.nested(
mock.patch.object(self.agent, 'ryu_send_msg'),
mock.patch.object(self.agent.tun_br, 'delete_port')
) as (ryu_send_msg_fn, del_port_fn):
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)
@ -744,9 +639,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
'segment_id': 'tun1',
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
self.lvms[1].ip: [['mac2', 'ip2']]}}}
with mock.patch.multiple(self.agent,
ryu_send_msg=mock.DEFAULT,
setup_tunnel_port=mock.DEFAULT):
with mock.patch.object(self.agent, 'setup_tunnel_port'):
self.agent.fdb_add(None, fdb_entry)
calls = [
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
@ -763,9 +656,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
'segment_id': 'tun1',
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
self.lvms[1].ip: [['mac2', 'ip2']]}}}
with mock.patch.multiple(self.agent,
ryu_send_msg=mock.DEFAULT,
setup_tunnel_port=mock.DEFAULT):
with mock.patch.object(self.agent, 'cleanup_tunnel_port'):
self.agent.fdb_remove(None, fdb_entry)
calls = [
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
@ -779,29 +670,27 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
self._prepare_l2_pop_ofports()
self.agent.enable_tunneling = True
with mock.patch.object(
self.agent.tun_br, 'cleanup_tunnel_port'
) as clean_tun_fn:
self.agent.int_br, 'delete_port'
) as del_port_fn:
self.agent.reclaim_local_vlan(self.lvms[0].net)
self.assertFalse(clean_tun_fn.called)
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 contextlib.nested(
mock.patch.object(self.agent.tun_br, 'delete_port'),
mock.patch.object(self.agent, 'ryu_send_msg')
) as (del_port_fn, ryu_send_msg_fn):
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.tun_br, 'add_tunnel_port',
mock.patch.object(self.agent.int_br, 'add_tunnel_port',
return_value='-1'),
mock.patch.object(self.mod_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_error_fn):
ofport = self.agent._setup_tunnel_port(
self.agent.tun_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE)
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)
@ -812,13 +701,13 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
def test__setup_tunnel_port_error_not_int(self):
with contextlib.nested(
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
mock.patch.object(self.agent.int_br, 'add_tunnel_port',
return_value=None),
mock.patch.object(self.mod_agent.LOG, 'exception'),
mock.patch.object(self.mod_agent.LOG, 'error')
) as (add_tunnel_port_fn, log_exc_fn, log_error_fn):
ofport = self.agent._setup_tunnel_port(
self.agent.tun_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE)
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)
@ -843,219 +732,6 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
self.agent.local_ip,
self.agent.tunnel_types[0])
def test__provision_local_vlan_inbound_for_tunnel(self):
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._provision_local_vlan_inbound_for_tunnel(1, 'gre', 3)
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
expected_msg = ofpp.OFPFlowMod(
self.agent.tun_br.datapath,
instructions=[
ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS,
[
ofpp.OFPActionPushVlan(),
ofpp.OFPActionSetField(vlan_vid=1 |
ofp.OFPVID_PRESENT),
]),
ofpp.OFPInstructionGotoTable(
table_id=constants.LEARN_FROM_TUN),
],
match=ofpp.OFPMatch(tunnel_id=3),
priority=1,
table_id=constants.TUN_TABLE['gre'])
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__provision_local_vlan_outbound(self):
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._provision_local_vlan_outbound(888, 999, 'phys-net1')
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
expected_msg = ofpp.OFPFlowMod(
self.agent.phys_brs['phys-net1'].datapath,
instructions=[
ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS,
[
ofpp.OFPActionSetField(vlan_vid=999),
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
]
)
],
match=ofpp.OFPMatch(
in_port=777,
vlan_vid=888 | ofp.OFPVID_PRESENT
),
priority=4)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__provision_local_vlan_inbound(self):
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._provision_local_vlan_inbound(888, 999, 'phys-net1')
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
expected_msg = ofpp.OFPFlowMod(
self.agent.int_br.datapath,
instructions=[
ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS,
[
ofpp.OFPActionSetField(
vlan_vid=888 | ofp.OFPVID_PRESENT
),
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
]
)
],
match=ofpp.OFPMatch(in_port=666, vlan_vid=999),
priority=3)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__reclaim_local_vlan_outbound(self):
lvm = mock.Mock()
lvm.network_type = p_const.TYPE_VLAN
lvm.segmentation_id = 555
lvm.vlan = 444
lvm.physical_network = 'phys-net1'
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._reclaim_local_vlan_outbound(lvm)
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
expected_msg = ofpp.OFPFlowMod(
self.agent.phys_brs['phys-net1'].datapath,
command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(
in_port=777,
vlan_vid=444 | ofp.OFPVID_PRESENT
),
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY,
table_id=ofp.OFPTT_ALL)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__reclaim_local_vlan_inbound(self):
lvm = mock.Mock()
lvm.network_type = p_const.TYPE_VLAN
lvm.segmentation_id = 555
lvm.vlan = 444
lvm.physical_network = 'phys-net1'
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._reclaim_local_vlan_inbound(lvm)
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
expected_msg = ofpp.OFPFlowMod(
self.agent.int_br.datapath,
command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(
in_port=666,
vlan_vid=555 | ofp.OFPVID_PRESENT
),
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY,
table_id=ofp.OFPTT_ALL)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__provision_local_vlan_outbound_flat(self):
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._provision_local_vlan_outbound(888, ofp.OFPVID_NONE,
'phys-net1')
expected_msg = ofpp.OFPFlowMod(
self.agent.phys_brs['phys-net1'].datapath,
instructions=[
ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS,
[
ofpp.OFPActionPopVlan(),
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
]
)
],
match=ofpp.OFPMatch(
in_port=777,
vlan_vid=888 | ofp.OFPVID_PRESENT
),
priority=4)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__provision_local_vlan_inbound_flat(self):
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._provision_local_vlan_inbound(888, ofp.OFPVID_NONE,
'phys-net1')
expected_msg = ofpp.OFPFlowMod(
self.agent.int_br.datapath,
instructions=[
ofpp.OFPInstructionActions(
ofp.OFPIT_APPLY_ACTIONS,
[
ofpp.OFPActionPushVlan(),
ofpp.OFPActionSetField(
vlan_vid=888 | ofp.OFPVID_PRESENT
),
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
]
)
],
match=ofpp.OFPMatch(in_port=666, vlan_vid=ofp.OFPVID_NONE),
priority=3)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__reclaim_local_vlan_outbound_flat(self):
lvm = mock.Mock()
lvm.network_type = p_const.TYPE_FLAT
lvm.segmentation_id = 555
lvm.vlan = 444
lvm.physical_network = 'phys-net1'
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._reclaim_local_vlan_outbound(lvm)
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
expected_msg = ofpp.OFPFlowMod(
self.agent.phys_brs['phys-net1'].datapath,
command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(
in_port=777,
vlan_vid=444 | ofp.OFPVID_PRESENT
),
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY,
table_id=ofp.OFPTT_ALL)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__reclaim_local_vlan_inbound_flat(self):
lvm = mock.Mock()
lvm.network_type = p_const.TYPE_FLAT
lvm.segmentation_id = 555
lvm.vlan = 444
lvm.physical_network = 'phys-net1'
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
self.agent._reclaim_local_vlan_inbound(lvm)
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
expected_msg = ofpp.OFPFlowMod(
self.agent.int_br.datapath,
command=ofp.OFPFC_DELETE,
match=ofpp.OFPMatch(
in_port=666,
vlan_vid=ofp.OFPVID_NONE
),
out_group=ofp.OFPG_ANY,
out_port=ofp.OFPP_ANY,
table_id=ofp.OFPTT_ALL)
sendmsg.assert_has_calls([mock.call(expected_msg)])
def test__get_ports(self):
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
reply = [ofpp.OFPPortDescStatsReply(body=[ofpp.OFPPort(name='hoge',
@ -1081,18 +757,3 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
result = self.agent._get_ofport_names('hoge')
_get_ports.assert_called_once_with('hoge')
self.assertEqual(set(names), result)
def test_setup_tunnel_br(self):
with contextlib.nested(
mock.patch.object(self.agent.int_br,
'add_patch_port', return_value='1'),
mock.patch.object(self.agent.tun_br,
'add_patch_port', return_value='2'),
mock.patch.object(self.mod_agent, 'OVSBridge',
return_value=self.agent.tun_br),
mock.patch.object(self.agent,
'_tun_br_output_arp_packet_to_controller')
) as (int_add_patch_port, tun_add_patch_port,
ovs_br_class, tun_output_ctrl):
self.agent.setup_tunnel_br(cfg.CONF.OVS.tunnel_bridge)
tun_output_ctrl.assert_called_once_with(self.agent.tun_br)

View File

@ -0,0 +1,82 @@
# 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.openstack.common 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)