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:
parent
0422f1aab7
commit
8ac9e0b2e0
|
@ -7,6 +7,21 @@ https://github.com/osrg/ryu/wiki/OpenStack
|
||||||
|
|
||||||
# -- Notes for updating from Icehouce
|
# -- 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.
|
The support of ancillary bridges has been removed after Icehouce.
|
||||||
While you can still use these bridges to provide connectivity,
|
While you can still use these bridges to provide connectivity,
|
||||||
neutron-ofagent-agent no longer reports port state changes (up/down)
|
neutron-ofagent-agent no longer reports port state changes (up/down)
|
||||||
|
|
|
@ -20,10 +20,9 @@ from ryu.lib.packet import arp
|
||||||
from ryu.lib.packet import ethernet
|
from ryu.lib.packet import ethernet
|
||||||
from ryu.lib.packet import packet
|
from ryu.lib.packet import packet
|
||||||
from ryu.lib.packet import vlan
|
from ryu.lib.packet import vlan
|
||||||
from ryu.ofproto import ether
|
|
||||||
|
|
||||||
from neutron.openstack.common import log as logging
|
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__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
@ -45,6 +44,10 @@ class ArpLib(object):
|
||||||
"""
|
"""
|
||||||
self.ryuapp = ryuapp
|
self.ryuapp = ryuapp
|
||||||
self._arp_tbl = {}
|
self._arp_tbl = {}
|
||||||
|
self.br = None
|
||||||
|
|
||||||
|
def set_bridge(self, br):
|
||||||
|
self.br = br
|
||||||
|
|
||||||
def _send_arp_reply(self, datapath, port, pkt):
|
def _send_arp_reply(self, datapath, port, pkt):
|
||||||
LOG.debug("packet-out %s", pkt)
|
LOG.debug("packet-out %s", pkt)
|
||||||
|
@ -60,21 +63,6 @@ class ArpLib(object):
|
||||||
data=data)
|
data=data)
|
||||||
ryu_api.send_msg(self.ryuapp, out)
|
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):
|
def _send_unknown_packet(self, msg, in_port, out_port):
|
||||||
LOG.debug("unknown packet-out in-port %(in_port)s "
|
LOG.debug("unknown packet-out in-port %(in_port)s "
|
||||||
"out-port %(out_port)s msg %(msg)s",
|
"out-port %(out_port)s msg %(msg)s",
|
||||||
|
@ -109,6 +97,7 @@ class ArpLib(object):
|
||||||
pkt.add_protocol(ethernet.ethernet(ethertype=pkt_ethernet.ethertype,
|
pkt.add_protocol(ethernet.ethernet(ethertype=pkt_ethernet.ethertype,
|
||||||
dst=pkt_ethernet.src,
|
dst=pkt_ethernet.src,
|
||||||
src=hw_addr))
|
src=hw_addr))
|
||||||
|
if pkt_vlan:
|
||||||
pkt.add_protocol(vlan.vlan(cfi=pkt_vlan.cfi,
|
pkt.add_protocol(vlan.vlan(cfi=pkt_vlan.cfi,
|
||||||
ethertype=pkt_vlan.ethertype,
|
ethertype=pkt_vlan.ethertype,
|
||||||
pcp=pkt_vlan.pcp,
|
pcp=pkt_vlan.pcp,
|
||||||
|
@ -146,32 +135,34 @@ class ArpLib(object):
|
||||||
msg = ev.msg
|
msg = ev.msg
|
||||||
LOG.debug("packet-in msg %s", msg)
|
LOG.debug("packet-in msg %s", msg)
|
||||||
datapath = msg.datapath
|
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
|
ofp = datapath.ofproto
|
||||||
ofpp = datapath.ofproto_parser
|
|
||||||
port = msg.match['in_port']
|
port = msg.match['in_port']
|
||||||
|
metadata = msg.match.get('metadata')
|
||||||
pkt = packet.Packet(msg.data)
|
pkt = packet.Packet(msg.data)
|
||||||
LOG.info(_("packet-in dpid %(dpid)s in_port %(port)s pkt %(pkt)s"),
|
LOG.info(_("packet-in dpid %(dpid)s in_port %(port)s pkt %(pkt)s"),
|
||||||
{'dpid': dpid_lib.dpid_to_str(datapath.id),
|
{'dpid': dpid_lib.dpid_to_str(datapath.id),
|
||||||
'port': port, 'pkt': pkt})
|
'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)
|
pkt_ethernet = pkt.get_protocol(ethernet.ethernet)
|
||||||
if not pkt_ethernet:
|
if not pkt_ethernet:
|
||||||
LOG.info(_("non-ethernet packet"))
|
LOG.info(_("drop non-ethernet packet"))
|
||||||
else:
|
return
|
||||||
pkt_vlan = pkt.get_protocol(vlan.vlan)
|
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)
|
pkt_arp = pkt.get_protocol(arp.arp)
|
||||||
if not pkt_arp:
|
if not pkt_arp:
|
||||||
LOG.info(_("drop non-arp packet"))
|
LOG.info(_("drop non-arp packet"))
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
# drop an unknown packet.
|
|
||||||
LOG.info(_("drop unknown packet"))
|
|
||||||
return
|
|
||||||
|
|
||||||
arptbl = self._arp_tbl.get(network)
|
arptbl = self._arp_tbl.get(network)
|
||||||
if arptbl:
|
if arptbl:
|
||||||
|
@ -180,11 +171,8 @@ class ArpLib(object):
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
LOG.info(_("unknown network %s"), network)
|
LOG.info(_("unknown network %s"), network)
|
||||||
|
|
||||||
# add a flow to skip a packet-in to a controller.
|
# add a flow to skip a packet-in to a controller.
|
||||||
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
|
self.br.arp_passthrough(network=network, tpa=pkt_arp.dst_ip)
|
||||||
vlan_vid=network | ofp.OFPVID_PRESENT,
|
|
||||||
arp_op=arp.ARP_REQUEST,
|
|
||||||
arp_tpa=pkt_arp.dst_ip)
|
|
||||||
self._add_flow_to_avoid_unknown_packet(datapath, match)
|
|
||||||
# send an unknown arp packet to the table.
|
# send an unknown arp packet to the table.
|
||||||
self._send_unknown_packet(msg, port, ofp.OFPP_TABLE)
|
self._send_unknown_packet(msg, port, ofp.OFPP_TABLE)
|
||||||
|
|
|
@ -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
|
|
@ -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)
|
|
@ -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)
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||||
|
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||||
# Based on openvswitch agent.
|
# Based on openvswitch agent.
|
||||||
#
|
#
|
||||||
# Copyright 2011 VMware, Inc.
|
# Copyright 2011 VMware, Inc.
|
||||||
|
@ -26,8 +27,6 @@ from ryu.base import app_manager
|
||||||
from ryu.controller import handler
|
from ryu.controller import handler
|
||||||
from ryu.controller import ofp_event
|
from ryu.controller import ofp_event
|
||||||
from ryu.lib import hub
|
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 ryu.ofproto import ofproto_v1_3 as ryu_ofp13
|
||||||
|
|
||||||
from neutron.agent import l2population_rpc
|
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.openstack.common import loopingcall
|
||||||
from neutron.plugins.common import constants as p_const
|
from neutron.plugins.common import constants as p_const
|
||||||
from neutron.plugins.ofagent.agent import arp_lib
|
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 ports
|
||||||
|
from neutron.plugins.ofagent.agent import tables
|
||||||
from neutron.plugins.ofagent.common import config # noqa
|
from neutron.plugins.ofagent.common import config # noqa
|
||||||
from neutron.plugins.openvswitch.common import constants
|
from neutron.plugins.openvswitch.common import constants
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
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'
|
# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
|
||||||
# attributes set).
|
# attributes set).
|
||||||
class LocalVLANMapping:
|
class LocalVLANMapping:
|
||||||
def __init__(self, vlan, network_type, physical_network, segmentation_id,
|
def __init__(self, vlan, network_type, physical_network, segmentation_id,
|
||||||
vif_ports=None):
|
vif_ports=None):
|
||||||
|
assert(isinstance(vlan, (int, long)))
|
||||||
if vif_ports is None:
|
if vif_ports is None:
|
||||||
vif_ports = {}
|
vif_ports = {}
|
||||||
self.vlan = vlan
|
self.vlan = vlan
|
||||||
|
@ -77,13 +77,13 @@ class LocalVLANMapping:
|
||||||
self.segmentation_id))
|
self.segmentation_id))
|
||||||
|
|
||||||
|
|
||||||
class OVSBridge(ovs_lib.OVSBridge):
|
class Bridge(flows.OFAgentIntegrationBridge, ovs_lib.OVSBridge):
|
||||||
def __init__(self, br_name, root_helper, ryuapp):
|
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_id = None
|
||||||
self.datapath = None
|
self.datapath = None
|
||||||
self.ofparser = None
|
|
||||||
self.ryuapp = ryuapp
|
self.ryuapp = ryuapp
|
||||||
|
self.set_app(ryuapp)
|
||||||
|
|
||||||
def find_datapath_id(self):
|
def find_datapath_id(self):
|
||||||
self.datapath_id = self.get_datapath_id()
|
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.'))
|
LOG.error(_('Agent terminated!: Failed to get a datapath.'))
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
self.ofparser = self.datapath.ofproto_parser
|
self.set_dp(self.datapath)
|
||||||
|
|
||||||
def setup_ofp(self, controller_names=None,
|
def setup_ofp(self, controller_names=None,
|
||||||
protocols='OpenFlow13',
|
protocols='OpenFlow13',
|
||||||
|
@ -162,6 +162,7 @@ class OFANeutronAgentRyuApp(app_manager.RyuApp):
|
||||||
cfg.CONF.set_default('ip_lib_force_root', True)
|
cfg.CONF.set_default('ip_lib_force_root', True)
|
||||||
|
|
||||||
agent = OFANeutronAgent(ryuapp, **agent_config)
|
agent = OFANeutronAgent(ryuapp, **agent_config)
|
||||||
|
self.arplib.set_bridge(agent.int_br)
|
||||||
|
|
||||||
# Start everything.
|
# Start everything.
|
||||||
LOG.info(_("Agent initialized successfully, now running... "))
|
LOG.info(_("Agent initialized successfully, now running... "))
|
||||||
|
@ -196,7 +197,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
# 1.1 Support Security Group RPC
|
# 1.1 Support Security Group RPC
|
||||||
RPC_API_VERSION = '1.1'
|
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,
|
bridge_mappings, root_helper,
|
||||||
polling_interval, tunnel_types=None,
|
polling_interval, tunnel_types=None,
|
||||||
veth_mtu=None):
|
veth_mtu=None):
|
||||||
|
@ -204,7 +205,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
|
|
||||||
:param ryuapp: object of the ryu app.
|
:param ryuapp: object of the ryu app.
|
||||||
:param integ_br: name of the integration bridge.
|
: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 local_ip: local IP address of this hypervisor.
|
||||||
:param bridge_mappings: mappings from physical network name to bridge.
|
:param bridge_mappings: mappings from physical network name to bridge.
|
||||||
:param root_helper: utility to use when running shell cmds.
|
:param root_helper: utility to use when running shell cmds.
|
||||||
|
@ -218,8 +218,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
self.ryuapp = ryuapp
|
self.ryuapp = ryuapp
|
||||||
self.veth_mtu = veth_mtu
|
self.veth_mtu = veth_mtu
|
||||||
self.root_helper = root_helper
|
self.root_helper = root_helper
|
||||||
self.available_local_vlans = set(xrange(n_const.MIN_VLAN_TAG,
|
# TODO(yamamoto): Remove this VLAN leftover
|
||||||
n_const.MAX_VLAN_TAG))
|
self.available_local_vlans = set(xrange(ofa_const.LOCAL_VLAN_MIN,
|
||||||
|
ofa_const.LOCAL_VLAN_MAX))
|
||||||
self.tunnel_types = tunnel_types or []
|
self.tunnel_types = tunnel_types or []
|
||||||
self.agent_state = {
|
self.agent_state = {
|
||||||
'binary': 'neutron-ofa-agent',
|
'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()
|
# Keep track of int_br's device count for use by _report_state()
|
||||||
self.int_br_device_count = 0
|
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
|
# Stores port update notifications for processing in main loop
|
||||||
self.updated_ports = set()
|
self.updated_ports = set()
|
||||||
self.setup_rpc()
|
self.setup_rpc()
|
||||||
self.setup_integration_br()
|
self.setup_integration_br()
|
||||||
self.setup_physical_bridges(bridge_mappings)
|
self.setup_physical_bridges(bridge_mappings)
|
||||||
self.local_vlan_map = {}
|
self.local_vlan_map = {}
|
||||||
self.tun_br_ofports = {p_const.TYPE_GRE: {},
|
self.tun_ofports = {}
|
||||||
p_const.TYPE_VXLAN: {}}
|
for t in tables.TUNNEL_TYPES:
|
||||||
|
self.tun_ofports[t] = {}
|
||||||
self.polling_interval = polling_interval
|
self.polling_interval = polling_interval
|
||||||
|
|
||||||
self.enable_tunneling = bool(self.tunnel_types)
|
self.enable_tunneling = bool(self.tunnel_types)
|
||||||
|
@ -252,8 +253,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
self.tunnel_count = 0
|
self.tunnel_count = 0
|
||||||
self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port
|
self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port
|
||||||
self.dont_fragment = cfg.CONF.AGENT.dont_fragment
|
self.dont_fragment = cfg.CONF.AGENT.dont_fragment
|
||||||
if self.enable_tunneling:
|
|
||||||
self.setup_tunnel_br(tun_br)
|
|
||||||
|
|
||||||
# Security group agent support
|
# Security group agent support
|
||||||
self.sg_agent = OFASecurityGroupAgent(self.context,
|
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"),
|
LOG.warn(_("Unable to create tunnel port. Invalid remote IP: %s"),
|
||||||
ip_address)
|
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):
|
def setup_rpc(self):
|
||||||
mac = self.int_br.get_local_port_mac()
|
mac = self.int_br.get_local_port_mac()
|
||||||
self.agent_id = '%s%s' % ('ovs', (mac.replace(":", "")))
|
self.agent_id = '%s%s' % ('ovs', (mac.replace(":", "")))
|
||||||
|
@ -346,8 +341,8 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
self.local_vlan_map):
|
self.local_vlan_map):
|
||||||
agent_ports.pop(self.local_ip, None)
|
agent_ports.pop(self.local_ip, None)
|
||||||
if len(agent_ports):
|
if len(agent_ports):
|
||||||
self.fdb_add_tun(context, self.tun_br, lvm, agent_ports,
|
self.fdb_add_tun(context, self.int_br, lvm, agent_ports,
|
||||||
self.tun_br_ofports)
|
self.tun_ofports)
|
||||||
|
|
||||||
def fdb_remove(self, context, fdb_entries):
|
def fdb_remove(self, context, fdb_entries):
|
||||||
LOG.debug("fdb_remove received")
|
LOG.debug("fdb_remove received")
|
||||||
|
@ -355,86 +350,40 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
self.local_vlan_map):
|
self.local_vlan_map):
|
||||||
agent_ports.pop(self.local_ip, None)
|
agent_ports.pop(self.local_ip, None)
|
||||||
if len(agent_ports):
|
if len(agent_ports):
|
||||||
self.fdb_remove_tun(context, self.tun_br, lvm, agent_ports,
|
self.fdb_remove_tun(context, self.int_br, lvm, agent_ports,
|
||||||
self.tun_br_ofports)
|
self.tun_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)
|
|
||||||
|
|
||||||
def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
|
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:
|
if port_info == n_const.FLOODING_ENTRY:
|
||||||
lvm.tun_ofports.add(ofport)
|
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:
|
else:
|
||||||
self.ryuapp.add_arp_table_entry(
|
self.ryuapp.add_arp_table_entry(
|
||||||
lvm.vlan, port_info[1], port_info[0])
|
lvm.vlan, port_info[1], port_info[0])
|
||||||
match = ofpp.OFPMatch(
|
br.install_tunnel_output(
|
||||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
|
tables.TUNNEL_OUT,
|
||||||
eth_dst=port_info[0])
|
lvm.vlan, lvm.segmentation_id,
|
||||||
actions = [ofpp.OFPActionPopVlan(),
|
set([ofport]), goto_next=False, eth_dst=port_info[0])
|
||||||
ofpp.OFPActionSetField(
|
|
||||||
tunnel_id=int(lvm.segmentation_id)),
|
|
||||||
ofpp.OFPActionOutput(int(ofport), 0)]
|
|
||||||
instructions = [ofpp.OFPInstructionActions(
|
|
||||||
ofp.OFPIT_APPLY_ACTIONS, actions)]
|
|
||||||
msg = ofpp.OFPFlowMod(datapath,
|
|
||||||
table_id=constants.UCAST_TO_TUN,
|
|
||||||
command=ofp.OFPFC_ADD,
|
|
||||||
priority=2,
|
|
||||||
match=match, instructions=instructions)
|
|
||||||
self.ryu_send_msg(msg)
|
|
||||||
|
|
||||||
def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
|
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:
|
if port_info == n_const.FLOODING_ENTRY:
|
||||||
lvm.tun_ofports.remove(ofport)
|
lvm.tun_ofports.remove(ofport)
|
||||||
if len(lvm.tun_ofports) > 0:
|
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:
|
else:
|
||||||
# This local vlan doesn't require any more tunelling
|
br.delete_tunnel_output(
|
||||||
match = ofpp.OFPMatch(
|
tables.TUNNEL_FLOOD[lvm.network_type],
|
||||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
|
lvm.vlan)
|
||||||
msg = ofpp.OFPFlowMod(datapath,
|
|
||||||
table_id=constants.FLOOD_TO_TUN,
|
|
||||||
command=ofp.OFPFC_DELETE,
|
|
||||||
out_group=ofp.OFPG_ANY,
|
|
||||||
out_port=ofp.OFPP_ANY,
|
|
||||||
match=match)
|
|
||||||
self.ryu_send_msg(msg)
|
|
||||||
else:
|
else:
|
||||||
self.ryuapp.del_arp_table_entry(lvm.vlan, port_info[1])
|
self.ryuapp.del_arp_table_entry(lvm.vlan, port_info[1])
|
||||||
match = ofpp.OFPMatch(
|
br.delete_tunnel_output(tables.TUNNEL_OUT,
|
||||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
|
lvm.vlan, eth_dst=port_info[0])
|
||||||
eth_dst=port_info[0])
|
|
||||||
msg = ofpp.OFPFlowMod(datapath,
|
|
||||||
table_id=constants.UCAST_TO_TUN,
|
|
||||||
command=ofp.OFPFC_DELETE,
|
|
||||||
out_group=ofp.OFPG_ANY,
|
|
||||||
out_port=ofp.OFPP_ANY,
|
|
||||||
match=match)
|
|
||||||
self.ryu_send_msg(msg)
|
|
||||||
|
|
||||||
def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address,
|
def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address,
|
||||||
ip_address):
|
ip_address):
|
||||||
|
@ -445,85 +394,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
|
|
||||||
def _fdb_chg_ip(self, context, fdb_entries):
|
def _fdb_chg_ip(self, context, fdb_entries):
|
||||||
LOG.debug("update chg_ip received")
|
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)
|
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,
|
def provision_local_vlan(self, net_uuid, network_type, physical_network,
|
||||||
segmentation_id):
|
segmentation_id):
|
||||||
"""Provisions a local VLAN.
|
"""Provisions a local VLAN.
|
||||||
|
@ -548,31 +421,25 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
|
|
||||||
if network_type in constants.TUNNEL_NETWORK_TYPES:
|
if network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||||
if self.enable_tunneling:
|
if self.enable_tunneling:
|
||||||
self._local_vlan_for_tunnel(lvid, network_type,
|
self.int_br.provision_tenant_tunnel(network_type, lvid,
|
||||||
segmentation_id)
|
segmentation_id)
|
||||||
else:
|
else:
|
||||||
LOG.error(_("Cannot provision %(network_type)s network for "
|
LOG.error(_("Cannot provision %(network_type)s network for "
|
||||||
"net-id=%(net_uuid)s - tunneling disabled"),
|
"net-id=%(net_uuid)s - tunneling disabled"),
|
||||||
{'network_type': network_type,
|
{'network_type': network_type,
|
||||||
'net_uuid': net_uuid})
|
'net_uuid': net_uuid})
|
||||||
elif network_type == p_const.TYPE_FLAT:
|
elif network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT]:
|
||||||
if physical_network in self.phys_brs:
|
if physical_network in self.int_ofports:
|
||||||
self._local_vlan_for_flat(lvid, physical_network)
|
phys_port = self.int_ofports[physical_network]
|
||||||
|
self.int_br.provision_tenant_physnet(network_type, lvid,
|
||||||
|
segmentation_id,
|
||||||
|
phys_port)
|
||||||
else:
|
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 "
|
"net-id=%(net_uuid)s - no bridge for "
|
||||||
"physical_network %(physical_network)s"),
|
"physical_network %(physical_network)s"),
|
||||||
{'net_uuid': net_uuid,
|
{'network_type': network_type,
|
||||||
'physical_network': physical_network})
|
'net_uuid': net_uuid,
|
||||||
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,
|
|
||||||
'physical_network': physical_network})
|
'physical_network': physical_network})
|
||||||
elif network_type == p_const.TYPE_LOCAL:
|
elif network_type == p_const.TYPE_LOCAL:
|
||||||
# no flows needed for local networks
|
# no flows needed for local networks
|
||||||
|
@ -583,35 +450,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
{'network_type': network_type,
|
{'network_type': network_type,
|
||||||
'net_uuid': net_uuid})
|
'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):
|
def reclaim_local_vlan(self, net_uuid):
|
||||||
"""Reclaim a local VLAN.
|
"""Reclaim a local VLAN.
|
||||||
|
|
||||||
|
@ -630,34 +468,16 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
|
|
||||||
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
|
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||||
if self.enable_tunneling:
|
if self.enable_tunneling:
|
||||||
match = self.tun_br.ofparser.OFPMatch(
|
self.int_br.reclaim_tenant_tunnel(lvm.network_type, lvm.vlan,
|
||||||
tunnel_id=int(lvm.segmentation_id))
|
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)
|
|
||||||
# Try to remove tunnel ports if not used by other networks
|
# Try to remove tunnel ports if not used by other networks
|
||||||
for ofport in lvm.tun_ofports:
|
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)
|
lvm.network_type)
|
||||||
elif lvm.network_type in (p_const.TYPE_FLAT, p_const.TYPE_VLAN):
|
elif lvm.network_type in [p_const.TYPE_FLAT, p_const.TYPE_VLAN]:
|
||||||
if lvm.physical_network in self.phys_brs:
|
phys_port = self.int_ofports[lvm.physical_network]
|
||||||
self._reclaim_local_vlan_outbound(lvm)
|
self.int_br.reclaim_tenant_physnet(lvm.network_type, lvm.vlan,
|
||||||
self._reclaim_local_vlan_inbound(lvm)
|
lvm.segmentation_id, phys_port)
|
||||||
elif lvm.network_type == p_const.TYPE_LOCAL:
|
elif lvm.network_type == p_const.TYPE_LOCAL:
|
||||||
# no flows needed for local networks
|
# no flows needed for local networks
|
||||||
pass
|
pass
|
||||||
|
@ -684,22 +504,18 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
self.provision_local_vlan(net_uuid, network_type,
|
self.provision_local_vlan(net_uuid, network_type,
|
||||||
physical_network, segmentation_id)
|
physical_network, segmentation_id)
|
||||||
lvm = self.local_vlan_map[net_uuid]
|
lvm = self.local_vlan_map[net_uuid]
|
||||||
|
|
||||||
lvm.vif_ports[port.normalized_port_name()] = port
|
lvm.vif_ports[port.normalized_port_name()] = port
|
||||||
# Do not bind a port if it's already bound
|
self.int_br.check_in_port_add_local_port(lvm.vlan, port.ofport)
|
||||||
cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
|
|
||||||
if cur_tag != str(lvm.vlan):
|
# if any of vif mac is unknown, flood unicasts as well
|
||||||
self.int_br.set_db_attribute("Port", port.port_name, "tag",
|
flood_unicast = any(map(lambda x: x.vif_mac is None,
|
||||||
str(lvm.vlan))
|
lvm.vif_ports.values()))
|
||||||
if port.ofport != -1:
|
ofports = (vp.ofport for vp in lvm.vif_ports.values())
|
||||||
match = self.int_br.ofparser.OFPMatch(in_port=port.ofport)
|
self.int_br.local_flood_update(lvm.vlan, ofports, flood_unicast)
|
||||||
msg = self.int_br.ofparser.OFPFlowMod(
|
if port.vif_mac is None:
|
||||||
self.int_br.datapath,
|
return
|
||||||
table_id=ryu_ofp13.OFPTT_ALL,
|
self.int_br.local_out_add_port(lvm.vlan, port.ofport, port.vif_mac)
|
||||||
command=ryu_ofp13.OFPFC_DELETE,
|
|
||||||
out_group=ryu_ofp13.OFPG_ANY,
|
|
||||||
out_port=ryu_ofp13.OFPP_ANY,
|
|
||||||
match=match)
|
|
||||||
self.ryu_send_msg(msg)
|
|
||||||
|
|
||||||
def port_unbound(self, vif_id, net_uuid=None):
|
def port_unbound(self, vif_id, net_uuid=None):
|
||||||
"""Unbind port.
|
"""Unbind port.
|
||||||
|
@ -718,178 +534,29 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
return
|
return
|
||||||
|
|
||||||
lvm = self.local_vlan_map[net_uuid]
|
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:
|
if not lvm.vif_ports:
|
||||||
self.reclaim_local_vlan(net_uuid)
|
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):
|
def port_dead(self, port):
|
||||||
"""Once a port has no binding, put it on the "dead vlan".
|
"""Once a port has no binding, put it on the "dead vlan".
|
||||||
|
|
||||||
:param port: a ovs_lib.VifPort object.
|
:param port: a ovs_lib.VifPort object.
|
||||||
"""
|
"""
|
||||||
# Don't kill a port if it's already dead
|
pass
|
||||||
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)
|
|
||||||
|
|
||||||
def setup_integration_br(self):
|
def setup_integration_br(self):
|
||||||
"""Setup the integration bridge.
|
"""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):
|
br = self.int_br
|
||||||
match = br.ofparser.OFPMatch(
|
br.setup_ofp()
|
||||||
in_port=int(self.patch_int_ofport))
|
br.setup_default_table()
|
||||||
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)
|
|
||||||
|
|
||||||
def _phys_br_prepare_create_veth(self, br, int_veth_name, phys_veth_name):
|
def _phys_br_prepare_create_veth(self, br, int_veth_name, phys_veth_name):
|
||||||
self.int_br.delete_port(int_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):
|
phys_veth_name, physical_network, ip_wrapper):
|
||||||
int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name,
|
int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name,
|
||||||
phys_veth_name)
|
phys_veth_name)
|
||||||
self.int_ofports[physical_network] = self.int_br.add_port(int_veth)
|
int_br = self.int_br
|
||||||
self.phys_ofports[physical_network] = br.add_port(phys_veth)
|
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)
|
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):
|
def _phys_br_enable_veth_to_pass_traffic(self, int_veth, phys_veth):
|
||||||
# enable veth to pass traffic
|
# enable veth to pass traffic
|
||||||
int_veth.link.set_up()
|
int_veth.link.set_up()
|
||||||
|
@ -939,7 +596,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
phys_veth_name,
|
phys_veth_name,
|
||||||
physical_network,
|
physical_network,
|
||||||
ip_wrapper)
|
ip_wrapper)
|
||||||
self._phys_br_block_untranslated_traffic(br, physical_network)
|
|
||||||
self._phys_br_enable_veth_to_pass_traffic(int_veth, phys_veth)
|
self._phys_br_enable_veth_to_pass_traffic(int_veth, phys_veth)
|
||||||
|
|
||||||
def setup_physical_bridges(self, bridge_mappings):
|
def setup_physical_bridges(self, bridge_mappings):
|
||||||
|
@ -967,22 +623,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
{'physical_network': physical_network,
|
{'physical_network': physical_network,
|
||||||
'bridge': bridge})
|
'bridge': bridge})
|
||||||
raise SystemExit(1)
|
raise SystemExit(1)
|
||||||
br = OVSBridge(bridge, self.root_helper, self.ryuapp)
|
br = Bridge(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)
|
|
||||||
self.phys_brs[physical_network] = br
|
self.phys_brs[physical_network] = br
|
||||||
|
|
||||||
self._phys_br_patch_physical_bridge_with_integration_bridge(
|
self._phys_br_patch_physical_bridge_with_integration_bridge(
|
||||||
|
@ -994,7 +635,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
port_info = {'current': cur_ports}
|
port_info = {'current': cur_ports}
|
||||||
if updated_ports is None:
|
if updated_ports is None:
|
||||||
updated_ports = set()
|
updated_ports = set()
|
||||||
updated_ports.update(self._find_lost_vlan_port(registered_ports))
|
|
||||||
if updated_ports:
|
if updated_ports:
|
||||||
# Some updated ports might have been removed in the
|
# Some updated ports might have been removed in the
|
||||||
# meanwhile, and therefore should not be processed.
|
# meanwhile, and therefore should not be processed.
|
||||||
|
@ -1013,33 +653,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
port_info['removed'] = registered_ports - cur_ports
|
port_info['removed'] = registered_ports - cur_ports
|
||||||
return port_info
|
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,
|
def treat_vif_port(self, vif_port, port_id, network_id, network_type,
|
||||||
physical_network, segmentation_id, admin_state_up):
|
physical_network, segmentation_id, admin_state_up):
|
||||||
if vif_port:
|
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)
|
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):
|
def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type):
|
||||||
ofport = br.add_tunnel_port(port_name,
|
ofport_str = br.add_tunnel_port(port_name,
|
||||||
remote_ip,
|
remote_ip,
|
||||||
self.local_ip,
|
self.local_ip,
|
||||||
tunnel_type,
|
tunnel_type,
|
||||||
self.vxlan_udp_port,
|
self.vxlan_udp_port,
|
||||||
self.dont_fragment)
|
self.dont_fragment)
|
||||||
ofport_int = -1
|
ofport = -1
|
||||||
try:
|
try:
|
||||||
ofport_int = int(ofport)
|
ofport = int(ofport_str)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
LOG.exception(_("ofport should have a value that can be "
|
LOG.exception(_("ofport should have a value that can be "
|
||||||
"interpreted as an integer"))
|
"interpreted as an integer"))
|
||||||
if ofport_int < 0:
|
if ofport < 0:
|
||||||
LOG.error(_("Failed to set-up %(type)s tunnel port to %(ip)s"),
|
LOG.error(_("Failed to set-up %(type)s tunnel port to %(ip)s"),
|
||||||
{'type': tunnel_type, 'ip': remote_ip})
|
{'type': tunnel_type, 'ip': remote_ip})
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
self.tun_br_ofports[tunnel_type][remote_ip] = ofport
|
self.tun_ofports[tunnel_type][remote_ip] = ofport
|
||||||
# Add flow in default table to resubmit to the right
|
br.check_in_port_add_tunnel_port(tunnel_type, ofport)
|
||||||
# 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)
|
|
||||||
return ofport
|
return ofport
|
||||||
|
|
||||||
def setup_tunnel_port(self, br, remote_ip, network_type):
|
def setup_tunnel_port(self, br, remote_ip, network_type):
|
||||||
|
@ -1100,23 +704,14 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
return ofport
|
return ofport
|
||||||
|
|
||||||
def _remove_tunnel_port(self, br, tun_ofport, tunnel_type):
|
def _remove_tunnel_port(self, br, tun_ofport, tunnel_type):
|
||||||
datapath = br.datapath
|
for remote_ip, ofport in self.tun_ofports[tunnel_type].items():
|
||||||
ofp = datapath.ofproto
|
|
||||||
ofpp = datapath.ofproto_parser
|
|
||||||
for remote_ip, ofport in self.tun_br_ofports[tunnel_type].items():
|
|
||||||
if ofport == tun_ofport:
|
if ofport == tun_ofport:
|
||||||
|
br.check_in_port_delete_port(ofport)
|
||||||
port_name = self._create_tunnel_port_name(tunnel_type,
|
port_name = self._create_tunnel_port_name(tunnel_type,
|
||||||
remote_ip)
|
remote_ip)
|
||||||
if port_name:
|
if port_name:
|
||||||
br.delete_port(port_name)
|
br.delete_port(port_name)
|
||||||
match = ofpp.OFPMatch(in_port=int(ofport))
|
self.tun_ofports[tunnel_type].pop(remote_ip, None)
|
||||||
msg = ofpp.OFPFlowMod(datapath,
|
|
||||||
command=ofp.OFPFC_DELETE,
|
|
||||||
out_group=ofp.OFPG_ANY,
|
|
||||||
out_port=ofp.OFPP_ANY,
|
|
||||||
match=match)
|
|
||||||
self.ryu_send_msg(msg)
|
|
||||||
self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
|
|
||||||
|
|
||||||
def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type):
|
def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type):
|
||||||
# Check if this tunnel port is still used
|
# Check if this tunnel port is still used
|
||||||
|
@ -1154,6 +749,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||||
if 'port_id' in details:
|
if 'port_id' in details:
|
||||||
LOG.info(_("Port %(device)s updated. Details: %(details)s"),
|
LOG.info(_("Port %(device)s updated. Details: %(details)s"),
|
||||||
{'device': device, 'details': details})
|
{'device': device, 'details': details})
|
||||||
|
port.vif_mac = details.get('mac_address')
|
||||||
self.treat_vif_port(port, details['port_id'],
|
self.treat_vif_port(port, details['port_id'],
|
||||||
details['network_id'],
|
details['network_id'],
|
||||||
details['network_type'],
|
details['network_type'],
|
||||||
|
@ -1344,7 +940,6 @@ def create_agent_config_map(config):
|
||||||
|
|
||||||
kwargs = dict(
|
kwargs = dict(
|
||||||
integ_br=config.OVS.integration_bridge,
|
integ_br=config.OVS.integration_bridge,
|
||||||
tun_br=config.OVS.tunnel_bridge,
|
|
||||||
local_ip=config.OVS.local_ip,
|
local_ip=config.OVS.local_ip,
|
||||||
bridge_mappings=bridge_mappings,
|
bridge_mappings=bridge_mappings,
|
||||||
root_helper=config.AGENT.root_helper,
|
root_helper=config.AGENT.root_helper,
|
||||||
|
|
|
@ -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)
|
|
@ -1,4 +1,5 @@
|
||||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||||
|
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# 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
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
#
|
|
||||||
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
|
|
||||||
|
|
||||||
|
|
||||||
class OFPort(object):
|
class OFPort(object):
|
||||||
|
@ -77,6 +76,10 @@ def _normalize_port_name(name):
|
||||||
|
|
||||||
|
|
||||||
class Port(OFPort):
|
class Port(OFPort):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(Port, self).__init__(*args, **kwargs)
|
||||||
|
self.vif_mac = None
|
||||||
|
|
||||||
def is_neutron_port(self):
|
def is_neutron_port(self):
|
||||||
"""Return True if the port looks like a neutron port."""
|
"""Return True if the port looks like a neutron port."""
|
||||||
return _is_neutron_port(self.port_name)
|
return _is_neutron_port(self.port_name)
|
||||||
|
|
|
@ -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()
|
|
@ -117,8 +117,10 @@ def patch_fake_oflib_of():
|
||||||
ryu_packet_mod.ethernet = ethernet
|
ryu_packet_mod.ethernet = ethernet
|
||||||
ryu_packet_mod.vlan = vlan
|
ryu_packet_mod.vlan = vlan
|
||||||
ryu_ofproto_mod = ryu_mod.ofproto
|
ryu_ofproto_mod = ryu_mod.ofproto
|
||||||
|
ether = _Mod('ryu.ofproto.ether')
|
||||||
ofp = _Mod('ryu.ofproto.ofproto_v1_3')
|
ofp = _Mod('ryu.ofproto.ofproto_v1_3')
|
||||||
ofpp = _Mod('ryu.ofproto.ofproto_v1_3_parser')
|
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 = ofp
|
||||||
ryu_ofproto_mod.ofproto_v1_3_parser = ofpp
|
ryu_ofproto_mod.ofproto_v1_3_parser = ofpp
|
||||||
ryu_app_mod = ryu_mod.app
|
ryu_app_mod = ryu_mod.app
|
||||||
|
@ -139,6 +141,7 @@ def patch_fake_oflib_of():
|
||||||
'ryu.lib.packet.ethernet': ethernet,
|
'ryu.lib.packet.ethernet': ethernet,
|
||||||
'ryu.lib.packet.vlan': vlan,
|
'ryu.lib.packet.vlan': vlan,
|
||||||
'ryu.ofproto': ryu_ofproto_mod,
|
'ryu.ofproto': ryu_ofproto_mod,
|
||||||
|
'ryu.ofproto.ether': ether,
|
||||||
'ryu.ofproto.ofproto_v1_3': ofp,
|
'ryu.ofproto.ofproto_v1_3': ofp,
|
||||||
'ryu.ofproto.ofproto_v1_3_parser': ofpp,
|
'ryu.ofproto.ofproto_v1_3_parser': ofpp,
|
||||||
'ryu.app': ryu_app_mod,
|
'ryu.app': ryu_app_mod,
|
||||||
|
|
|
@ -23,29 +23,13 @@ from neutron.tests import base
|
||||||
from neutron.tests.unit.ofagent import fake_oflib
|
from neutron.tests.unit.ofagent import fake_oflib
|
||||||
|
|
||||||
|
|
||||||
class OFAAgentTestBase(base.BaseTestCase):
|
class OFATestBase(base.BaseTestCase):
|
||||||
|
|
||||||
_AGENT_NAME = 'neutron.plugins.ofagent.agent.ofa_neutron_agent'
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.fake_oflib_of = fake_oflib.patch_fake_oflib_of()
|
self.fake_oflib_of = fake_oflib.patch_fake_oflib_of()
|
||||||
self.fake_oflib_of.start()
|
self.fake_oflib_of.start()
|
||||||
self.addCleanup(self.fake_oflib_of.stop)
|
self.addCleanup(self.fake_oflib_of.stop)
|
||||||
self.mod_agent = importutils.import_module(self._AGENT_NAME)
|
super(OFATestBase, self).setUp()
|
||||||
super(OFAAgentTestBase, self).setUp()
|
|
||||||
self.ryuapp = mock.Mock()
|
|
||||||
|
|
||||||
def setup_config(self):
|
|
||||||
cfg.CONF.set_default('firewall_driver',
|
|
||||||
'neutron.agent.firewall.NoopFirewallDriver',
|
|
||||||
group='SECURITYGROUP')
|
|
||||||
cfg.CONF.register_cli_opts([
|
|
||||||
cfg.StrOpt('ofp-listen-host', default='',
|
|
||||||
help='openflow listen host'),
|
|
||||||
cfg.IntOpt('ofp-tcp-listen-port', default=6633,
|
|
||||||
help='openflow tcp listen port')
|
|
||||||
])
|
|
||||||
cfg.CONF.set_override('root_helper', 'fake_helper', group='AGENT')
|
|
||||||
|
|
||||||
def _mk_test_dp(self, name):
|
def _mk_test_dp(self, name):
|
||||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||||
|
@ -63,3 +47,25 @@ class OFAAgentTestBase(base.BaseTestCase):
|
||||||
br.ofproto = dp.ofproto
|
br.ofproto = dp.ofproto
|
||||||
br.ofparser = dp.ofproto_parser
|
br.ofparser = dp.ofproto_parser
|
||||||
return br
|
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')
|
||||||
|
|
|
@ -19,6 +19,7 @@ import contextlib
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
from neutron.openstack.common import importutils
|
from neutron.openstack.common import importutils
|
||||||
|
import neutron.plugins.ofagent.agent.metadata as meta
|
||||||
from neutron.tests.unit.ofagent import ofa_test_base
|
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.packet_mod = mock.Mock()
|
||||||
self.proto_ethernet_mod = mock.Mock()
|
self.proto_ethernet_mod = mock.Mock()
|
||||||
self.proto_vlan_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.proto_arp_mod = mock.Mock()
|
||||||
self.fake_get_protocol = mock.Mock(return_value=self.proto_vlan_mod)
|
self.fake_get_protocol = mock.Mock(return_value=self.proto_vlan_mod)
|
||||||
self.packet_mod.get_protocol = self.fake_get_protocol
|
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 = 'test_message_data'
|
||||||
self.msg.data = self.msg_data
|
self.msg.data = self.msg_data
|
||||||
self.ev.msg = self.msg
|
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):
|
class TestArpLib(OFAAgentTestCase):
|
||||||
|
@ -81,6 +83,8 @@ class TestArpLib(OFAAgentTestCase):
|
||||||
self._fake_get_protocol_ethernet = True
|
self._fake_get_protocol_ethernet = True
|
||||||
self._fake_get_protocol_vlan = True
|
self._fake_get_protocol_vlan = True
|
||||||
self._fake_get_protocol_arp = 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):
|
def test__send_unknown_packet_no_buffer(self):
|
||||||
in_port = 3
|
in_port = 3
|
||||||
|
@ -233,14 +237,14 @@ class TestArpLib(OFAAgentTestCase):
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
def test_packet_in_handler(self):
|
def _test_packet_in_handler(self):
|
||||||
self.arplib._arp_tbl = {
|
self.arplib._arp_tbl = {
|
||||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch.object(self.arplib, '_respond_arp',
|
mock.patch.object(self.arplib, '_respond_arp',
|
||||||
return_value=True),
|
return_value=True),
|
||||||
mock.patch.object(self.arplib,
|
mock.patch.object(self.br,
|
||||||
'_add_flow_to_avoid_unknown_packet'),
|
'arp_passthrough'),
|
||||||
mock.patch.object(self.arplib,
|
mock.patch.object(self.arplib,
|
||||||
'_send_unknown_packet'),
|
'_send_unknown_packet'),
|
||||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
) 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(
|
res_arp_fn.assert_called_once_with(
|
||||||
self.datapath, self.inport,
|
self.datapath, self.inport,
|
||||||
self.arplib._arp_tbl[self.nets[0].net],
|
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.arplib._arp_tbl = {
|
||||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch.object(self.arplib, '_respond_arp',
|
mock.patch.object(self.arplib, '_respond_arp',
|
||||||
return_value=True),
|
return_value=True),
|
||||||
mock.patch.object(self.arplib,
|
mock.patch.object(self.br, 'arp_passthrough'),
|
||||||
'_add_flow_to_avoid_unknown_packet'),
|
|
||||||
mock.patch.object(self.arplib,
|
mock.patch.object(self.arplib,
|
||||||
'_send_unknown_packet'),
|
'_send_unknown_packet'),
|
||||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
) 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(send_unknown_pk_fn.call_count)
|
||||||
self.assertFalse(res_arp_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):
|
def test_packet_in_handler_non_ethernet(self):
|
||||||
self._fake_get_protocol_ethernet = False
|
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):
|
def test_packet_in_handler_non_vlan(self):
|
||||||
self._fake_get_protocol_vlan = False
|
self._fake_get_protocol_vlan = False
|
||||||
|
@ -278,7 +286,7 @@ class TestArpLib(OFAAgentTestCase):
|
||||||
|
|
||||||
def test_packet_in_handler_non_arp(self):
|
def test_packet_in_handler_non_arp(self):
|
||||||
self._fake_get_protocol_arp = False
|
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):
|
def test_packet_in_handler_unknown_network(self):
|
||||||
self.arplib._arp_tbl = {
|
self.arplib._arp_tbl = {
|
||||||
|
@ -286,20 +294,14 @@ class TestArpLib(OFAAgentTestCase):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch.object(self.arplib, '_respond_arp',
|
mock.patch.object(self.arplib, '_respond_arp',
|
||||||
return_value=False),
|
return_value=False),
|
||||||
mock.patch.object(self.arplib,
|
mock.patch.object(self.br, 'arp_passthrough'),
|
||||||
'_add_flow_to_avoid_unknown_packet'),
|
|
||||||
mock.patch.object(self.arplib,
|
mock.patch.object(self.arplib,
|
||||||
'_send_unknown_packet'),
|
'_send_unknown_packet'),
|
||||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
||||||
self.arplib.packet_in_handler(self.ev)
|
self.arplib.packet_in_handler(self.ev)
|
||||||
add_flow_fn.assert_called_once_with(
|
add_flow_fn.assert_called_once_with(
|
||||||
self.datapath,
|
network=self.nets[0].net,
|
||||||
self.datapath.ofproto_parser.OFPMatch(
|
tpa=self.proto_arp_mod.dst_ip)
|
||||||
eth_type=self.ethernet.ETH_TYPE_ARP,
|
|
||||||
vlan_vid=self.proto_vlan_mod.vid |
|
|
||||||
self.datapath.ofproto.OFPVID_PRESENT,
|
|
||||||
arp_op=self.arp.ARP_REQUEST,
|
|
||||||
arp_tpa=self.proto_arp_mod.dst_ip))
|
|
||||||
send_unknown_pk_fn.assert_called_once_with(
|
send_unknown_pk_fn.assert_called_once_with(
|
||||||
self.ev.msg, self.msg.match['in_port'],
|
self.ev.msg, self.msg.match['in_port'],
|
||||||
self.datapath.ofproto.OFPP_TABLE)
|
self.datapath.ofproto.OFPP_TABLE)
|
||||||
|
|
|
@ -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)
|
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import copy
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
import netaddr
|
import netaddr
|
||||||
|
@ -31,7 +32,6 @@ from neutron.agent.linux import utils
|
||||||
from neutron.common import constants as n_const
|
from neutron.common import constants as n_const
|
||||||
from neutron.openstack.common import importutils
|
from neutron.openstack.common import importutils
|
||||||
from neutron.plugins.common import constants as p_const
|
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
|
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])
|
[p_const.TYPE_GRE, p_const.TYPE_VXLAN])
|
||||||
|
|
||||||
|
|
||||||
class TestOFANeutronAgentOVSBridge(ofa_test_base.OFAAgentTestBase):
|
class TestOFANeutronAgentBridge(ofa_test_base.OFAAgentTestBase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestOFANeutronAgentOVSBridge, self).setUp()
|
super(TestOFANeutronAgentBridge, self).setUp()
|
||||||
self.br_name = 'bridge1'
|
self.br_name = 'bridge1'
|
||||||
self.root_helper = 'fake_helper'
|
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)
|
self.br_name, self.root_helper, self.ryuapp)
|
||||||
|
|
||||||
def test_find_datapath_id(self):
|
def test_find_datapath_id(self):
|
||||||
|
@ -205,7 +205,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
mock.patch.object(self.mod_agent.OFANeutronAgent,
|
mock.patch.object(self.mod_agent.OFANeutronAgent,
|
||||||
'setup_integration_br',
|
'setup_integration_br',
|
||||||
return_value=mock.Mock()),
|
return_value=mock.Mock()),
|
||||||
mock.patch.object(self.mod_agent.OVSBridge,
|
mock.patch.object(self.mod_agent.Bridge,
|
||||||
'get_local_port_mac',
|
'get_local_port_mac',
|
||||||
return_value='00:00:00:00:00:01'),
|
return_value='00:00:00:00:00:01'),
|
||||||
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
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.agent.sg_agent = mock.Mock()
|
||||||
self.int_dp = self._mk_test_dp('int_br')
|
self.int_dp = self._mk_test_dp('int_br')
|
||||||
self.agent.int_br.ofparser = self.int_dp.ofproto_parser
|
self.agent.int_br = self._mk_test_br('int_br')
|
||||||
self.agent.int_br.datapath = self.int_dp
|
self.agent.int_br.set_dp(self.int_dp)
|
||||||
self.agent.tun_br = self._mk_test_br('tun_br')
|
|
||||||
self.agent.phys_brs['phys-net1'] = self._mk_test_br('phys_br1')
|
self.agent.phys_brs['phys-net1'] = self._mk_test_br('phys_br1')
|
||||||
self.agent.phys_ofports['phys-net1'] = 777
|
self.agent.phys_ofports['phys-net1'] = 777
|
||||||
self.agent.int_ofports['phys-net1'] = 666
|
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)
|
tunnel_ip_hex = '%08x' % netaddr.IPAddress(tunnel_ip, version=4)
|
||||||
return '%s-%s' % (tunnel_type, tunnel_ip_hex)
|
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,
|
def mock_scan_ports(self, port_set=None, registered_ports=None,
|
||||||
updated_ports=None, port_tags_dict=None):
|
updated_ports=None, port_tags_dict=None):
|
||||||
port_tags_dict = port_tags_dict or {}
|
port_tags_dict = port_tags_dict or {}
|
||||||
|
@ -373,27 +289,6 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
updated_ports)
|
updated_ports)
|
||||||
self.assertEqual(expected, actual)
|
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):
|
def test_treat_devices_added_returns_true_for_missing_device(self):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch.object(self.agent.plugin_rpc, 'get_device_details',
|
mock.patch.object(self.agent.plugin_rpc, 'get_device_details',
|
||||||
|
@ -556,11 +451,11 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch.object(ip_lib, "device_exists"),
|
mock.patch.object(ip_lib, "device_exists"),
|
||||||
mock.patch.object(utils, "execute"),
|
mock.patch.object(utils, "execute"),
|
||||||
mock.patch.object(self.mod_agent.OVSBridge, "add_port"),
|
mock.patch.object(self.mod_agent.Bridge, "add_port"),
|
||||||
mock.patch.object(self.mod_agent.OVSBridge, "delete_port"),
|
mock.patch.object(self.mod_agent.Bridge, "delete_port"),
|
||||||
mock.patch.object(self.mod_agent.OVSBridge, "set_protocols"),
|
mock.patch.object(self.mod_agent.Bridge, "set_protocols"),
|
||||||
mock.patch.object(self.mod_agent.OVSBridge, "set_controller"),
|
mock.patch.object(self.mod_agent.Bridge, "set_controller"),
|
||||||
mock.patch.object(self.mod_agent.OVSBridge, "get_datapath_id",
|
mock.patch.object(self.mod_agent.Bridge, "get_datapath_id",
|
||||||
return_value='0xa'),
|
return_value='0xa'),
|
||||||
mock.patch.object(self.agent.int_br, "add_port"),
|
mock.patch.object(self.agent.int_br, "add_port"),
|
||||||
mock.patch.object(self.agent.int_br, "delete_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',
|
mock.call.add_veth('int-br-eth',
|
||||||
'phy-br-eth')]
|
'phy-br-eth')]
|
||||||
parent.assert_has_calls(expected_calls, any_order=False)
|
parent.assert_has_calls(expected_calls, any_order=False)
|
||||||
self.assertEqual(self.agent.int_ofports["physnet1"],
|
self.assertEqual(11, self.agent.int_ofports["physnet1"])
|
||||||
"11")
|
self.assertEqual(25, self.agent.phys_ofports["physnet1"])
|
||||||
self.assertEqual(self.agent.phys_ofports["physnet1"],
|
|
||||||
"25")
|
|
||||||
|
|
||||||
def test_port_unbound(self):
|
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
|
self.agent.enable_tunneling = True
|
||||||
lvm = mock.Mock()
|
lvm = mock.Mock()
|
||||||
lvm.network_type = "gre"
|
lvm.network_type = "gre"
|
||||||
lvm.vif_ports = {"vif1": mock.Mock()}
|
lvm.vif_ports = {"vif1": mock.Mock()}
|
||||||
self.agent.local_vlan_map["netuid12345"] = lvm
|
self.agent.local_vlan_map["netuid12345"] = lvm
|
||||||
self.agent.port_unbound("vif1", "netuid12345")
|
self.agent.port_unbound("vif1")
|
||||||
self.assertTrue(reclvl_fn.called)
|
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):
|
def _prepare_l2_pop_ofports(self):
|
||||||
LVM = collections.namedtuple('LVM', 'net, vlan, segid, ip')
|
LVM = collections.namedtuple('LVM', 'net, vlan, segid, ip')
|
||||||
self.lvms = [LVM(net='net1', vlan=11, segid='21', ip='1.1.1.1'),
|
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')]
|
LVM(net='net2', vlan=12, segid=22, ip='2.2.2.2')]
|
||||||
self.tunnel_type = 'gre'
|
self.tunnel_type = 'gre'
|
||||||
self.tun_name1 = self._create_tunnel_port_name(self.lvms[0].ip,
|
self.tun_name1 = self._create_tunnel_port_name(self.lvms[0].ip,
|
||||||
self.tunnel_type)
|
self.tunnel_type)
|
||||||
|
@ -629,31 +517,29 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
lvm1.network_type = self.tunnel_type
|
lvm1.network_type = self.tunnel_type
|
||||||
lvm1.vlan = self.lvms[0].vlan
|
lvm1.vlan = self.lvms[0].vlan
|
||||||
lvm1.segmentation_id = self.lvms[0].segid
|
lvm1.segmentation_id = self.lvms[0].segid
|
||||||
lvm1.tun_ofports = set(['1'])
|
lvm1.tun_ofports = set([1])
|
||||||
lvm2 = mock.Mock()
|
lvm2 = mock.Mock()
|
||||||
lvm2.network_type = self.tunnel_type
|
lvm2.network_type = self.tunnel_type
|
||||||
lvm2.vlan = self.lvms[1].vlan
|
lvm2.vlan = self.lvms[1].vlan
|
||||||
lvm2.segmentation_id = self.lvms[1].segid
|
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.agent.local_vlan_map = {self.lvms[0].net: lvm1,
|
||||||
self.lvms[1].net: lvm2}
|
self.lvms[1].net: lvm2}
|
||||||
self.agent.tun_br_ofports = {self.tunnel_type:
|
self.agent.tun_ofports = {self.tunnel_type:
|
||||||
{self.lvms[0].ip: '1',
|
{self.lvms[0].ip: 1,
|
||||||
self.lvms[1].ip: '2'}}
|
self.lvms[1].ip: 2}}
|
||||||
|
|
||||||
def test_fdb_ignore_network(self):
|
def test_fdb_ignore_network(self):
|
||||||
self._prepare_l2_pop_ofports()
|
self._prepare_l2_pop_ofports()
|
||||||
fdb_entry = {'net3': {}}
|
fdb_entry = {'net3': {}}
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
|
||||||
mock.patch.object(self.agent, '_setup_tunnel_port'),
|
mock.patch.object(self.agent, '_setup_tunnel_port'),
|
||||||
mock.patch.object(self.agent, 'cleanup_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.agent.fdb_add(None, fdb_entry)
|
||||||
self.assertFalse(add_tun_fn.called)
|
self.assertFalse(add_tun_fn.called)
|
||||||
self.agent.fdb_remove(None, fdb_entry)
|
self.agent.fdb_remove(None, fdb_entry)
|
||||||
self.assertFalse(clean_tun_fn.called)
|
self.assertFalse(clean_tun_fn.called)
|
||||||
self.assertFalse(ryu_send_msg_fn.called)
|
|
||||||
|
|
||||||
def test_fdb_ignore_self(self):
|
def test_fdb_ignore_self(self):
|
||||||
self._prepare_l2_pop_ofports()
|
self._prepare_l2_pop_ofports()
|
||||||
|
@ -665,13 +551,16 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
{'agent_ip':
|
{'agent_ip':
|
||||||
[['mac', 'ip'],
|
[['mac', 'ip'],
|
||||||
n_const.FLOODING_ENTRY]}}}
|
n_const.FLOODING_ENTRY]}}}
|
||||||
with mock.patch.object(self.agent.tun_br,
|
with contextlib.nested(
|
||||||
"defer_apply_on") as defer_fn:
|
mock.patch.object(self.agent.ryuapp, "add_arp_table_entry"),
|
||||||
self.agent.fdb_add(None, fdb_entry)
|
mock.patch.object(self.agent.ryuapp, "del_arp_table_entry"),
|
||||||
self.assertFalse(defer_fn.called)
|
) 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.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):
|
def test_fdb_add_flows(self):
|
||||||
self._prepare_l2_pop_ofports()
|
self._prepare_l2_pop_ofports()
|
||||||
|
@ -683,12 +572,19 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
[['mac', 'ip'],
|
[['mac', 'ip'],
|
||||||
n_const.FLOODING_ENTRY]}}}
|
n_const.FLOODING_ENTRY]}}}
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
mock.patch.object(self.agent, '_setup_tunnel_port'),
|
||||||
mock.patch.object(self.agent.tun_br, '_setup_tunnel_port'),
|
mock.patch.object(self.agent.int_br, 'install_tunnel_output'),
|
||||||
) as (ryu_send_msg_fn, add_tun_fn):
|
mock.patch.object(self.agent.int_br, 'delete_tunnel_output'),
|
||||||
add_tun_fn.return_value = '2'
|
) as (add_tun_fn, install_fn, delete_fn):
|
||||||
|
add_tun_fn.return_value = 2
|
||||||
self.agent.fdb_add(None, fdb_entry)
|
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):
|
def test_fdb_del_flows(self):
|
||||||
self._prepare_l2_pop_ofports()
|
self._prepare_l2_pop_ofports()
|
||||||
|
@ -699,10 +595,14 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
{self.lvms[1].ip:
|
{self.lvms[1].ip:
|
||||||
[['mac', 'ip'],
|
[['mac', 'ip'],
|
||||||
n_const.FLOODING_ENTRY]}}}
|
n_const.FLOODING_ENTRY]}}}
|
||||||
with mock.patch.object(self.agent,
|
with contextlib.nested(
|
||||||
'ryu_send_msg') as ryu_send_msg_fn:
|
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.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):
|
def test_fdb_add_port(self):
|
||||||
self._prepare_l2_pop_ofports()
|
self._prepare_l2_pop_ofports()
|
||||||
|
@ -713,16 +613,13 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
{'network_type': self.tunnel_type,
|
{'network_type': self.tunnel_type,
|
||||||
'segment_id': 'tun1',
|
'segment_id': 'tun1',
|
||||||
'ports': {self.lvms[0].ip: [['mac', 'ip']]}}}
|
'ports': {self.lvms[0].ip: [['mac', 'ip']]}}}
|
||||||
with contextlib.nested(
|
with mock.patch.object(self.agent, '_setup_tunnel_port') as add_tun_fn:
|
||||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
|
||||||
mock.patch.object(self.agent, '_setup_tunnel_port')
|
|
||||||
) as (ryu_send_msg_fn, add_tun_fn):
|
|
||||||
self.agent.fdb_add(None, fdb_entry)
|
self.agent.fdb_add(None, fdb_entry)
|
||||||
self.assertFalse(add_tun_fn.called)
|
self.assertFalse(add_tun_fn.called)
|
||||||
fdb_entry[self.lvms[0].net]['ports'][tunnel_ip] = [['mac', 'ip']]
|
fdb_entry[self.lvms[0].net]['ports'][tunnel_ip] = [['mac', 'ip']]
|
||||||
self.agent.fdb_add(None, fdb_entry)
|
self.agent.fdb_add(None, fdb_entry)
|
||||||
add_tun_fn.assert_called_with(
|
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):
|
def test_fdb_del_port(self):
|
||||||
self._prepare_l2_pop_ofports()
|
self._prepare_l2_pop_ofports()
|
||||||
|
@ -730,10 +627,8 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
{'network_type': self.tunnel_type,
|
{'network_type': self.tunnel_type,
|
||||||
'segment_id': 'tun2',
|
'segment_id': 'tun2',
|
||||||
'ports': {self.lvms[1].ip: [n_const.FLOODING_ENTRY]}}}
|
'ports': {self.lvms[1].ip: [n_const.FLOODING_ENTRY]}}}
|
||||||
with contextlib.nested(
|
with mock.patch.object(self.agent.int_br,
|
||||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
'delete_port') as del_port_fn:
|
||||||
mock.patch.object(self.agent.tun_br, 'delete_port')
|
|
||||||
) as (ryu_send_msg_fn, del_port_fn):
|
|
||||||
self.agent.fdb_remove(None, fdb_entry)
|
self.agent.fdb_remove(None, fdb_entry)
|
||||||
del_port_fn.assert_called_once_with(self.tun_name2)
|
del_port_fn.assert_called_once_with(self.tun_name2)
|
||||||
|
|
||||||
|
@ -744,9 +639,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||||
'segment_id': 'tun1',
|
'segment_id': 'tun1',
|
||||||
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
||||||
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
||||||
with mock.patch.multiple(self.agent,
|
with mock.patch.object(self.agent, 'setup_tunnel_port'):
|
||||||
ryu_send_msg=mock.DEFAULT,
|
|
||||||
setup_tunnel_port=mock.DEFAULT):
|
|
||||||
self.agent.fdb_add(None, fdb_entry)
|
self.agent.fdb_add(None, fdb_entry)
|
||||||
calls = [
|
calls = [
|
||||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
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',
|
'segment_id': 'tun1',
|
||||||
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
||||||
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
||||||
with mock.patch.multiple(self.agent,
|
with mock.patch.object(self.agent, 'cleanup_tunnel_port'):
|
||||||
ryu_send_msg=mock.DEFAULT,
|
|
||||||
setup_tunnel_port=mock.DEFAULT):
|
|
||||||
self.agent.fdb_remove(None, fdb_entry)
|
self.agent.fdb_remove(None, fdb_entry)
|
||||||
calls = [
|
calls = [
|
||||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
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._prepare_l2_pop_ofports()
|
||||||
self.agent.enable_tunneling = True
|
self.agent.enable_tunneling = True
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.agent.tun_br, 'cleanup_tunnel_port'
|
self.agent.int_br, 'delete_port'
|
||||||
) as clean_tun_fn:
|
) as del_port_fn:
|
||||||
self.agent.reclaim_local_vlan(self.lvms[0].net)
|
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):
|
def test_recl_lv_port_to_remove(self):
|
||||||
self._prepare_l2_pop_ofports()
|
self._prepare_l2_pop_ofports()
|
||||||
self.agent.enable_tunneling = True
|
self.agent.enable_tunneling = True
|
||||||
with contextlib.nested(
|
with mock.patch.object(self.agent.int_br,
|
||||||
mock.patch.object(self.agent.tun_br, 'delete_port'),
|
'delete_port') as del_port_fn:
|
||||||
mock.patch.object(self.agent, 'ryu_send_msg')
|
|
||||||
) as (del_port_fn, ryu_send_msg_fn):
|
|
||||||
self.agent.reclaim_local_vlan(self.lvms[1].net)
|
self.agent.reclaim_local_vlan(self.lvms[1].net)
|
||||||
del_port_fn.assert_called_once_with(self.tun_name2)
|
del_port_fn.assert_called_once_with(self.tun_name2)
|
||||||
|
|
||||||
def test__setup_tunnel_port_error_negative(self):
|
def test__setup_tunnel_port_error_negative(self):
|
||||||
with contextlib.nested(
|
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'),
|
return_value='-1'),
|
||||||
mock.patch.object(self.mod_agent.LOG, 'error')
|
mock.patch.object(self.mod_agent.LOG, 'error')
|
||||||
) as (add_tunnel_port_fn, log_error_fn):
|
) as (add_tunnel_port_fn, log_error_fn):
|
||||||
ofport = self.agent._setup_tunnel_port(
|
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(
|
add_tunnel_port_fn.assert_called_once_with(
|
||||||
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
|
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
|
||||||
self.agent.vxlan_udp_port, self.agent.dont_fragment)
|
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):
|
def test__setup_tunnel_port_error_not_int(self):
|
||||||
with contextlib.nested(
|
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),
|
return_value=None),
|
||||||
mock.patch.object(self.mod_agent.LOG, 'exception'),
|
mock.patch.object(self.mod_agent.LOG, 'exception'),
|
||||||
mock.patch.object(self.mod_agent.LOG, 'error')
|
mock.patch.object(self.mod_agent.LOG, 'error')
|
||||||
) as (add_tunnel_port_fn, log_exc_fn, log_error_fn):
|
) as (add_tunnel_port_fn, log_exc_fn, log_error_fn):
|
||||||
ofport = self.agent._setup_tunnel_port(
|
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(
|
add_tunnel_port_fn.assert_called_once_with(
|
||||||
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
|
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
|
||||||
self.agent.vxlan_udp_port, self.agent.dont_fragment)
|
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.local_ip,
|
||||||
self.agent.tunnel_types[0])
|
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):
|
def test__get_ports(self):
|
||||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||||
reply = [ofpp.OFPPortDescStatsReply(body=[ofpp.OFPPort(name='hoge',
|
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')
|
result = self.agent._get_ofport_names('hoge')
|
||||||
_get_ports.assert_called_once_with('hoge')
|
_get_ports.assert_called_once_with('hoge')
|
||||||
self.assertEqual(set(names), result)
|
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)
|
|
||||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue