Merge "ofagent: merge br-tun into br-int"
This commit is contained in:
commit
e0cba06c9e
@ -7,6 +7,21 @@ https://github.com/osrg/ryu/wiki/OpenStack
|
||||
|
||||
# -- Notes for updating from Icehouce
|
||||
|
||||
After Icehouce, most of the functionality have been folded into
|
||||
a single bridge, the integration bridge. (aka. br-int)
|
||||
The integration bridge is the only bridge which would have an
|
||||
OpenFlow connection to the embedded controller in ofagent now.
|
||||
|
||||
- ofagent no longer uses a separate bridge for tunneling.
|
||||
Please remove br-tun if you have one.
|
||||
|
||||
# ovs-vsctl del-br br-tun
|
||||
|
||||
- ofagent no longer acts as an OpenFlow controller for physical bridges.
|
||||
Please remove set-controller configuration from your physical bridges.
|
||||
|
||||
# ovs-vsctl del-controller ${PHYSICAL_BRIDGE}
|
||||
|
||||
The support of ancillary bridges has been removed after Icehouce.
|
||||
While you can still use these bridges to provide connectivity,
|
||||
neutron-ofagent-agent no longer reports port state changes (up/down)
|
||||
|
@ -20,10 +20,9 @@ from ryu.lib.packet import arp
|
||||
from ryu.lib.packet import ethernet
|
||||
from ryu.lib.packet import packet
|
||||
from ryu.lib.packet import vlan
|
||||
from ryu.ofproto import ether
|
||||
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
import neutron.plugins.ofagent.agent.metadata as meta
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
@ -45,6 +44,10 @@ class ArpLib(object):
|
||||
"""
|
||||
self.ryuapp = ryuapp
|
||||
self._arp_tbl = {}
|
||||
self.br = None
|
||||
|
||||
def set_bridge(self, br):
|
||||
self.br = br
|
||||
|
||||
def _send_arp_reply(self, datapath, port, pkt):
|
||||
LOG.debug("packet-out %s", pkt)
|
||||
@ -60,21 +63,6 @@ class ArpLib(object):
|
||||
data=data)
|
||||
ryu_api.send_msg(self.ryuapp, out)
|
||||
|
||||
def _add_flow_to_avoid_unknown_packet(self, datapath, match):
|
||||
LOG.debug("add flow to avoid an unknown packet from packet-in")
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
instructions = [ofpp.OFPInstructionGotoTable(
|
||||
table_id=constants.FLOOD_TO_TUN)]
|
||||
out = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
command=ofp.OFPFC_ADD,
|
||||
idle_timeout=5,
|
||||
priority=20,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
ryu_api.send_msg(self.ryuapp, out)
|
||||
|
||||
def _send_unknown_packet(self, msg, in_port, out_port):
|
||||
LOG.debug("unknown packet-out in-port %(in_port)s "
|
||||
"out-port %(out_port)s msg %(msg)s",
|
||||
@ -109,6 +97,7 @@ class ArpLib(object):
|
||||
pkt.add_protocol(ethernet.ethernet(ethertype=pkt_ethernet.ethertype,
|
||||
dst=pkt_ethernet.src,
|
||||
src=hw_addr))
|
||||
if pkt_vlan:
|
||||
pkt.add_protocol(vlan.vlan(cfi=pkt_vlan.cfi,
|
||||
ethertype=pkt_vlan.ethertype,
|
||||
pcp=pkt_vlan.pcp,
|
||||
@ -146,32 +135,34 @@ class ArpLib(object):
|
||||
msg = ev.msg
|
||||
LOG.debug("packet-in msg %s", msg)
|
||||
datapath = msg.datapath
|
||||
if self.br is None:
|
||||
LOG.info(_("No bridge is set"))
|
||||
return
|
||||
if self.br.datapath.id != datapath.id:
|
||||
LOG.info(_("Unknown bridge %(dpid)s ours %(ours)s"),
|
||||
{"dpid": datapath.id, "ours": self.br.datapath.id})
|
||||
return
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
port = msg.match['in_port']
|
||||
metadata = msg.match.get('metadata')
|
||||
pkt = packet.Packet(msg.data)
|
||||
LOG.info(_("packet-in dpid %(dpid)s in_port %(port)s pkt %(pkt)s"),
|
||||
{'dpid': dpid_lib.dpid_to_str(datapath.id),
|
||||
'port': port, 'pkt': pkt})
|
||||
pkt_vlan = None
|
||||
pkt_arp = None
|
||||
|
||||
if metadata is None:
|
||||
LOG.info(_("drop non tenant packet"))
|
||||
return
|
||||
network = metadata & meta.NETWORK_MASK
|
||||
pkt_ethernet = pkt.get_protocol(ethernet.ethernet)
|
||||
if not pkt_ethernet:
|
||||
LOG.info(_("non-ethernet packet"))
|
||||
else:
|
||||
LOG.info(_("drop non-ethernet packet"))
|
||||
return
|
||||
pkt_vlan = pkt.get_protocol(vlan.vlan)
|
||||
if not pkt_vlan:
|
||||
LOG.info(_("non-vlan packet"))
|
||||
if pkt_vlan:
|
||||
network = pkt_vlan.vid
|
||||
pkt_arp = pkt.get_protocol(arp.arp)
|
||||
if not pkt_arp:
|
||||
LOG.info(_("drop non-arp packet"))
|
||||
return
|
||||
else:
|
||||
# drop an unknown packet.
|
||||
LOG.info(_("drop unknown packet"))
|
||||
return
|
||||
|
||||
arptbl = self._arp_tbl.get(network)
|
||||
if arptbl:
|
||||
@ -180,11 +171,8 @@ class ArpLib(object):
|
||||
return
|
||||
else:
|
||||
LOG.info(_("unknown network %s"), network)
|
||||
|
||||
# add a flow to skip a packet-in to a controller.
|
||||
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
|
||||
vlan_vid=network | ofp.OFPVID_PRESENT,
|
||||
arp_op=arp.ARP_REQUEST,
|
||||
arp_tpa=pkt_arp.dst_ip)
|
||||
self._add_flow_to_avoid_unknown_packet(datapath, match)
|
||||
self.br.arp_passthrough(network=network, tpa=pkt_arp.dst_ip)
|
||||
# send an unknown arp packet to the table.
|
||||
self._send_unknown_packet(msg, port, ofp.OFPP_TABLE)
|
||||
|
19
neutron/plugins/ofagent/agent/constants.py
Normal file
19
neutron/plugins/ofagent/agent/constants.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
LOCAL_VLAN_MIN = 1
|
||||
LOCAL_VLAN_MAX = 0xfff
|
||||
LOCAL_VLAN_MASK = 0xfff
|
407
neutron/plugins/ofagent/agent/flows.py
Normal file
407
neutron/plugins/ofagent/agent/flows.py
Normal file
@ -0,0 +1,407 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
OpenFlow1.3 flow table for OFAgent
|
||||
|
||||
* requirements
|
||||
** plain OpenFlow 1.3. no vendor extensions.
|
||||
|
||||
* legends
|
||||
xxx: network id (agent internal use)
|
||||
yyy: segment id (vlan id, gre key, ...)
|
||||
a,b,c: tunnel port (tun_ofports, map[net_id].tun_ofports)
|
||||
i,j,k: vm port (map[net_id].vif_ports[vif_id].ofport)
|
||||
x,y,z: physical port (int_ofports)
|
||||
N: tunnel type (0 for TYPE_GRE, 1 for TYPE_xxx, ...)
|
||||
iii: unknown ip address
|
||||
uuu: unicast l2 address
|
||||
|
||||
* tables (in order)
|
||||
CHECK_IN_PORT
|
||||
TUNNEL_IN+N
|
||||
PHYS_IN
|
||||
LOCAL_IN
|
||||
ARP_PASSTHROUGH
|
||||
ARP_RESPONDER
|
||||
TUNNEL_OUT
|
||||
LOCAL_OUT
|
||||
PHYS_OUT
|
||||
TUNNEL_FLOOD+N
|
||||
PHYS_FLOOD
|
||||
LOCAL_FLOOD
|
||||
|
||||
* CHECK_IN_PORT
|
||||
|
||||
for each vm ports:
|
||||
// check_in_port_add_local_port, check_in_port_delete_port
|
||||
in_port=i, write_metadata(LOCAL|xxx),goto(LOCAL_IN)
|
||||
TYPE_GRE
|
||||
for each tunnel ports:
|
||||
// check_in_port_add_tunnel_port, check_in_port_delete_port
|
||||
in_port=a, goto(TUNNEL_IN+N)
|
||||
TYPE_VLAN
|
||||
for each networks ports:
|
||||
// provision_tenant_physnet, reclaim_tenant_physnet
|
||||
in_port=x,vlan_vid=present|yyy, write_metadata(xxx),goto(PHYS_IN)
|
||||
TYPE_FLAT
|
||||
// provision_tenant_physnet, reclaim_tenant_physnet
|
||||
in_port=x, write_metadata(xxx),goto(PHYS_IN)
|
||||
default drop
|
||||
|
||||
* TUNNEL_IN+N (per tunnel types) tunnel -> network
|
||||
|
||||
for each networks:
|
||||
// provision_tenant_tunnel, reclaim_tenant_tunnel
|
||||
tun_id=yyy, write_metadata(xxx),goto(TUNNEL_OUT)
|
||||
|
||||
default drop
|
||||
|
||||
* PHYS_IN
|
||||
default goto(TUNNEL_OUT)
|
||||
|
||||
* LOCAL_IN
|
||||
default goto(next_table)
|
||||
|
||||
* ARP_PASSTHROUGH
|
||||
for each unknown tpa:
|
||||
// arp_passthrough
|
||||
arp,arp_op=request,metadata=xxx,tpa=iii, idle_timeout=5, goto(TUNNEL_OUT)
|
||||
default goto(next_table)
|
||||
|
||||
* ARP_RESPONDER
|
||||
arp,arp_op=request, output:controller
|
||||
default goto(next_table)
|
||||
|
||||
* TUNNEL_OUT
|
||||
TYPE_GRE
|
||||
// !FLOODING_ENTRY
|
||||
// install_tunnel_output, delete_tunnel_output
|
||||
metadata=LOCAL|xxx,eth_dst=uuu set_tunnel(yyy),output:a
|
||||
|
||||
default goto(next table)
|
||||
|
||||
* LOCAL_OUT
|
||||
for each known destinations:
|
||||
// local_out_add_port, local_out_delete_port
|
||||
metadata=xxx,eth_dst=uuu output:i
|
||||
default goto(next table)
|
||||
|
||||
* PHYS_OUT
|
||||
|
||||
NOTE(yamamoto): currently this table is always empty.
|
||||
|
||||
default goto(next table)
|
||||
|
||||
* TUNNEL_FLOOD+N. (per tunnel types)
|
||||
|
||||
network -> tunnel/vlan
|
||||
output to tunnel/physical ports
|
||||
"next table" might be LOCAL_OUT
|
||||
TYPE_GRE
|
||||
for each networks:
|
||||
// FLOODING_ENTRY
|
||||
// install_tunnel_output, delete_tunnel_output
|
||||
metadata=LOCAL|xxx, set_tunnel(yyy),output:a,b,c,goto(next table)
|
||||
|
||||
default goto(next table)
|
||||
|
||||
* PHYS_FLOOD
|
||||
|
||||
TYPE_VLAN
|
||||
for each networks:
|
||||
// provision_tenant_physnet, reclaim_tenant_physnet
|
||||
metadata=LOCAL|xxx, push_vlan:0x8100,set_field:present|yyy->vlan_vid,
|
||||
output:x,pop_vlan,goto(next table)
|
||||
TYPE_FLAT
|
||||
for each networks:
|
||||
// provision_tenant_physnet, reclaim_tenant_physnet
|
||||
metadata=LOCAL|xxx, output:x,goto(next table)
|
||||
|
||||
default goto(next table)
|
||||
|
||||
* LOCAL_FLOOD
|
||||
|
||||
for each networks:
|
||||
// local_flood_update, local_flood_delete
|
||||
metadata=xxx, output:i,j,k
|
||||
or
|
||||
metadata=xxx,eth_dst=broadcast, output:i,j,k
|
||||
|
||||
default drop
|
||||
|
||||
* references
|
||||
** OVS agent https://wiki.openstack.org/wiki/Ovs-flow-logic
|
||||
*** we use metadata instead of "internal" VLANs
|
||||
*** we don't want to use NX learn action
|
||||
"""
|
||||
|
||||
from ryu.lib.packet import arp
|
||||
from ryu.ofproto import ether
|
||||
|
||||
from neutron.plugins.common import constants as p_const
|
||||
import neutron.plugins.ofagent.agent.metadata as meta
|
||||
from neutron.plugins.ofagent.agent import ofswitch
|
||||
from neutron.plugins.ofagent.agent import tables
|
||||
|
||||
|
||||
class OFAgentIntegrationBridge(ofswitch.OpenFlowSwitch):
|
||||
"""ofagent br-int specific logic."""
|
||||
|
||||
def setup_default_table(self):
|
||||
self.delete_flows()
|
||||
|
||||
self.install_default_drop(tables.CHECK_IN_PORT)
|
||||
|
||||
for t in tables.TUNNEL_IN.values():
|
||||
self.install_default_drop(t)
|
||||
self.install_default_goto(tables.PHYS_IN, tables.TUNNEL_OUT)
|
||||
self.install_default_goto_next(tables.LOCAL_IN)
|
||||
self.install_default_goto_next(tables.ARP_PASSTHROUGH)
|
||||
self.install_arp_responder(tables.ARP_RESPONDER)
|
||||
|
||||
self.install_default_goto_next(tables.TUNNEL_OUT)
|
||||
self.install_default_goto_next(tables.LOCAL_OUT)
|
||||
self.install_default_goto_next(tables.PHYS_OUT)
|
||||
|
||||
for t in tables.TUNNEL_FLOOD.values():
|
||||
self.install_default_goto_next(t)
|
||||
self.install_default_goto_next(tables.PHYS_FLOOD)
|
||||
self.install_default_drop(tables.LOCAL_FLOOD)
|
||||
|
||||
def install_arp_responder(self, table_id):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
|
||||
arp_op=arp.ARP_REQUEST)
|
||||
actions = [ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=table_id,
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
self.install_default_goto_next(table_id)
|
||||
|
||||
def install_tunnel_output(self, table_id,
|
||||
network, segmentation_id,
|
||||
ports, goto_next, **additional_matches):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network, meta.LOCAL),
|
||||
**additional_matches)
|
||||
actions = [ofpp.OFPActionSetField(tunnel_id=segmentation_id)]
|
||||
actions += [ofpp.OFPActionOutput(port=p) for p in ports]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
|
||||
]
|
||||
if goto_next:
|
||||
instructions += [
|
||||
ofpp.OFPInstructionGotoTable(table_id=table_id + 1),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=table_id,
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
def delete_tunnel_output(self, table_id,
|
||||
network, **additional_matches):
|
||||
(dp, _ofp, ofpp) = self._get_dp()
|
||||
self.delete_flows(table_id=table_id,
|
||||
metadata=meta.mk_metadata(network, meta.LOCAL),
|
||||
**additional_matches)
|
||||
|
||||
def provision_tenant_tunnel(self, network_type, network, segmentation_id):
|
||||
(dp, _ofp, ofpp) = self._get_dp()
|
||||
match = ofpp.OFPMatch(tunnel_id=segmentation_id)
|
||||
metadata = meta.mk_metadata(network)
|
||||
instructions = [
|
||||
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
|
||||
metadata_mask=metadata[1]),
|
||||
ofpp.OFPInstructionGotoTable(table_id=tables.TUNNEL_OUT),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=tables.TUNNEL_IN[network_type],
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
def reclaim_tenant_tunnel(self, network_type, network, segmentation_id):
|
||||
table_id = tables.TUNNEL_IN[network_type]
|
||||
self.delete_flows(table_id=table_id, tunnel_id=segmentation_id)
|
||||
|
||||
def provision_tenant_physnet(self, network_type, network,
|
||||
segmentation_id, phys_port):
|
||||
"""for vlan and flat."""
|
||||
assert(network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT])
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
|
||||
# inbound
|
||||
metadata = meta.mk_metadata(network)
|
||||
instructions = [
|
||||
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
|
||||
metadata_mask=metadata[1])
|
||||
]
|
||||
if network_type == p_const.TYPE_VLAN:
|
||||
vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
|
||||
match = ofpp.OFPMatch(in_port=phys_port, vlan_vid=vlan_vid)
|
||||
actions = [ofpp.OFPActionPopVlan()]
|
||||
instructions += [ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS, actions)]
|
||||
else:
|
||||
match = ofpp.OFPMatch(in_port=phys_port)
|
||||
instructions += [ofpp.OFPInstructionGotoTable(table_id=tables.PHYS_IN)]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
priority=1,
|
||||
table_id=tables.CHECK_IN_PORT,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
# outbound
|
||||
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network, meta.LOCAL))
|
||||
if network_type == p_const.TYPE_VLAN:
|
||||
actions = [
|
||||
ofpp.OFPActionPushVlan(),
|
||||
ofpp.OFPActionSetField(vlan_vid=vlan_vid),
|
||||
]
|
||||
else:
|
||||
actions = []
|
||||
actions += [ofpp.OFPActionOutput(port=phys_port)]
|
||||
if network_type == p_const.TYPE_VLAN:
|
||||
actions += [ofpp.OFPActionPopVlan()]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
|
||||
ofpp.OFPInstructionGotoTable(table_id=tables.PHYS_FLOOD + 1),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
priority=1,
|
||||
table_id=tables.PHYS_FLOOD,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
def reclaim_tenant_physnet(self, network_type, network,
|
||||
segmentation_id, phys_port):
|
||||
(_dp, ofp, _ofpp) = self._get_dp()
|
||||
vlan_vid = segmentation_id | ofp.OFPVID_PRESENT
|
||||
if network_type == p_const.TYPE_VLAN:
|
||||
self.delete_flows(table_id=tables.CHECK_IN_PORT,
|
||||
in_port=phys_port, vlan_vid=vlan_vid)
|
||||
else:
|
||||
self.delete_flows(table_id=tables.CHECK_IN_PORT,
|
||||
in_port=phys_port)
|
||||
self.delete_flows(table_id=tables.PHYS_FLOOD,
|
||||
metadata=meta.mk_metadata(network))
|
||||
|
||||
def check_in_port_add_tunnel_port(self, network_type, port):
|
||||
(dp, _ofp, ofpp) = self._get_dp()
|
||||
match = ofpp.OFPMatch(in_port=port)
|
||||
instructions = [
|
||||
ofpp.OFPInstructionGotoTable(
|
||||
table_id=tables.TUNNEL_IN[network_type])
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=tables.CHECK_IN_PORT,
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
def check_in_port_add_local_port(self, network, port):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
match = ofpp.OFPMatch(in_port=port)
|
||||
metadata = meta.mk_metadata(network, meta.LOCAL)
|
||||
instructions = [
|
||||
ofpp.OFPInstructionWriteMetadata(metadata=metadata[0],
|
||||
metadata_mask=metadata[1]),
|
||||
ofpp.OFPInstructionGotoTable(table_id=tables.LOCAL_IN),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=tables.CHECK_IN_PORT,
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
def check_in_port_delete_port(self, port):
|
||||
self.delete_flows(table_id=tables.CHECK_IN_PORT, in_port=port)
|
||||
|
||||
def local_flood_update(self, network, ports, flood_unicast):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
match_all = ofpp.OFPMatch(metadata=meta.mk_metadata(network))
|
||||
match_multicast = ofpp.OFPMatch(metadata=meta.mk_metadata(network),
|
||||
eth_dst=('01:00:00:00:00:00',
|
||||
'01:00:00:00:00:00'))
|
||||
if flood_unicast:
|
||||
match_add = match_all
|
||||
match_del = match_multicast
|
||||
else:
|
||||
match_add = match_multicast
|
||||
match_del = match_all
|
||||
actions = [ofpp.OFPActionOutput(port=p) for p in ports]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=tables.LOCAL_FLOOD,
|
||||
priority=1,
|
||||
match=match_add,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
self.delete_flows(table_id=tables.LOCAL_FLOOD, strict=True,
|
||||
priority=1, match=match_del)
|
||||
|
||||
def local_flood_delete(self, network):
|
||||
self.delete_flows(table_id=tables.LOCAL_FLOOD,
|
||||
metadata=meta.mk_metadata(network))
|
||||
|
||||
def local_out_add_port(self, network, port, mac):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network), eth_dst=mac)
|
||||
actions = [ofpp.OFPActionOutput(port=port)]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=tables.LOCAL_OUT,
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
def local_out_delete_port(self, network, mac):
|
||||
self.delete_flows(table_id=tables.LOCAL_OUT,
|
||||
metadata=meta.mk_metadata(network), eth_dst=mac)
|
||||
|
||||
def arp_passthrough(self, network, tpa):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
match = ofpp.OFPMatch(metadata=meta.mk_metadata(network),
|
||||
eth_type=ether.ETH_TYPE_ARP,
|
||||
arp_op=arp.ARP_REQUEST,
|
||||
arp_tpa=tpa)
|
||||
instructions = [
|
||||
ofpp.OFPInstructionGotoTable(table_id=tables.TUNNEL_OUT)]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=tables.ARP_PASSTHROUGH,
|
||||
priority=1,
|
||||
idle_timeout=5,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
26
neutron/plugins/ofagent/agent/metadata.py
Normal file
26
neutron/plugins/ofagent/agent/metadata.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.plugins.ofagent.agent import constants as const
|
||||
|
||||
|
||||
# metadata mask
|
||||
NETWORK_MASK = const.LOCAL_VLAN_MASK
|
||||
LOCAL = 0x10000 # the packet came from local vm ports
|
||||
|
||||
|
||||
def mk_metadata(network, flags=0):
|
||||
return (flags | network, flags | NETWORK_MASK)
|
@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# Based on openvswitch agent.
|
||||
#
|
||||
# Copyright 2011 VMware, Inc.
|
||||
@ -26,8 +27,6 @@ from ryu.base import app_manager
|
||||
from ryu.controller import handler
|
||||
from ryu.controller import ofp_event
|
||||
from ryu.lib import hub
|
||||
from ryu.lib.packet import arp
|
||||
from ryu.ofproto import ether
|
||||
from ryu.ofproto import ofproto_v1_3 as ryu_ofp13
|
||||
|
||||
from neutron.agent import l2population_rpc
|
||||
@ -45,22 +44,23 @@ from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common import loopingcall
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.ofagent.agent import arp_lib
|
||||
from neutron.plugins.ofagent.agent import constants as ofa_const
|
||||
from neutron.plugins.ofagent.agent import flows
|
||||
from neutron.plugins.ofagent.agent import ports
|
||||
from neutron.plugins.ofagent.agent import tables
|
||||
from neutron.plugins.ofagent.common import config # noqa
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
# A placeholder for dead vlans.
|
||||
DEAD_VLAN_TAG = str(n_const.MAX_VLAN_TAG + 1)
|
||||
|
||||
|
||||
# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
|
||||
# attributes set).
|
||||
class LocalVLANMapping:
|
||||
def __init__(self, vlan, network_type, physical_network, segmentation_id,
|
||||
vif_ports=None):
|
||||
assert(isinstance(vlan, (int, long)))
|
||||
if vif_ports is None:
|
||||
vif_ports = {}
|
||||
self.vlan = vlan
|
||||
@ -77,13 +77,13 @@ class LocalVLANMapping:
|
||||
self.segmentation_id))
|
||||
|
||||
|
||||
class OVSBridge(ovs_lib.OVSBridge):
|
||||
class Bridge(flows.OFAgentIntegrationBridge, ovs_lib.OVSBridge):
|
||||
def __init__(self, br_name, root_helper, ryuapp):
|
||||
super(OVSBridge, self).__init__(br_name, root_helper)
|
||||
super(Bridge, self).__init__(br_name, root_helper)
|
||||
self.datapath_id = None
|
||||
self.datapath = None
|
||||
self.ofparser = None
|
||||
self.ryuapp = ryuapp
|
||||
self.set_app(ryuapp)
|
||||
|
||||
def find_datapath_id(self):
|
||||
self.datapath_id = self.get_datapath_id()
|
||||
@ -98,7 +98,7 @@ class OVSBridge(ovs_lib.OVSBridge):
|
||||
LOG.error(_('Agent terminated!: Failed to get a datapath.'))
|
||||
raise SystemExit(1)
|
||||
time.sleep(1)
|
||||
self.ofparser = self.datapath.ofproto_parser
|
||||
self.set_dp(self.datapath)
|
||||
|
||||
def setup_ofp(self, controller_names=None,
|
||||
protocols='OpenFlow13',
|
||||
@ -162,6 +162,7 @@ class OFANeutronAgentRyuApp(app_manager.RyuApp):
|
||||
cfg.CONF.set_default('ip_lib_force_root', True)
|
||||
|
||||
agent = OFANeutronAgent(ryuapp, **agent_config)
|
||||
self.arplib.set_bridge(agent.int_br)
|
||||
|
||||
# Start everything.
|
||||
LOG.info(_("Agent initialized successfully, now running... "))
|
||||
@ -196,7 +197,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
# 1.1 Support Security Group RPC
|
||||
RPC_API_VERSION = '1.1'
|
||||
|
||||
def __init__(self, ryuapp, integ_br, tun_br, local_ip,
|
||||
def __init__(self, ryuapp, integ_br, local_ip,
|
||||
bridge_mappings, root_helper,
|
||||
polling_interval, tunnel_types=None,
|
||||
veth_mtu=None):
|
||||
@ -204,7 +205,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
|
||||
:param ryuapp: object of the ryu app.
|
||||
:param integ_br: name of the integration bridge.
|
||||
:param tun_br: name of the tunnel bridge.
|
||||
:param local_ip: local IP address of this hypervisor.
|
||||
:param bridge_mappings: mappings from physical network name to bridge.
|
||||
:param root_helper: utility to use when running shell cmds.
|
||||
@ -218,8 +218,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
self.ryuapp = ryuapp
|
||||
self.veth_mtu = veth_mtu
|
||||
self.root_helper = root_helper
|
||||
self.available_local_vlans = set(xrange(n_const.MIN_VLAN_TAG,
|
||||
n_const.MAX_VLAN_TAG))
|
||||
# TODO(yamamoto): Remove this VLAN leftover
|
||||
self.available_local_vlans = set(xrange(ofa_const.LOCAL_VLAN_MIN,
|
||||
ofa_const.LOCAL_VLAN_MAX))
|
||||
self.tunnel_types = tunnel_types or []
|
||||
self.agent_state = {
|
||||
'binary': 'neutron-ofa-agent',
|
||||
@ -235,16 +236,16 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
# Keep track of int_br's device count for use by _report_state()
|
||||
self.int_br_device_count = 0
|
||||
|
||||
self.int_br = OVSBridge(integ_br, self.root_helper, self.ryuapp)
|
||||
self.int_br = Bridge(integ_br, self.root_helper, self.ryuapp)
|
||||
# Stores port update notifications for processing in main loop
|
||||
self.updated_ports = set()
|
||||
self.setup_rpc()
|
||||
self.setup_integration_br()
|
||||
self.setup_physical_bridges(bridge_mappings)
|
||||
self.local_vlan_map = {}
|
||||
self.tun_br_ofports = {p_const.TYPE_GRE: {},
|
||||
p_const.TYPE_VXLAN: {}}
|
||||
|
||||
self.tun_ofports = {}
|
||||
for t in tables.TUNNEL_TYPES:
|
||||
self.tun_ofports[t] = {}
|
||||
self.polling_interval = polling_interval
|
||||
|
||||
self.enable_tunneling = bool(self.tunnel_types)
|
||||
@ -252,8 +253,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
self.tunnel_count = 0
|
||||
self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port
|
||||
self.dont_fragment = cfg.CONF.AGENT.dont_fragment
|
||||
if self.enable_tunneling:
|
||||
self.setup_tunnel_br(tun_br)
|
||||
|
||||
# Security group agent support
|
||||
self.sg_agent = OFASecurityGroupAgent(self.context,
|
||||
@ -281,10 +280,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
LOG.warn(_("Unable to create tunnel port. Invalid remote IP: %s"),
|
||||
ip_address)
|
||||
|
||||
def ryu_send_msg(self, msg):
|
||||
result = ryu_api.send_msg(self.ryuapp, msg)
|
||||
LOG.info(_("ryu send_msg() result: %s"), result)
|
||||
|
||||
def setup_rpc(self):
|
||||
mac = self.int_br.get_local_port_mac()
|
||||
self.agent_id = '%s%s' % ('ovs', (mac.replace(":", "")))
|
||||
@ -346,8 +341,8 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
self.local_vlan_map):
|
||||
agent_ports.pop(self.local_ip, None)
|
||||
if len(agent_ports):
|
||||
self.fdb_add_tun(context, self.tun_br, lvm, agent_ports,
|
||||
self.tun_br_ofports)
|
||||
self.fdb_add_tun(context, self.int_br, lvm, agent_ports,
|
||||
self.tun_ofports)
|
||||
|
||||
def fdb_remove(self, context, fdb_entries):
|
||||
LOG.debug("fdb_remove received")
|
||||
@ -355,86 +350,40 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
self.local_vlan_map):
|
||||
agent_ports.pop(self.local_ip, None)
|
||||
if len(agent_ports):
|
||||
self.fdb_remove_tun(context, self.tun_br, lvm, agent_ports,
|
||||
self.tun_br_ofports)
|
||||
|
||||
def _add_fdb_flooding_flow(self, br, lvm):
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
match = ofpp.OFPMatch(
|
||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
|
||||
actions = [ofpp.OFPActionPopVlan(),
|
||||
ofpp.OFPActionSetField(
|
||||
tunnel_id=int(lvm.segmentation_id))]
|
||||
for tun_ofport in lvm.tun_ofports:
|
||||
actions.append(ofpp.OFPActionOutput(int(tun_ofport), 0))
|
||||
instructions = [ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS, actions)]
|
||||
msg = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.FLOOD_TO_TUN,
|
||||
command=ofp.OFPFC_ADD,
|
||||
priority=1,
|
||||
match=match, instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
self.fdb_remove_tun(context, self.int_br, lvm, agent_ports,
|
||||
self.tun_ofports)
|
||||
|
||||
def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
if port_info == n_const.FLOODING_ENTRY:
|
||||
lvm.tun_ofports.add(ofport)
|
||||
self._add_fdb_flooding_flow(br, lvm)
|
||||
br.install_tunnel_output(
|
||||
tables.TUNNEL_FLOOD[lvm.network_type],
|
||||
lvm.vlan, lvm.segmentation_id,
|
||||
lvm.tun_ofports, goto_next=True)
|
||||
else:
|
||||
self.ryuapp.add_arp_table_entry(
|
||||
lvm.vlan, port_info[1], port_info[0])
|
||||
match = ofpp.OFPMatch(
|
||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
|
||||
eth_dst=port_info[0])
|
||||
actions = [ofpp.OFPActionPopVlan(),
|
||||
ofpp.OFPActionSetField(
|
||||
tunnel_id=int(lvm.segmentation_id)),
|
||||
ofpp.OFPActionOutput(int(ofport), 0)]
|
||||
instructions = [ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS, actions)]
|
||||
msg = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.UCAST_TO_TUN,
|
||||
command=ofp.OFPFC_ADD,
|
||||
priority=2,
|
||||
match=match, instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
br.install_tunnel_output(
|
||||
tables.TUNNEL_OUT,
|
||||
lvm.vlan, lvm.segmentation_id,
|
||||
set([ofport]), goto_next=False, eth_dst=port_info[0])
|
||||
|
||||
def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
if port_info == n_const.FLOODING_ENTRY:
|
||||
lvm.tun_ofports.remove(ofport)
|
||||
if len(lvm.tun_ofports) > 0:
|
||||
self._add_fdb_flooding_flow(br, lvm)
|
||||
br.install_tunnel_output(
|
||||
tables.TUNNEL_FLOOD[lvm.network_type],
|
||||
lvm.vlan, lvm.segmentation_id,
|
||||
lvm.tun_ofports, goto_next=True)
|
||||
else:
|
||||
# This local vlan doesn't require any more tunelling
|
||||
match = ofpp.OFPMatch(
|
||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
|
||||
msg = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.FLOOD_TO_TUN,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
br.delete_tunnel_output(
|
||||
tables.TUNNEL_FLOOD[lvm.network_type],
|
||||
lvm.vlan)
|
||||
else:
|
||||
self.ryuapp.del_arp_table_entry(lvm.vlan, port_info[1])
|
||||
match = ofpp.OFPMatch(
|
||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT,
|
||||
eth_dst=port_info[0])
|
||||
msg = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.UCAST_TO_TUN,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
br.delete_tunnel_output(tables.TUNNEL_OUT,
|
||||
lvm.vlan, eth_dst=port_info[0])
|
||||
|
||||
def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address,
|
||||
ip_address):
|
||||
@ -445,85 +394,9 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
|
||||
def _fdb_chg_ip(self, context, fdb_entries):
|
||||
LOG.debug("update chg_ip received")
|
||||
self.fdb_chg_ip_tun(context, self.tun_br, fdb_entries, self.local_ip,
|
||||
self.fdb_chg_ip_tun(context, self.int_br, fdb_entries, self.local_ip,
|
||||
self.local_vlan_map)
|
||||
|
||||
def _provision_local_vlan_inbound_for_tunnel(self, lvid, network_type,
|
||||
segmentation_id):
|
||||
br = self.tun_br
|
||||
match = br.ofparser.OFPMatch(
|
||||
tunnel_id=int(segmentation_id))
|
||||
actions = [
|
||||
br.ofparser.OFPActionPushVlan(),
|
||||
br.ofparser.OFPActionSetField(
|
||||
vlan_vid=int(lvid) | ryu_ofp13.OFPVID_PRESENT)]
|
||||
instructions = [
|
||||
br.ofparser.OFPInstructionActions(
|
||||
ryu_ofp13.OFPIT_APPLY_ACTIONS, actions),
|
||||
br.ofparser.OFPInstructionGotoTable(
|
||||
table_id=constants.LEARN_FROM_TUN)]
|
||||
msg = br.ofparser.OFPFlowMod(
|
||||
br.datapath,
|
||||
table_id=constants.TUN_TABLE[network_type],
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _local_vlan_for_tunnel(self, lvid, network_type, segmentation_id):
|
||||
self._provision_local_vlan_inbound_for_tunnel(lvid, network_type,
|
||||
segmentation_id)
|
||||
|
||||
def _provision_local_vlan_outbound(self, lvid, vlan_vid, physical_network):
|
||||
br = self.phys_brs[physical_network]
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
match = ofpp.OFPMatch(in_port=int(self.phys_ofports[physical_network]),
|
||||
vlan_vid=int(lvid) | ofp.OFPVID_PRESENT)
|
||||
if vlan_vid == ofp.OFPVID_NONE:
|
||||
actions = [ofpp.OFPActionPopVlan()]
|
||||
else:
|
||||
actions = [ofpp.OFPActionSetField(vlan_vid=vlan_vid)]
|
||||
actions += [ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0)]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(datapath, priority=4, match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _provision_local_vlan_inbound(self, lvid, vlan_vid, physical_network):
|
||||
datapath = self.int_br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
match = ofpp.OFPMatch(in_port=int(self.int_ofports[physical_network]),
|
||||
vlan_vid=vlan_vid)
|
||||
if vlan_vid == ofp.OFPVID_NONE:
|
||||
actions = [ofpp.OFPActionPushVlan()]
|
||||
else:
|
||||
actions = []
|
||||
actions += [
|
||||
ofpp.OFPActionSetField(vlan_vid=int(lvid) | ofp.OFPVID_PRESENT),
|
||||
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
|
||||
]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions),
|
||||
]
|
||||
msg = ofpp.OFPFlowMod(datapath, priority=3, match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _local_vlan_for_flat(self, lvid, physical_network):
|
||||
vlan_vid = ryu_ofp13.OFPVID_NONE
|
||||
self._provision_local_vlan_outbound(lvid, vlan_vid, physical_network)
|
||||
self._provision_local_vlan_inbound(lvid, vlan_vid, physical_network)
|
||||
|
||||
def _local_vlan_for_vlan(self, lvid, physical_network, segmentation_id):
|
||||
vlan_vid = int(segmentation_id) | ryu_ofp13.OFPVID_PRESENT
|
||||
self._provision_local_vlan_outbound(lvid, vlan_vid, physical_network)
|
||||
self._provision_local_vlan_inbound(lvid, vlan_vid, physical_network)
|
||||
|
||||
def provision_local_vlan(self, net_uuid, network_type, physical_network,
|
||||
segmentation_id):
|
||||
"""Provisions a local VLAN.
|
||||
@ -548,31 +421,25 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
|
||||
if network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
if self.enable_tunneling:
|
||||
self._local_vlan_for_tunnel(lvid, network_type,
|
||||
self.int_br.provision_tenant_tunnel(network_type, lvid,
|
||||
segmentation_id)
|
||||
else:
|
||||
LOG.error(_("Cannot provision %(network_type)s network for "
|
||||
"net-id=%(net_uuid)s - tunneling disabled"),
|
||||
{'network_type': network_type,
|
||||
'net_uuid': net_uuid})
|
||||
elif network_type == p_const.TYPE_FLAT:
|
||||
if physical_network in self.phys_brs:
|
||||
self._local_vlan_for_flat(lvid, physical_network)
|
||||
elif network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT]:
|
||||
if physical_network in self.int_ofports:
|
||||
phys_port = self.int_ofports[physical_network]
|
||||
self.int_br.provision_tenant_physnet(network_type, lvid,
|
||||
segmentation_id,
|
||||
phys_port)
|
||||
else:
|
||||
LOG.error(_("Cannot provision flat network for "
|
||||
LOG.error(_("Cannot provision %(network_type)s network for "
|
||||
"net-id=%(net_uuid)s - no bridge for "
|
||||
"physical_network %(physical_network)s"),
|
||||
{'net_uuid': net_uuid,
|
||||
'physical_network': physical_network})
|
||||
elif network_type == p_const.TYPE_VLAN:
|
||||
if physical_network in self.phys_brs:
|
||||
self._local_vlan_for_vlan(lvid, physical_network,
|
||||
segmentation_id)
|
||||
else:
|
||||
LOG.error(_("Cannot provision VLAN network for "
|
||||
"net-id=%(net_uuid)s - no bridge for "
|
||||
"physical_network %(physical_network)s"),
|
||||
{'net_uuid': net_uuid,
|
||||
{'network_type': network_type,
|
||||
'net_uuid': net_uuid,
|
||||
'physical_network': physical_network})
|
||||
elif network_type == p_const.TYPE_LOCAL:
|
||||
# no flows needed for local networks
|
||||
@ -583,35 +450,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
{'network_type': network_type,
|
||||
'net_uuid': net_uuid})
|
||||
|
||||
def _reclaim_local_vlan_outbound(self, lvm):
|
||||
br = self.phys_brs[lvm.physical_network]
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
match = ofpp.OFPMatch(
|
||||
in_port=int(self.phys_ofports[lvm.physical_network]),
|
||||
vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT)
|
||||
msg = ofpp.OFPFlowMod(datapath, table_id=ofp.OFPTT_ALL,
|
||||
command=ofp.OFPFC_DELETE, out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _reclaim_local_vlan_inbound(self, lvm):
|
||||
datapath = self.int_br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
if lvm.network_type == p_const.TYPE_FLAT:
|
||||
vid = ofp.OFPVID_NONE
|
||||
else: # p_const.TYPE_VLAN
|
||||
vid = lvm.segmentation_id | ofp.OFPVID_PRESENT
|
||||
match = ofpp.OFPMatch(
|
||||
in_port=int(self.int_ofports[lvm.physical_network]),
|
||||
vlan_vid=vid)
|
||||
msg = ofpp.OFPFlowMod(datapath, table_id=ofp.OFPTT_ALL,
|
||||
command=ofp.OFPFC_DELETE, out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def reclaim_local_vlan(self, net_uuid):
|
||||
"""Reclaim a local VLAN.
|
||||
|
||||
@ -630,34 +468,16 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
|
||||
if lvm.network_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
if self.enable_tunneling:
|
||||
match = self.tun_br.ofparser.OFPMatch(
|
||||
tunnel_id=int(lvm.segmentation_id))
|
||||
msg = self.tun_br.ofparser.OFPFlowMod(
|
||||
self.tun_br.datapath,
|
||||
table_id=constants.TUN_TABLE[lvm.network_type],
|
||||
command=ryu_ofp13.OFPFC_DELETE,
|
||||
out_group=ryu_ofp13.OFPG_ANY,
|
||||
out_port=ryu_ofp13.OFPP_ANY,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
match = self.tun_br.ofparser.OFPMatch(
|
||||
vlan_vid=int(lvm.vlan) | ryu_ofp13.OFPVID_PRESENT)
|
||||
msg = self.tun_br.ofparser.OFPFlowMod(
|
||||
self.tun_br.datapath,
|
||||
table_id=ryu_ofp13.OFPTT_ALL,
|
||||
command=ryu_ofp13.OFPFC_DELETE,
|
||||
out_group=ryu_ofp13.OFPG_ANY,
|
||||
out_port=ryu_ofp13.OFPP_ANY,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
self.int_br.reclaim_tenant_tunnel(lvm.network_type, lvm.vlan,
|
||||
lvm.segmentation_id)
|
||||
# Try to remove tunnel ports if not used by other networks
|
||||
for ofport in lvm.tun_ofports:
|
||||
self.cleanup_tunnel_port(self.tun_br, ofport,
|
||||
self.cleanup_tunnel_port(self.int_br, ofport,
|
||||
lvm.network_type)
|
||||
elif lvm.network_type in (p_const.TYPE_FLAT, p_const.TYPE_VLAN):
|
||||
if lvm.physical_network in self.phys_brs:
|
||||
self._reclaim_local_vlan_outbound(lvm)
|
||||
self._reclaim_local_vlan_inbound(lvm)
|
||||
elif lvm.network_type in [p_const.TYPE_FLAT, p_const.TYPE_VLAN]:
|
||||
phys_port = self.int_ofports[lvm.physical_network]
|
||||
self.int_br.reclaim_tenant_physnet(lvm.network_type, lvm.vlan,
|
||||
lvm.segmentation_id, phys_port)
|
||||
elif lvm.network_type == p_const.TYPE_LOCAL:
|
||||
# no flows needed for local networks
|
||||
pass
|
||||
@ -684,22 +504,18 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
self.provision_local_vlan(net_uuid, network_type,
|
||||
physical_network, segmentation_id)
|
||||
lvm = self.local_vlan_map[net_uuid]
|
||||
|
||||
lvm.vif_ports[port.normalized_port_name()] = port
|
||||
# Do not bind a port if it's already bound
|
||||
cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
|
||||
if cur_tag != str(lvm.vlan):
|
||||
self.int_br.set_db_attribute("Port", port.port_name, "tag",
|
||||
str(lvm.vlan))
|
||||
if port.ofport != -1:
|
||||
match = self.int_br.ofparser.OFPMatch(in_port=port.ofport)
|
||||
msg = self.int_br.ofparser.OFPFlowMod(
|
||||
self.int_br.datapath,
|
||||
table_id=ryu_ofp13.OFPTT_ALL,
|
||||
command=ryu_ofp13.OFPFC_DELETE,
|
||||
out_group=ryu_ofp13.OFPG_ANY,
|
||||
out_port=ryu_ofp13.OFPP_ANY,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
self.int_br.check_in_port_add_local_port(lvm.vlan, port.ofport)
|
||||
|
||||
# if any of vif mac is unknown, flood unicasts as well
|
||||
flood_unicast = any(map(lambda x: x.vif_mac is None,
|
||||
lvm.vif_ports.values()))
|
||||
ofports = (vp.ofport for vp in lvm.vif_ports.values())
|
||||
self.int_br.local_flood_update(lvm.vlan, ofports, flood_unicast)
|
||||
if port.vif_mac is None:
|
||||
return
|
||||
self.int_br.local_out_add_port(lvm.vlan, port.ofport, port.vif_mac)
|
||||
|
||||
def port_unbound(self, vif_id, net_uuid=None):
|
||||
"""Unbind port.
|
||||
@ -718,178 +534,29 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
return
|
||||
|
||||
lvm = self.local_vlan_map[net_uuid]
|
||||
lvm.vif_ports.pop(vif_id, None)
|
||||
port = lvm.vif_ports.pop(vif_id, None)
|
||||
|
||||
self.int_br.check_in_port_delete_port(port.ofport)
|
||||
if not lvm.vif_ports:
|
||||
self.reclaim_local_vlan(net_uuid)
|
||||
if port.vif_mac is None:
|
||||
return
|
||||
self.int_br.local_out_delete_port(lvm.vlan, port.vif_mac)
|
||||
|
||||
def port_dead(self, port):
|
||||
"""Once a port has no binding, put it on the "dead vlan".
|
||||
|
||||
:param port: a ovs_lib.VifPort object.
|
||||
"""
|
||||
# Don't kill a port if it's already dead
|
||||
cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag")
|
||||
if cur_tag != DEAD_VLAN_TAG:
|
||||
self.int_br.set_db_attribute("Port", port.port_name, "tag",
|
||||
DEAD_VLAN_TAG)
|
||||
match = self.int_br.ofparser.OFPMatch(in_port=port.ofport)
|
||||
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
|
||||
priority=2, match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
pass
|
||||
|
||||
def setup_integration_br(self):
|
||||
"""Setup the integration bridge.
|
||||
|
||||
Create patch ports and remove all existing flows.
|
||||
|
||||
:param bridge_name: the name of the integration bridge.
|
||||
:returns: the integration bridge
|
||||
"""
|
||||
self.int_br.setup_ofp()
|
||||
self.int_br.delete_port(cfg.CONF.OVS.int_peer_patch_port)
|
||||
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
|
||||
table_id=ryu_ofp13.OFPTT_ALL,
|
||||
command=ryu_ofp13.OFPFC_DELETE,
|
||||
out_group=ryu_ofp13.OFPG_ANY,
|
||||
out_port=ryu_ofp13.OFPP_ANY)
|
||||
self.ryu_send_msg(msg)
|
||||
# switch all traffic using L2 learning
|
||||
actions = [self.int_br.ofparser.OFPActionOutput(
|
||||
ryu_ofp13.OFPP_NORMAL, 0)]
|
||||
instructions = [self.int_br.ofparser.OFPInstructionActions(
|
||||
ryu_ofp13.OFPIT_APPLY_ACTIONS,
|
||||
actions)]
|
||||
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
|
||||
priority=1,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_sort_incoming_traffic_depend_in_port(self, br):
|
||||
match = br.ofparser.OFPMatch(
|
||||
in_port=int(self.patch_int_ofport))
|
||||
instructions = [br.ofparser.OFPInstructionGotoTable(
|
||||
table_id=constants.PATCH_LV_TO_TUN)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath, priority=0)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_output_arp_packet_to_controller(self, br):
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP,
|
||||
arp_op=arp.ARP_REQUEST)
|
||||
actions = [ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)]
|
||||
instructions = [
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)]
|
||||
msg = ofpp.OFPFlowMod(datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
priority=10,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_goto_table_ucast_unicast(self, br):
|
||||
match = br.ofparser.OFPMatch(eth_dst=('00:00:00:00:00:00',
|
||||
'01:00:00:00:00:00'))
|
||||
instructions = [br.ofparser.OFPInstructionGotoTable(
|
||||
table_id=constants.UCAST_TO_TUN)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_goto_table_flood_broad_multi_cast(self, br):
|
||||
match = br.ofparser.OFPMatch(eth_dst=('01:00:00:00:00:00',
|
||||
'01:00:00:00:00:00'))
|
||||
instructions = [br.ofparser.OFPInstructionGotoTable(
|
||||
table_id=constants.FLOOD_TO_TUN)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
table_id=constants.PATCH_LV_TO_TUN,
|
||||
priority=0,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_set_table_tun_by_tunnel_type(self, br):
|
||||
for tunnel_type in constants.TUNNEL_NETWORK_TYPES:
|
||||
msg = br.ofparser.OFPFlowMod(
|
||||
br.datapath,
|
||||
table_id=constants.TUN_TABLE[tunnel_type],
|
||||
priority=0)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_output_patch_int(self, br):
|
||||
actions = [br.ofparser.OFPActionOutput(
|
||||
int(self.patch_int_ofport), 0)]
|
||||
instructions = [br.ofparser.OFPInstructionActions(
|
||||
ryu_ofp13.OFPIT_APPLY_ACTIONS,
|
||||
actions)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
table_id=constants.LEARN_FROM_TUN,
|
||||
priority=1,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_goto_table_flood_unknown_unicast(self, br):
|
||||
instructions = [br.ofparser.OFPInstructionGotoTable(
|
||||
table_id=constants.FLOOD_TO_TUN)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
table_id=constants.UCAST_TO_TUN,
|
||||
priority=0,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _tun_br_default_drop(self, br):
|
||||
msg = br.ofparser.OFPFlowMod(
|
||||
br.datapath,
|
||||
table_id=constants.FLOOD_TO_TUN,
|
||||
priority=0)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def setup_tunnel_br(self, tun_br):
|
||||
"""Setup the tunnel bridge.
|
||||
|
||||
Creates tunnel bridge, and links it to the integration bridge
|
||||
using a patch port.
|
||||
|
||||
:param tun_br: the name of the tunnel bridge.
|
||||
"""
|
||||
self.tun_br = OVSBridge(tun_br, self.root_helper, self.ryuapp)
|
||||
self.tun_br.reset_bridge()
|
||||
self.tun_br.setup_ofp()
|
||||
self.patch_tun_ofport = self.int_br.add_patch_port(
|
||||
cfg.CONF.OVS.int_peer_patch_port, cfg.CONF.OVS.tun_peer_patch_port)
|
||||
self.patch_int_ofport = self.tun_br.add_patch_port(
|
||||
cfg.CONF.OVS.tun_peer_patch_port, cfg.CONF.OVS.int_peer_patch_port)
|
||||
if int(self.patch_tun_ofport) < 0 or int(self.patch_int_ofport) < 0:
|
||||
LOG.error(_("Failed to create OVS patch port. Cannot have "
|
||||
"tunneling enabled on this agent, since this version "
|
||||
"of OVS does not support tunnels or patch ports. "
|
||||
"Agent terminated!"))
|
||||
raise SystemExit(1)
|
||||
msg = self.tun_br.ofparser.OFPFlowMod(self.tun_br.datapath,
|
||||
table_id=ryu_ofp13.OFPTT_ALL,
|
||||
command=ryu_ofp13.OFPFC_DELETE,
|
||||
out_group=ryu_ofp13.OFPG_ANY,
|
||||
out_port=ryu_ofp13.OFPP_ANY)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
self._tun_br_sort_incoming_traffic_depend_in_port(self.tun_br)
|
||||
self._tun_br_output_arp_packet_to_controller(self.tun_br)
|
||||
self._tun_br_goto_table_ucast_unicast(self.tun_br)
|
||||
self._tun_br_goto_table_flood_broad_multi_cast(self.tun_br)
|
||||
self._tun_br_set_table_tun_by_tunnel_type(self.tun_br)
|
||||
self._tun_br_output_patch_int(self.tun_br)
|
||||
self._tun_br_goto_table_flood_unknown_unicast(self.tun_br)
|
||||
self._tun_br_default_drop(self.tun_br)
|
||||
br = self.int_br
|
||||
br.setup_ofp()
|
||||
br.setup_default_table()
|
||||
|
||||
def _phys_br_prepare_create_veth(self, br, int_veth_name, phys_veth_name):
|
||||
self.int_br.delete_port(int_veth_name)
|
||||
@ -905,21 +572,11 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
phys_veth_name, physical_network, ip_wrapper):
|
||||
int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name,
|
||||
phys_veth_name)
|
||||
self.int_ofports[physical_network] = self.int_br.add_port(int_veth)
|
||||
self.phys_ofports[physical_network] = br.add_port(phys_veth)
|
||||
int_br = self.int_br
|
||||
self.int_ofports[physical_network] = int(int_br.add_port(int_veth))
|
||||
self.phys_ofports[physical_network] = int(br.add_port(phys_veth))
|
||||
return (int_veth, phys_veth)
|
||||
|
||||
def _phys_br_block_untranslated_traffic(self, br, physical_network):
|
||||
match = self.int_br.ofparser.OFPMatch(in_port=int(
|
||||
self.int_ofports[physical_network]))
|
||||
msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath,
|
||||
priority=2, match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
match = br.ofparser.OFPMatch(in_port=int(
|
||||
self.phys_ofports[physical_network]))
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath, priority=2, match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
|
||||
def _phys_br_enable_veth_to_pass_traffic(self, int_veth, phys_veth):
|
||||
# enable veth to pass traffic
|
||||
int_veth.link.set_up()
|
||||
@ -939,7 +596,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
phys_veth_name,
|
||||
physical_network,
|
||||
ip_wrapper)
|
||||
self._phys_br_block_untranslated_traffic(br, physical_network)
|
||||
self._phys_br_enable_veth_to_pass_traffic(int_veth, phys_veth)
|
||||
|
||||
def setup_physical_bridges(self, bridge_mappings):
|
||||
@ -967,22 +623,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
{'physical_network': physical_network,
|
||||
'bridge': bridge})
|
||||
raise SystemExit(1)
|
||||
br = OVSBridge(bridge, self.root_helper, self.ryuapp)
|
||||
br.setup_ofp()
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
table_id=ryu_ofp13.OFPTT_ALL,
|
||||
command=ryu_ofp13.OFPFC_DELETE,
|
||||
out_group=ryu_ofp13.OFPG_ANY,
|
||||
out_port=ryu_ofp13.OFPP_ANY)
|
||||
self.ryu_send_msg(msg)
|
||||
actions = [br.ofparser.OFPActionOutput(ryu_ofp13.OFPP_NORMAL, 0)]
|
||||
instructions = [br.ofparser.OFPInstructionActions(
|
||||
ryu_ofp13.OFPIT_APPLY_ACTIONS,
|
||||
actions)]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
priority=1,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
br = Bridge(bridge, self.root_helper, self.ryuapp)
|
||||
self.phys_brs[physical_network] = br
|
||||
|
||||
self._phys_br_patch_physical_bridge_with_integration_bridge(
|
||||
@ -994,7 +635,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
port_info = {'current': cur_ports}
|
||||
if updated_ports is None:
|
||||
updated_ports = set()
|
||||
updated_ports.update(self._find_lost_vlan_port(registered_ports))
|
||||
if updated_ports:
|
||||
# Some updated ports might have been removed in the
|
||||
# meanwhile, and therefore should not be processed.
|
||||
@ -1013,33 +653,6 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
port_info['removed'] = registered_ports - cur_ports
|
||||
return port_info
|
||||
|
||||
def _find_lost_vlan_port(self, registered_ports):
|
||||
"""Return ports which have lost their vlan tag.
|
||||
|
||||
The returned value is a set of port ids of the ports concerned by a
|
||||
vlan tag loss.
|
||||
"""
|
||||
# TODO(yamamoto): stop using ovsdb
|
||||
# an idea is to use metadata instead of tagged vlans.
|
||||
# cf. blueprint ofagent-merge-bridges
|
||||
port_tags = self.int_br.get_port_tag_dict()
|
||||
changed_ports = set()
|
||||
for lvm in self.local_vlan_map.values():
|
||||
for port in registered_ports:
|
||||
if (
|
||||
port in lvm.vif_ports
|
||||
and port in port_tags
|
||||
and port_tags[port] != lvm.vlan
|
||||
):
|
||||
LOG.info(
|
||||
_("Port '%(port_name)s' has lost "
|
||||
"its vlan tag '%(vlan_tag)d'!"),
|
||||
{'port_name': port,
|
||||
'vlan_tag': lvm.vlan}
|
||||
)
|
||||
changed_ports.add(port)
|
||||
return changed_ports
|
||||
|
||||
def treat_vif_port(self, vif_port, port_id, network_id, network_type,
|
||||
physical_network, segmentation_id, admin_state_up):
|
||||
if vif_port:
|
||||
@ -1059,34 +672,25 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
LOG.debug(_("No VIF port for port %s defined on agent."), port_id)
|
||||
|
||||
def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type):
|
||||
ofport = br.add_tunnel_port(port_name,
|
||||
ofport_str = br.add_tunnel_port(port_name,
|
||||
remote_ip,
|
||||
self.local_ip,
|
||||
tunnel_type,
|
||||
self.vxlan_udp_port,
|
||||
self.dont_fragment)
|
||||
ofport_int = -1
|
||||
ofport = -1
|
||||
try:
|
||||
ofport_int = int(ofport)
|
||||
ofport = int(ofport_str)
|
||||
except (TypeError, ValueError):
|
||||
LOG.exception(_("ofport should have a value that can be "
|
||||
"interpreted as an integer"))
|
||||
if ofport_int < 0:
|
||||
if ofport < 0:
|
||||
LOG.error(_("Failed to set-up %(type)s tunnel port to %(ip)s"),
|
||||
{'type': tunnel_type, 'ip': remote_ip})
|
||||
return 0
|
||||
|
||||
self.tun_br_ofports[tunnel_type][remote_ip] = ofport
|
||||
# Add flow in default table to resubmit to the right
|
||||
# tunelling table (lvid will be set in the latter)
|
||||
match = br.ofparser.OFPMatch(in_port=int(ofport))
|
||||
instructions = [br.ofparser.OFPInstructionGotoTable(
|
||||
table_id=constants.TUN_TABLE[tunnel_type])]
|
||||
msg = br.ofparser.OFPFlowMod(br.datapath,
|
||||
priority=1,
|
||||
match=match,
|
||||
instructions=instructions)
|
||||
self.ryu_send_msg(msg)
|
||||
self.tun_ofports[tunnel_type][remote_ip] = ofport
|
||||
br.check_in_port_add_tunnel_port(tunnel_type, ofport)
|
||||
return ofport
|
||||
|
||||
def setup_tunnel_port(self, br, remote_ip, network_type):
|
||||
@ -1100,23 +704,14 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
return ofport
|
||||
|
||||
def _remove_tunnel_port(self, br, tun_ofport, tunnel_type):
|
||||
datapath = br.datapath
|
||||
ofp = datapath.ofproto
|
||||
ofpp = datapath.ofproto_parser
|
||||
for remote_ip, ofport in self.tun_br_ofports[tunnel_type].items():
|
||||
for remote_ip, ofport in self.tun_ofports[tunnel_type].items():
|
||||
if ofport == tun_ofport:
|
||||
br.check_in_port_delete_port(ofport)
|
||||
port_name = self._create_tunnel_port_name(tunnel_type,
|
||||
remote_ip)
|
||||
if port_name:
|
||||
br.delete_port(port_name)
|
||||
match = ofpp.OFPMatch(in_port=int(ofport))
|
||||
msg = ofpp.OFPFlowMod(datapath,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY,
|
||||
match=match)
|
||||
self.ryu_send_msg(msg)
|
||||
self.tun_br_ofports[tunnel_type].pop(remote_ip, None)
|
||||
self.tun_ofports[tunnel_type].pop(remote_ip, None)
|
||||
|
||||
def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type):
|
||||
# Check if this tunnel port is still used
|
||||
@ -1154,6 +749,7 @@ class OFANeutronAgent(n_rpc.RpcCallback,
|
||||
if 'port_id' in details:
|
||||
LOG.info(_("Port %(device)s updated. Details: %(details)s"),
|
||||
{'device': device, 'details': details})
|
||||
port.vif_mac = details.get('mac_address')
|
||||
self.treat_vif_port(port, details['port_id'],
|
||||
details['network_id'],
|
||||
details['network_type'],
|
||||
@ -1344,7 +940,6 @@ def create_agent_config_map(config):
|
||||
|
||||
kwargs = dict(
|
||||
integ_br=config.OVS.integration_bridge,
|
||||
tun_br=config.OVS.tunnel_bridge,
|
||||
local_ip=config.OVS.local_ip,
|
||||
bridge_mappings=bridge_mappings,
|
||||
root_helper=config.AGENT.root_helper,
|
||||
|
78
neutron/plugins/ofagent/agent/ofswitch.py
Normal file
78
neutron/plugins/ofagent/agent/ofswitch.py
Normal file
@ -0,0 +1,78 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from ryu.app.ofctl import api as ofctl_api
|
||||
|
||||
|
||||
class OpenFlowSwitch(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(OpenFlowSwitch, self).__init__(*args, **kwargs)
|
||||
self._dp = None
|
||||
# logically app doesn't belong here. just for convenience.
|
||||
self._app = None
|
||||
|
||||
def set_dp(self, dp):
|
||||
self._dp = dp
|
||||
|
||||
def set_app(self, app):
|
||||
self._app = app
|
||||
|
||||
def _get_dp(self):
|
||||
"""a convenient method for openflow message composers"""
|
||||
dp = self._dp
|
||||
return (dp, dp.ofproto, dp.ofproto_parser)
|
||||
|
||||
def _send_msg(self, msg):
|
||||
return ofctl_api.send_msg(self._app, msg)
|
||||
|
||||
def delete_flows(self, table_id=None, strict=False, priority=0,
|
||||
match=None, **match_kwargs):
|
||||
(dp, ofp, ofpp) = self._get_dp()
|
||||
if table_id is None:
|
||||
table_id = ofp.OFPTT_ALL
|
||||
if match is None:
|
||||
match = ofpp.OFPMatch(**match_kwargs)
|
||||
if strict:
|
||||
cmd = ofp.OFPFC_DELETE_STRICT
|
||||
else:
|
||||
cmd = ofp.OFPFC_DELETE
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
command=cmd,
|
||||
table_id=table_id,
|
||||
match=match,
|
||||
priority=priority,
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY)
|
||||
self._send_msg(msg)
|
||||
|
||||
def install_default_drop(self, table_id):
|
||||
(dp, _ofp, ofpp) = self._get_dp()
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=table_id,
|
||||
priority=0)
|
||||
self._send_msg(msg)
|
||||
|
||||
def install_default_goto(self, table_id, dest_table_id):
|
||||
(dp, _ofp, ofpp) = self._get_dp()
|
||||
instructions = [ofpp.OFPInstructionGotoTable(table_id=dest_table_id)]
|
||||
msg = ofpp.OFPFlowMod(dp,
|
||||
table_id=table_id,
|
||||
priority=0,
|
||||
instructions=instructions)
|
||||
self._send_msg(msg)
|
||||
|
||||
def install_default_goto_next(self, table_id):
|
||||
self.install_default_goto(table_id, table_id + 1)
|
@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
@ -12,8 +13,6 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# @author: YAMAMOTO Takashi, VA Linux Systems Japan K.K.
|
||||
|
||||
|
||||
class OFPort(object):
|
||||
@ -77,6 +76,10 @@ def _normalize_port_name(name):
|
||||
|
||||
|
||||
class Port(OFPort):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(Port, self).__init__(*args, **kwargs)
|
||||
self.vif_mac = None
|
||||
|
||||
def is_neutron_port(self):
|
||||
"""Return True if the port looks like a neutron port."""
|
||||
return _is_neutron_port(self.port_name)
|
||||
|
62
neutron/plugins/ofagent/agent/tables.py
Normal file
62
neutron/plugins/ofagent/agent/tables.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from neutron.plugins.common import constants as p_const
|
||||
|
||||
|
||||
def _seq():
|
||||
"""Yield sequential numbers starting from 0."""
|
||||
i = 0
|
||||
while True:
|
||||
yield i
|
||||
i += 1
|
||||
|
||||
_table_id_gen = _seq()
|
||||
|
||||
|
||||
def _table_id():
|
||||
"""A simple table id allocator."""
|
||||
return _table_id_gen.next()
|
||||
|
||||
# Supported tunnel types.
|
||||
TUNNEL_TYPES = [
|
||||
p_const.TYPE_GRE,
|
||||
p_const.TYPE_VXLAN,
|
||||
]
|
||||
|
||||
# Reversed version of TUNNEL_TYPES.
|
||||
TUNNEL_TYPE_IDX = dict((t, TUNNEL_TYPES.index(t)) for t in TUNNEL_TYPES)
|
||||
|
||||
# We use sequential table ids starting from 0.
|
||||
# We don't hardcode values here to avoid manual reassignments eg. when adding
|
||||
# a new tunnel type.
|
||||
# See a big comment in flows.py for how each tables are used.
|
||||
CHECK_IN_PORT = _table_id()
|
||||
TUNNEL_IN = {}
|
||||
for t in TUNNEL_TYPES:
|
||||
TUNNEL_IN[t] = _table_id()
|
||||
PHYS_IN = _table_id()
|
||||
LOCAL_IN = _table_id()
|
||||
ARP_PASSTHROUGH = _table_id()
|
||||
ARP_RESPONDER = _table_id()
|
||||
TUNNEL_OUT = _table_id()
|
||||
LOCAL_OUT = _table_id()
|
||||
PHYS_OUT = _table_id()
|
||||
TUNNEL_FLOOD = {}
|
||||
for t in TUNNEL_TYPES:
|
||||
TUNNEL_FLOOD[t] = _table_id()
|
||||
PHYS_FLOOD = _table_id()
|
||||
LOCAL_FLOOD = _table_id()
|
@ -117,8 +117,10 @@ def patch_fake_oflib_of():
|
||||
ryu_packet_mod.ethernet = ethernet
|
||||
ryu_packet_mod.vlan = vlan
|
||||
ryu_ofproto_mod = ryu_mod.ofproto
|
||||
ether = _Mod('ryu.ofproto.ether')
|
||||
ofp = _Mod('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = _Mod('ryu.ofproto.ofproto_v1_3_parser')
|
||||
ryu_ofproto_mod.ether = ether
|
||||
ryu_ofproto_mod.ofproto_v1_3 = ofp
|
||||
ryu_ofproto_mod.ofproto_v1_3_parser = ofpp
|
||||
ryu_app_mod = ryu_mod.app
|
||||
@ -139,6 +141,7 @@ def patch_fake_oflib_of():
|
||||
'ryu.lib.packet.ethernet': ethernet,
|
||||
'ryu.lib.packet.vlan': vlan,
|
||||
'ryu.ofproto': ryu_ofproto_mod,
|
||||
'ryu.ofproto.ether': ether,
|
||||
'ryu.ofproto.ofproto_v1_3': ofp,
|
||||
'ryu.ofproto.ofproto_v1_3_parser': ofpp,
|
||||
'ryu.app': ryu_app_mod,
|
||||
|
@ -23,29 +23,13 @@ from neutron.tests import base
|
||||
from neutron.tests.unit.ofagent import fake_oflib
|
||||
|
||||
|
||||
class OFAAgentTestBase(base.BaseTestCase):
|
||||
|
||||
_AGENT_NAME = 'neutron.plugins.ofagent.agent.ofa_neutron_agent'
|
||||
class OFATestBase(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.fake_oflib_of = fake_oflib.patch_fake_oflib_of()
|
||||
self.fake_oflib_of.start()
|
||||
self.addCleanup(self.fake_oflib_of.stop)
|
||||
self.mod_agent = importutils.import_module(self._AGENT_NAME)
|
||||
super(OFAAgentTestBase, self).setUp()
|
||||
self.ryuapp = mock.Mock()
|
||||
|
||||
def setup_config(self):
|
||||
cfg.CONF.set_default('firewall_driver',
|
||||
'neutron.agent.firewall.NoopFirewallDriver',
|
||||
group='SECURITYGROUP')
|
||||
cfg.CONF.register_cli_opts([
|
||||
cfg.StrOpt('ofp-listen-host', default='',
|
||||
help='openflow listen host'),
|
||||
cfg.IntOpt('ofp-tcp-listen-port', default=6633,
|
||||
help='openflow tcp listen port')
|
||||
])
|
||||
cfg.CONF.set_override('root_helper', 'fake_helper', group='AGENT')
|
||||
super(OFATestBase, self).setUp()
|
||||
|
||||
def _mk_test_dp(self, name):
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
@ -63,3 +47,25 @@ class OFAAgentTestBase(base.BaseTestCase):
|
||||
br.ofproto = dp.ofproto
|
||||
br.ofparser = dp.ofproto_parser
|
||||
return br
|
||||
|
||||
|
||||
class OFAAgentTestBase(OFATestBase):
|
||||
|
||||
_AGENT_NAME = 'neutron.plugins.ofagent.agent.ofa_neutron_agent'
|
||||
|
||||
def setUp(self):
|
||||
super(OFAAgentTestBase, self).setUp()
|
||||
self.mod_agent = importutils.import_module(self._AGENT_NAME)
|
||||
self.ryuapp = mock.Mock()
|
||||
|
||||
def setup_config(self):
|
||||
cfg.CONF.set_default('firewall_driver',
|
||||
'neutron.agent.firewall.NoopFirewallDriver',
|
||||
group='SECURITYGROUP')
|
||||
cfg.CONF.register_cli_opts([
|
||||
cfg.StrOpt('ofp-listen-host', default='',
|
||||
help='openflow listen host'),
|
||||
cfg.IntOpt('ofp-tcp-listen-port', default=6633,
|
||||
help='openflow tcp listen port')
|
||||
])
|
||||
cfg.CONF.set_override('root_helper', 'fake_helper', group='AGENT')
|
||||
|
@ -19,6 +19,7 @@ import contextlib
|
||||
import mock
|
||||
|
||||
from neutron.openstack.common import importutils
|
||||
import neutron.plugins.ofagent.agent.metadata as meta
|
||||
from neutron.tests.unit.ofagent import ofa_test_base
|
||||
|
||||
|
||||
@ -38,7 +39,7 @@ class OFAAgentTestCase(ofa_test_base.OFAAgentTestBase):
|
||||
self.packet_mod = mock.Mock()
|
||||
self.proto_ethernet_mod = mock.Mock()
|
||||
self.proto_vlan_mod = mock.Mock()
|
||||
self.proto_vlan_mod.vid = self.nets[0].net
|
||||
self.proto_vlan_mod.vid = 999
|
||||
self.proto_arp_mod = mock.Mock()
|
||||
self.fake_get_protocol = mock.Mock(return_value=self.proto_vlan_mod)
|
||||
self.packet_mod.get_protocol = self.fake_get_protocol
|
||||
@ -67,7 +68,8 @@ class OFAAgentTestCase(ofa_test_base.OFAAgentTestBase):
|
||||
self.msg_data = 'test_message_data'
|
||||
self.msg.data = self.msg_data
|
||||
self.ev.msg = self.msg
|
||||
self.msg.match = {'in_port': self.inport}
|
||||
self.msg.match = {'in_port': self.inport,
|
||||
'metadata': meta.LOCAL | self.nets[0].net}
|
||||
|
||||
|
||||
class TestArpLib(OFAAgentTestCase):
|
||||
@ -81,6 +83,8 @@ class TestArpLib(OFAAgentTestCase):
|
||||
self._fake_get_protocol_ethernet = True
|
||||
self._fake_get_protocol_vlan = True
|
||||
self._fake_get_protocol_arp = True
|
||||
self.br = mock.Mock(datapath=self.datapath)
|
||||
self.arplib.set_bridge(self.br)
|
||||
|
||||
def test__send_unknown_packet_no_buffer(self):
|
||||
in_port = 3
|
||||
@ -233,14 +237,14 @@ class TestArpLib(OFAAgentTestCase):
|
||||
else:
|
||||
return
|
||||
|
||||
def test_packet_in_handler(self):
|
||||
def _test_packet_in_handler(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.arplib, '_respond_arp',
|
||||
return_value=True),
|
||||
mock.patch.object(self.arplib,
|
||||
'_add_flow_to_avoid_unknown_packet'),
|
||||
mock.patch.object(self.br,
|
||||
'arp_passthrough'),
|
||||
mock.patch.object(self.arplib,
|
||||
'_send_unknown_packet'),
|
||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
||||
@ -250,16 +254,17 @@ class TestArpLib(OFAAgentTestCase):
|
||||
res_arp_fn.assert_called_once_with(
|
||||
self.datapath, self.inport,
|
||||
self.arplib._arp_tbl[self.nets[0].net],
|
||||
self.proto_ethernet_mod, self.proto_vlan_mod, self.proto_arp_mod)
|
||||
self.proto_ethernet_mod,
|
||||
self.proto_vlan_mod if self._fake_get_protocol_vlan else None,
|
||||
self.proto_arp_mod)
|
||||
|
||||
def _test_packet_in_handler(self):
|
||||
def _test_packet_in_handler_drop(self):
|
||||
self.arplib._arp_tbl = {
|
||||
self.nets[0].net: {self.nets[0].ip: self.nets[0].mac}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.arplib, '_respond_arp',
|
||||
return_value=True),
|
||||
mock.patch.object(self.arplib,
|
||||
'_add_flow_to_avoid_unknown_packet'),
|
||||
mock.patch.object(self.br, 'arp_passthrough'),
|
||||
mock.patch.object(self.arplib,
|
||||
'_send_unknown_packet'),
|
||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
||||
@ -268,9 +273,12 @@ class TestArpLib(OFAAgentTestCase):
|
||||
self.assertFalse(send_unknown_pk_fn.call_count)
|
||||
self.assertFalse(res_arp_fn.call_count)
|
||||
|
||||
def test_packet_in_handler(self):
|
||||
self._test_packet_in_handler()
|
||||
|
||||
def test_packet_in_handler_non_ethernet(self):
|
||||
self._fake_get_protocol_ethernet = False
|
||||
self._test_packet_in_handler()
|
||||
self._test_packet_in_handler_drop()
|
||||
|
||||
def test_packet_in_handler_non_vlan(self):
|
||||
self._fake_get_protocol_vlan = False
|
||||
@ -278,7 +286,7 @@ class TestArpLib(OFAAgentTestCase):
|
||||
|
||||
def test_packet_in_handler_non_arp(self):
|
||||
self._fake_get_protocol_arp = False
|
||||
self._test_packet_in_handler()
|
||||
self._test_packet_in_handler_drop()
|
||||
|
||||
def test_packet_in_handler_unknown_network(self):
|
||||
self.arplib._arp_tbl = {
|
||||
@ -286,20 +294,14 @@ class TestArpLib(OFAAgentTestCase):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.arplib, '_respond_arp',
|
||||
return_value=False),
|
||||
mock.patch.object(self.arplib,
|
||||
'_add_flow_to_avoid_unknown_packet'),
|
||||
mock.patch.object(self.br, 'arp_passthrough'),
|
||||
mock.patch.object(self.arplib,
|
||||
'_send_unknown_packet'),
|
||||
) as (res_arp_fn, add_flow_fn, send_unknown_pk_fn):
|
||||
self.arplib.packet_in_handler(self.ev)
|
||||
add_flow_fn.assert_called_once_with(
|
||||
self.datapath,
|
||||
self.datapath.ofproto_parser.OFPMatch(
|
||||
eth_type=self.ethernet.ETH_TYPE_ARP,
|
||||
vlan_vid=self.proto_vlan_mod.vid |
|
||||
self.datapath.ofproto.OFPVID_PRESENT,
|
||||
arp_op=self.arp.ARP_REQUEST,
|
||||
arp_tpa=self.proto_arp_mod.dst_ip))
|
||||
network=self.nets[0].net,
|
||||
tpa=self.proto_arp_mod.dst_ip)
|
||||
send_unknown_pk_fn.assert_called_once_with(
|
||||
self.ev.msg, self.msg.match['in_port'],
|
||||
self.datapath.ofproto.OFPP_TABLE)
|
||||
|
351
neutron/tests/unit/ofagent/test_ofa_flows.py
Normal file
351
neutron/tests/unit/ofagent/test_ofa_flows.py
Normal file
@ -0,0 +1,351 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.openstack.common import importutils
|
||||
import neutron.plugins.ofagent.agent.metadata as meta
|
||||
from neutron.tests.unit.ofagent import ofa_test_base
|
||||
|
||||
|
||||
class TestOFAgentFlows(ofa_test_base.OFATestBase):
|
||||
|
||||
_MOD = 'neutron.plugins.ofagent.agent.flows'
|
||||
|
||||
def setUp(self):
|
||||
super(TestOFAgentFlows, self).setUp()
|
||||
self.mod = importutils.import_module(self._MOD)
|
||||
self.br = self.mod.OFAgentIntegrationBridge()
|
||||
self.br.set_dp(self._mk_test_dp("dp"))
|
||||
|
||||
def test_setup_default_table(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.setup_default_table()
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
arp = importutils.import_module('ryu.lib.packet.arp')
|
||||
ether = importutils.import_module('ryu.ofproto.ether')
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(), out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, priority=0, table_id=ofp.OFPTT_ALL)),
|
||||
call(ofpp.OFPFlowMod(dp, priority=0, table_id=0)),
|
||||
call(ofpp.OFPFlowMod(dp, priority=0, table_id=1)),
|
||||
call(ofpp.OFPFlowMod(dp, priority=0, table_id=2)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=7)],
|
||||
priority=0, table_id=3)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=5)],
|
||||
priority=0, table_id=4)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=6)],
|
||||
priority=0, table_id=5)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
|
||||
[ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)])],
|
||||
match=ofpp.OFPMatch(arp_op=arp.ARP_REQUEST,
|
||||
eth_type=ether.ETH_TYPE_ARP), priority=1, table_id=6)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=7)],
|
||||
priority=0, table_id=6)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=8)],
|
||||
priority=0, table_id=7)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=9)],
|
||||
priority=0, table_id=8)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=10)],
|
||||
priority=0, table_id=9)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=11)],
|
||||
priority=0, table_id=10)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=12)],
|
||||
priority=0, table_id=11)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=13)],
|
||||
priority=0, table_id=12)),
|
||||
call(ofpp.OFPFlowMod(dp, priority=0, table_id=13)),
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_install_arp_responder(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.install_arp_responder(table_id=99)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
arp = importutils.import_module('ryu.lib.packet.arp')
|
||||
ether = importutils.import_module('ryu.ofproto.ether')
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
|
||||
[ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)])],
|
||||
match=ofpp.OFPMatch(arp_op=arp.ARP_REQUEST,
|
||||
eth_type=ether.ETH_TYPE_ARP), priority=1, table_id=99)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=100)],
|
||||
priority=0, table_id=99)),
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_install_tunnel_output(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.install_tunnel_output(table_id=110, network=111,
|
||||
segmentation_id=112, ports=[113, 114],
|
||||
goto_next=True)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
|
||||
[ofpp.OFPActionSetField(tunnel_id=112),
|
||||
ofpp.OFPActionOutput(port=113),
|
||||
ofpp.OFPActionOutput(port=114)]),
|
||||
ofpp.OFPInstructionGotoTable(table_id=111)],
|
||||
match=ofpp.OFPMatch(metadata=
|
||||
meta.mk_metadata(111, meta.LOCAL)),
|
||||
priority=1, table_id=110))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_delete_tunnel_output(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.delete_tunnel_output(table_id=110, network=111)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(metadata=
|
||||
meta.mk_metadata(111, meta.LOCAL)),
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, priority=0, table_id=110))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_provision_tenant_tunnel(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.provision_tenant_tunnel(network_type="gre", network=150,
|
||||
segmentation_id=151)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionWriteMetadata(metadata=150,
|
||||
metadata_mask=meta.NETWORK_MASK),
|
||||
ofpp.OFPInstructionGotoTable(table_id=7)],
|
||||
match=ofpp.OFPMatch(tunnel_id=151), priority=1, table_id=1))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_reclaim_tenant_tunnel(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.reclaim_tenant_tunnel(network_type="gre", network=150,
|
||||
segmentation_id=151)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(tunnel_id=151), out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, priority=0, table_id=1))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_provision_tenant_physnet(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.provision_tenant_physnet(network_type="vlan", network=150,
|
||||
segmentation_id=151, phys_port=99)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionWriteMetadata(metadata=150,
|
||||
metadata_mask=meta.NETWORK_MASK),
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
|
||||
ofpp.OFPActionPopVlan()]),
|
||||
ofpp.OFPInstructionGotoTable(table_id=3)],
|
||||
match=ofpp.OFPMatch(in_port=99,
|
||||
vlan_vid=151 | ofp.OFPVID_PRESENT),
|
||||
priority=1, table_id=0)),
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, [
|
||||
ofpp.OFPActionPushVlan(),
|
||||
ofpp.OFPActionSetField(vlan_vid=151 | ofp.OFPVID_PRESENT),
|
||||
ofpp.OFPActionOutput(port=99), ofpp.OFPActionPopVlan()]),
|
||||
ofpp.OFPInstructionGotoTable(table_id=13)],
|
||||
match=ofpp.OFPMatch(metadata=
|
||||
meta.mk_metadata(150, meta.LOCAL)),
|
||||
priority=1, table_id=12))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_reclaim_tenant_physnet(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.reclaim_tenant_physnet(network_type="vlan", network=150,
|
||||
segmentation_id=151, phys_port=99)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(in_port=99,
|
||||
vlan_vid=151 | ofp.OFPVID_PRESENT),
|
||||
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=0,
|
||||
table_id=0)),
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(metadata=meta.mk_metadata(150)),
|
||||
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=0,
|
||||
table_id=12))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_check_in_port_add_tunnel_port(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.check_in_port_add_tunnel_port(network_type="gre", port=99)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp,
|
||||
instructions=[ofpp.OFPInstructionGotoTable(table_id=1)],
|
||||
match=ofpp.OFPMatch(in_port=99), priority=1, table_id=0))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_check_in_port_add_local_port(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.check_in_port_add_local_port(network=123, port=99)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionWriteMetadata(
|
||||
metadata=meta.LOCAL | 123,
|
||||
metadata_mask=meta.LOCAL | meta.NETWORK_MASK),
|
||||
ofpp.OFPInstructionGotoTable(table_id=4)],
|
||||
match=ofpp.OFPMatch(in_port=99), priority=1, table_id=0))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_check_in_port_delete_port(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.check_in_port_delete_port(port=99)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(in_port=99), out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, priority=0, table_id=0))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_local_flood_update(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.local_flood_update(network=1234, ports=[1, 2, 3],
|
||||
flood_unicast=True)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp,
|
||||
instructions=[ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS, [
|
||||
ofpp.OFPActionOutput(port=1),
|
||||
ofpp.OFPActionOutput(port=2),
|
||||
ofpp.OFPActionOutput(port=3)])],
|
||||
match=ofpp.OFPMatch(metadata=meta.mk_metadata(1234)),
|
||||
priority=1, table_id=13)),
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE_STRICT,
|
||||
match=ofpp.OFPMatch(
|
||||
eth_dst=('01:00:00:00:00:00', '01:00:00:00:00:00'),
|
||||
metadata=meta.mk_metadata(1234)),
|
||||
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=1,
|
||||
table_id=13))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_local_flood_delete(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.local_flood_delete(network=1234)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(metadata=meta.mk_metadata(1234)),
|
||||
out_group=ofp.OFPG_ANY, out_port=ofp.OFPP_ANY, priority=0,
|
||||
table_id=13))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_local_out_add_port(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.local_out_add_port(network=1234, port=7,
|
||||
mac='12:34:56:78:9a:bc')
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS,
|
||||
[ofpp.OFPActionOutput(port=7)])],
|
||||
match=ofpp.OFPMatch(eth_dst="12:34:56:78:9a:bc",
|
||||
metadata=meta.mk_metadata(1234)), priority=1, table_id=8))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_local_out_delete_port(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.local_out_delete_port(network=1234, mac='12:34:56:78:9a:bc')
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(eth_dst="12:34:56:78:9a:bc",
|
||||
metadata=meta.mk_metadata(1234)), out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, priority=0, table_id=8))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_arp_passthrough(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.arp_passthrough(network=1234, tpa='192.0.2.1')
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
arp = importutils.import_module('ryu.lib.packet.arp')
|
||||
ether = importutils.import_module('ryu.ofproto.ether')
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, idle_timeout=5,
|
||||
instructions=[ofpp.OFPInstructionGotoTable(table_id=7)],
|
||||
match=ofpp.OFPMatch(arp_op=arp.ARP_REQUEST,
|
||||
arp_tpa="192.0.2.1", eth_type=ether.ETH_TYPE_ARP,
|
||||
metadata=meta.mk_metadata(1234)), priority=1, table_id=5))
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
@ -20,6 +20,7 @@
|
||||
|
||||
import collections
|
||||
import contextlib
|
||||
import copy
|
||||
|
||||
import mock
|
||||
import netaddr
|
||||
@ -31,7 +32,6 @@ from neutron.agent.linux import utils
|
||||
from neutron.common import constants as n_const
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.openvswitch.common import constants
|
||||
from neutron.tests.unit.ofagent import ofa_test_base
|
||||
|
||||
|
||||
@ -91,13 +91,13 @@ class CreateAgentConfigMap(ofa_test_base.OFAAgentTestBase):
|
||||
[p_const.TYPE_GRE, p_const.TYPE_VXLAN])
|
||||
|
||||
|
||||
class TestOFANeutronAgentOVSBridge(ofa_test_base.OFAAgentTestBase):
|
||||
class TestOFANeutronAgentBridge(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestOFANeutronAgentOVSBridge, self).setUp()
|
||||
super(TestOFANeutronAgentBridge, self).setUp()
|
||||
self.br_name = 'bridge1'
|
||||
self.root_helper = 'fake_helper'
|
||||
self.ovs = self.mod_agent.OVSBridge(
|
||||
self.ovs = self.mod_agent.Bridge(
|
||||
self.br_name, self.root_helper, self.ryuapp)
|
||||
|
||||
def test_find_datapath_id(self):
|
||||
@ -205,7 +205,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
mock.patch.object(self.mod_agent.OFANeutronAgent,
|
||||
'setup_integration_br',
|
||||
return_value=mock.Mock()),
|
||||
mock.patch.object(self.mod_agent.OVSBridge,
|
||||
mock.patch.object(self.mod_agent.Bridge,
|
||||
'get_local_port_mac',
|
||||
return_value='00:00:00:00:00:01'),
|
||||
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
||||
@ -217,9 +217,8 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
self.agent.sg_agent = mock.Mock()
|
||||
self.int_dp = self._mk_test_dp('int_br')
|
||||
self.agent.int_br.ofparser = self.int_dp.ofproto_parser
|
||||
self.agent.int_br.datapath = self.int_dp
|
||||
self.agent.tun_br = self._mk_test_br('tun_br')
|
||||
self.agent.int_br = self._mk_test_br('int_br')
|
||||
self.agent.int_br.set_dp(self.int_dp)
|
||||
self.agent.phys_brs['phys-net1'] = self._mk_test_br('phys_br1')
|
||||
self.agent.phys_ofports['phys-net1'] = 777
|
||||
self.agent.int_ofports['phys-net1'] = 666
|
||||
@ -229,89 +228,6 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
tunnel_ip_hex = '%08x' % netaddr.IPAddress(tunnel_ip, version=4)
|
||||
return '%s-%s' % (tunnel_type, tunnel_ip_hex)
|
||||
|
||||
def _mock_port_bound(self, ofport=None, new_local_vlan=None,
|
||||
old_local_vlan=None):
|
||||
port_name = 'tap96408df7-16'
|
||||
port = _mock_port(True, port_name)
|
||||
port.ofport = ofport
|
||||
net_uuid = 'my-net-uuid'
|
||||
ofp = self.agent.int_br.datapath.ofproto
|
||||
ofpp = self.agent.int_br.datapath.ofproto_parser
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.int_br.datapath,
|
||||
match=ofpp.OFPMatch(in_port=port.ofport),
|
||||
table_id=ofp.OFPTT_ALL,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY
|
||||
)
|
||||
if old_local_vlan is not None:
|
||||
self.agent.local_vlan_map[net_uuid] = (
|
||||
self.mod_agent.LocalVLANMapping(
|
||||
old_local_vlan, None, None, None))
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.mod_agent.OVSBridge,
|
||||
'set_db_attribute', return_value=True),
|
||||
mock.patch.object(self.mod_agent.OVSBridge,
|
||||
'db_get_val', return_value=str(old_local_vlan)),
|
||||
mock.patch.object(self.agent, 'ryu_send_msg')
|
||||
) as (set_ovs_db_func, get_ovs_db_func, ryu_send_msg_func):
|
||||
self.agent.port_bound(port, net_uuid, 'local', None, None)
|
||||
get_ovs_db_func.assert_called_once_with("Port", mock.ANY, "tag")
|
||||
if new_local_vlan != old_local_vlan:
|
||||
set_ovs_db_func.assert_called_once_with(
|
||||
"Port", mock.ANY, "tag", str(new_local_vlan))
|
||||
if ofport != -1:
|
||||
ryu_send_msg_func.assert_called_once_with(expected_msg)
|
||||
else:
|
||||
self.assertFalse(ryu_send_msg_func.called)
|
||||
else:
|
||||
self.assertFalse(set_ovs_db_func.called)
|
||||
self.assertFalse(ryu_send_msg_func.called)
|
||||
self.assertTrue(self.agent.local_vlan_map[net_uuid].
|
||||
vif_ports[port_name] is port)
|
||||
|
||||
def test_port_bound_deletes_flows_for_valid_ofport(self):
|
||||
self._mock_port_bound(ofport=1, new_local_vlan=1)
|
||||
|
||||
def test_port_bound_ignores_flows_for_invalid_ofport(self):
|
||||
self._mock_port_bound(ofport=-1, new_local_vlan=1)
|
||||
|
||||
def test_port_bound_does_not_rewire_if_already_bound(self):
|
||||
self._mock_port_bound(ofport=-1, new_local_vlan=1, old_local_vlan=1)
|
||||
|
||||
def _test_port_dead(self, cur_tag=None):
|
||||
port = mock.Mock()
|
||||
port.ofport = 1
|
||||
ofpp = self.agent.int_br.datapath.ofproto_parser
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.int_br.datapath,
|
||||
priority=2,
|
||||
match=ofpp.OFPMatch(in_port=port.ofport)
|
||||
)
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.mod_agent.OVSBridge,
|
||||
'set_db_attribute', return_value=True),
|
||||
mock.patch.object(self.mod_agent.OVSBridge,
|
||||
'db_get_val', return_value=cur_tag),
|
||||
mock.patch.object(self.agent, 'ryu_send_msg')
|
||||
) as (set_ovs_db_func, get_ovs_db_func, ryu_send_msg_func):
|
||||
self.agent.port_dead(port)
|
||||
get_ovs_db_func.assert_called_once_with("Port", mock.ANY, "tag")
|
||||
if cur_tag == self.mod_agent.DEAD_VLAN_TAG:
|
||||
self.assertFalse(set_ovs_db_func.called)
|
||||
self.assertFalse(ryu_send_msg_func.called)
|
||||
else:
|
||||
set_ovs_db_func.assert_called_once_with(
|
||||
"Port", mock.ANY, "tag", str(self.mod_agent.DEAD_VLAN_TAG))
|
||||
ryu_send_msg_func.assert_called_once_with(expected_msg)
|
||||
|
||||
def test_port_dead(self):
|
||||
self._test_port_dead()
|
||||
|
||||
def test_port_dead_with_port_already_dead(self):
|
||||
self._test_port_dead(self.mod_agent.DEAD_VLAN_TAG)
|
||||
|
||||
def mock_scan_ports(self, port_set=None, registered_ports=None,
|
||||
updated_ports=None, port_tags_dict=None):
|
||||
port_tags_dict = port_tags_dict or {}
|
||||
@ -373,27 +289,6 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
updated_ports)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_update_ports_returns_lost_vlan_port(self):
|
||||
port = mock.Mock(port_name='tap00000001-00', ofport=1)
|
||||
lvm = self.mod_agent.LocalVLANMapping(
|
||||
vlan=1, network_type='1', physical_network=None, segmentation_id=1,
|
||||
vif_ports={port.port_name: port})
|
||||
local_vlan_map = {'1': lvm}
|
||||
port_set = set(['tap00000001-00',
|
||||
'tap00000003-00'])
|
||||
registered_ports = set(['tap00000001-00', 'tap00000002-00'])
|
||||
port_tags_dict = {'tap00000001-00': []}
|
||||
expected = dict(
|
||||
added=set(['tap00000003-00']),
|
||||
current=set(['tap00000001-00', 'tap00000003-00']),
|
||||
removed=set(['tap00000002-00']),
|
||||
updated=set(['tap00000001-00'])
|
||||
)
|
||||
with mock.patch.dict(self.agent.local_vlan_map, local_vlan_map):
|
||||
actual = self.mock_scan_ports(
|
||||
port_set, registered_ports, port_tags_dict=port_tags_dict)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_treat_devices_added_returns_true_for_missing_device(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.plugin_rpc, 'get_device_details',
|
||||
@ -556,11 +451,11 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(ip_lib, "device_exists"),
|
||||
mock.patch.object(utils, "execute"),
|
||||
mock.patch.object(self.mod_agent.OVSBridge, "add_port"),
|
||||
mock.patch.object(self.mod_agent.OVSBridge, "delete_port"),
|
||||
mock.patch.object(self.mod_agent.OVSBridge, "set_protocols"),
|
||||
mock.patch.object(self.mod_agent.OVSBridge, "set_controller"),
|
||||
mock.patch.object(self.mod_agent.OVSBridge, "get_datapath_id",
|
||||
mock.patch.object(self.mod_agent.Bridge, "add_port"),
|
||||
mock.patch.object(self.mod_agent.Bridge, "delete_port"),
|
||||
mock.patch.object(self.mod_agent.Bridge, "set_protocols"),
|
||||
mock.patch.object(self.mod_agent.Bridge, "set_controller"),
|
||||
mock.patch.object(self.mod_agent.Bridge, "get_datapath_id",
|
||||
return_value='0xa'),
|
||||
mock.patch.object(self.agent.int_br, "add_port"),
|
||||
mock.patch.object(self.agent.int_br, "delete_port"),
|
||||
@ -592,34 +487,27 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
mock.call.add_veth('int-br-eth',
|
||||
'phy-br-eth')]
|
||||
parent.assert_has_calls(expected_calls, any_order=False)
|
||||
self.assertEqual(self.agent.int_ofports["physnet1"],
|
||||
"11")
|
||||
self.assertEqual(self.agent.phys_ofports["physnet1"],
|
||||
"25")
|
||||
self.assertEqual(11, self.agent.int_ofports["physnet1"])
|
||||
self.assertEqual(25, self.agent.phys_ofports["physnet1"])
|
||||
|
||||
def test_port_unbound(self):
|
||||
with mock.patch.object(self.agent, "reclaim_local_vlan") as reclvl_fn:
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent, "reclaim_local_vlan"),
|
||||
mock.patch.object(self.agent, "get_net_uuid",
|
||||
return_value="netuid12345"),
|
||||
) as (reclvl_fn, _):
|
||||
self.agent.enable_tunneling = True
|
||||
lvm = mock.Mock()
|
||||
lvm.network_type = "gre"
|
||||
lvm.vif_ports = {"vif1": mock.Mock()}
|
||||
self.agent.local_vlan_map["netuid12345"] = lvm
|
||||
self.agent.port_unbound("vif1", "netuid12345")
|
||||
self.agent.port_unbound("vif1")
|
||||
self.assertTrue(reclvl_fn.called)
|
||||
reclvl_fn.called = False
|
||||
|
||||
lvm.vif_ports = {}
|
||||
self.agent.port_unbound("vif1", "netuid12345")
|
||||
self.assertEqual(reclvl_fn.call_count, 2)
|
||||
|
||||
lvm.vif_ports = {"vif1": mock.Mock()}
|
||||
self.agent.port_unbound("vif3", "netuid12345")
|
||||
self.assertEqual(reclvl_fn.call_count, 2)
|
||||
|
||||
def _prepare_l2_pop_ofports(self):
|
||||
LVM = collections.namedtuple('LVM', 'net, vlan, segid, ip')
|
||||
self.lvms = [LVM(net='net1', vlan=11, segid='21', ip='1.1.1.1'),
|
||||
LVM(net='net2', vlan=12, segid='22', ip='2.2.2.2')]
|
||||
self.lvms = [LVM(net='net1', vlan=11, segid=21, ip='1.1.1.1'),
|
||||
LVM(net='net2', vlan=12, segid=22, ip='2.2.2.2')]
|
||||
self.tunnel_type = 'gre'
|
||||
self.tun_name1 = self._create_tunnel_port_name(self.lvms[0].ip,
|
||||
self.tunnel_type)
|
||||
@ -629,31 +517,29 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
lvm1.network_type = self.tunnel_type
|
||||
lvm1.vlan = self.lvms[0].vlan
|
||||
lvm1.segmentation_id = self.lvms[0].segid
|
||||
lvm1.tun_ofports = set(['1'])
|
||||
lvm1.tun_ofports = set([1])
|
||||
lvm2 = mock.Mock()
|
||||
lvm2.network_type = self.tunnel_type
|
||||
lvm2.vlan = self.lvms[1].vlan
|
||||
lvm2.segmentation_id = self.lvms[1].segid
|
||||
lvm2.tun_ofports = set(['1', '2'])
|
||||
lvm2.tun_ofports = set([1, 2])
|
||||
self.agent.local_vlan_map = {self.lvms[0].net: lvm1,
|
||||
self.lvms[1].net: lvm2}
|
||||
self.agent.tun_br_ofports = {self.tunnel_type:
|
||||
{self.lvms[0].ip: '1',
|
||||
self.lvms[1].ip: '2'}}
|
||||
self.agent.tun_ofports = {self.tunnel_type:
|
||||
{self.lvms[0].ip: 1,
|
||||
self.lvms[1].ip: 2}}
|
||||
|
||||
def test_fdb_ignore_network(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
fdb_entry = {'net3': {}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
||||
mock.patch.object(self.agent, '_setup_tunnel_port'),
|
||||
mock.patch.object(self.agent, 'cleanup_tunnel_port')
|
||||
) as (ryu_send_msg_fn, add_tun_fn, clean_tun_fn):
|
||||
) as (add_tun_fn, clean_tun_fn):
|
||||
self.agent.fdb_add(None, fdb_entry)
|
||||
self.assertFalse(add_tun_fn.called)
|
||||
self.agent.fdb_remove(None, fdb_entry)
|
||||
self.assertFalse(clean_tun_fn.called)
|
||||
self.assertFalse(ryu_send_msg_fn.called)
|
||||
|
||||
def test_fdb_ignore_self(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
@ -665,13 +551,16 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
{'agent_ip':
|
||||
[['mac', 'ip'],
|
||||
n_const.FLOODING_ENTRY]}}}
|
||||
with mock.patch.object(self.agent.tun_br,
|
||||
"defer_apply_on") as defer_fn:
|
||||
self.agent.fdb_add(None, fdb_entry)
|
||||
self.assertFalse(defer_fn.called)
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.ryuapp, "add_arp_table_entry"),
|
||||
mock.patch.object(self.agent.ryuapp, "del_arp_table_entry"),
|
||||
) as (add_fn, del_fn):
|
||||
self.agent.fdb_add(None, copy.deepcopy(fdb_entry))
|
||||
self.assertFalse(add_fn.called)
|
||||
self.assertFalse(del_fn.called)
|
||||
self.agent.fdb_remove(None, fdb_entry)
|
||||
self.assertFalse(defer_fn.called)
|
||||
self.assertFalse(add_fn.called)
|
||||
self.assertFalse(del_fn.called)
|
||||
|
||||
def test_fdb_add_flows(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
@ -683,12 +572,19 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
[['mac', 'ip'],
|
||||
n_const.FLOODING_ENTRY]}}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
||||
mock.patch.object(self.agent.tun_br, '_setup_tunnel_port'),
|
||||
) as (ryu_send_msg_fn, add_tun_fn):
|
||||
add_tun_fn.return_value = '2'
|
||||
mock.patch.object(self.agent, '_setup_tunnel_port'),
|
||||
mock.patch.object(self.agent.int_br, 'install_tunnel_output'),
|
||||
mock.patch.object(self.agent.int_br, 'delete_tunnel_output'),
|
||||
) as (add_tun_fn, install_fn, delete_fn):
|
||||
add_tun_fn.return_value = 2
|
||||
self.agent.fdb_add(None, fdb_entry)
|
||||
self.assertEqual(ryu_send_msg_fn.call_count, 2)
|
||||
self.assertEqual(2, install_fn.call_count)
|
||||
expected_calls = [
|
||||
mock.call(7, 11, 21, set([2]), eth_dst='mac', goto_next=False),
|
||||
mock.call(10, 11, 21, set([1, 2]), goto_next=True)
|
||||
]
|
||||
install_fn.assert_has_calls(expected_calls)
|
||||
self.assertFalse(delete_fn.called)
|
||||
|
||||
def test_fdb_del_flows(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
@ -699,10 +595,14 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
{self.lvms[1].ip:
|
||||
[['mac', 'ip'],
|
||||
n_const.FLOODING_ENTRY]}}}
|
||||
with mock.patch.object(self.agent,
|
||||
'ryu_send_msg') as ryu_send_msg_fn:
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.int_br, 'install_tunnel_output'),
|
||||
mock.patch.object(self.agent.int_br, 'delete_tunnel_output'),
|
||||
) as (install_fn, delete_fn):
|
||||
self.agent.fdb_remove(None, fdb_entry)
|
||||
self.assertEqual(ryu_send_msg_fn.call_count, 3)
|
||||
install_fn.assert_called_once_with(10, 12, 22, set([1]),
|
||||
goto_next=True)
|
||||
delete_fn.assert_called_once_with(7, 12, eth_dst='mac')
|
||||
|
||||
def test_fdb_add_port(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
@ -713,16 +613,13 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
{'network_type': self.tunnel_type,
|
||||
'segment_id': 'tun1',
|
||||
'ports': {self.lvms[0].ip: [['mac', 'ip']]}}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
||||
mock.patch.object(self.agent, '_setup_tunnel_port')
|
||||
) as (ryu_send_msg_fn, add_tun_fn):
|
||||
with mock.patch.object(self.agent, '_setup_tunnel_port') as add_tun_fn:
|
||||
self.agent.fdb_add(None, fdb_entry)
|
||||
self.assertFalse(add_tun_fn.called)
|
||||
fdb_entry[self.lvms[0].net]['ports'][tunnel_ip] = [['mac', 'ip']]
|
||||
self.agent.fdb_add(None, fdb_entry)
|
||||
add_tun_fn.assert_called_with(
|
||||
self.agent.tun_br, tun_name, tunnel_ip, self.tunnel_type)
|
||||
self.agent.int_br, tun_name, tunnel_ip, self.tunnel_type)
|
||||
|
||||
def test_fdb_del_port(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
@ -730,10 +627,8 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
{'network_type': self.tunnel_type,
|
||||
'segment_id': 'tun2',
|
||||
'ports': {self.lvms[1].ip: [n_const.FLOODING_ENTRY]}}}
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent, 'ryu_send_msg'),
|
||||
mock.patch.object(self.agent.tun_br, 'delete_port')
|
||||
) as (ryu_send_msg_fn, del_port_fn):
|
||||
with mock.patch.object(self.agent.int_br,
|
||||
'delete_port') as del_port_fn:
|
||||
self.agent.fdb_remove(None, fdb_entry)
|
||||
del_port_fn.assert_called_once_with(self.tun_name2)
|
||||
|
||||
@ -744,9 +639,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
'segment_id': 'tun1',
|
||||
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
||||
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
||||
with mock.patch.multiple(self.agent,
|
||||
ryu_send_msg=mock.DEFAULT,
|
||||
setup_tunnel_port=mock.DEFAULT):
|
||||
with mock.patch.object(self.agent, 'setup_tunnel_port'):
|
||||
self.agent.fdb_add(None, fdb_entry)
|
||||
calls = [
|
||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
||||
@ -763,9 +656,7 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
'segment_id': 'tun1',
|
||||
'ports': {self.lvms[0].ip: [['mac1', 'ip1']],
|
||||
self.lvms[1].ip: [['mac2', 'ip2']]}}}
|
||||
with mock.patch.multiple(self.agent,
|
||||
ryu_send_msg=mock.DEFAULT,
|
||||
setup_tunnel_port=mock.DEFAULT):
|
||||
with mock.patch.object(self.agent, 'cleanup_tunnel_port'):
|
||||
self.agent.fdb_remove(None, fdb_entry)
|
||||
calls = [
|
||||
mock.call(self.agent.local_vlan_map[self.lvms[0].net].vlan,
|
||||
@ -779,29 +670,27 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
self._prepare_l2_pop_ofports()
|
||||
self.agent.enable_tunneling = True
|
||||
with mock.patch.object(
|
||||
self.agent.tun_br, 'cleanup_tunnel_port'
|
||||
) as clean_tun_fn:
|
||||
self.agent.int_br, 'delete_port'
|
||||
) as del_port_fn:
|
||||
self.agent.reclaim_local_vlan(self.lvms[0].net)
|
||||
self.assertFalse(clean_tun_fn.called)
|
||||
self.assertFalse(del_port_fn.called)
|
||||
|
||||
def test_recl_lv_port_to_remove(self):
|
||||
self._prepare_l2_pop_ofports()
|
||||
self.agent.enable_tunneling = True
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.tun_br, 'delete_port'),
|
||||
mock.patch.object(self.agent, 'ryu_send_msg')
|
||||
) as (del_port_fn, ryu_send_msg_fn):
|
||||
with mock.patch.object(self.agent.int_br,
|
||||
'delete_port') as del_port_fn:
|
||||
self.agent.reclaim_local_vlan(self.lvms[1].net)
|
||||
del_port_fn.assert_called_once_with(self.tun_name2)
|
||||
|
||||
def test__setup_tunnel_port_error_negative(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
|
||||
mock.patch.object(self.agent.int_br, 'add_tunnel_port',
|
||||
return_value='-1'),
|
||||
mock.patch.object(self.mod_agent.LOG, 'error')
|
||||
) as (add_tunnel_port_fn, log_error_fn):
|
||||
ofport = self.agent._setup_tunnel_port(
|
||||
self.agent.tun_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE)
|
||||
self.agent.int_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE)
|
||||
add_tunnel_port_fn.assert_called_once_with(
|
||||
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
|
||||
self.agent.vxlan_udp_port, self.agent.dont_fragment)
|
||||
@ -812,13 +701,13 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
|
||||
def test__setup_tunnel_port_error_not_int(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.tun_br, 'add_tunnel_port',
|
||||
mock.patch.object(self.agent.int_br, 'add_tunnel_port',
|
||||
return_value=None),
|
||||
mock.patch.object(self.mod_agent.LOG, 'exception'),
|
||||
mock.patch.object(self.mod_agent.LOG, 'error')
|
||||
) as (add_tunnel_port_fn, log_exc_fn, log_error_fn):
|
||||
ofport = self.agent._setup_tunnel_port(
|
||||
self.agent.tun_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE)
|
||||
self.agent.int_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE)
|
||||
add_tunnel_port_fn.assert_called_once_with(
|
||||
'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE,
|
||||
self.agent.vxlan_udp_port, self.agent.dont_fragment)
|
||||
@ -843,219 +732,6 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
self.agent.local_ip,
|
||||
self.agent.tunnel_types[0])
|
||||
|
||||
def test__provision_local_vlan_inbound_for_tunnel(self):
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._provision_local_vlan_inbound_for_tunnel(1, 'gre', 3)
|
||||
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.tun_br.datapath,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS,
|
||||
[
|
||||
ofpp.OFPActionPushVlan(),
|
||||
ofpp.OFPActionSetField(vlan_vid=1 |
|
||||
ofp.OFPVID_PRESENT),
|
||||
]),
|
||||
ofpp.OFPInstructionGotoTable(
|
||||
table_id=constants.LEARN_FROM_TUN),
|
||||
],
|
||||
match=ofpp.OFPMatch(tunnel_id=3),
|
||||
priority=1,
|
||||
table_id=constants.TUN_TABLE['gre'])
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__provision_local_vlan_outbound(self):
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._provision_local_vlan_outbound(888, 999, 'phys-net1')
|
||||
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.phys_brs['phys-net1'].datapath,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS,
|
||||
[
|
||||
ofpp.OFPActionSetField(vlan_vid=999),
|
||||
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
|
||||
]
|
||||
)
|
||||
],
|
||||
match=ofpp.OFPMatch(
|
||||
in_port=777,
|
||||
vlan_vid=888 | ofp.OFPVID_PRESENT
|
||||
),
|
||||
priority=4)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__provision_local_vlan_inbound(self):
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._provision_local_vlan_inbound(888, 999, 'phys-net1')
|
||||
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.int_br.datapath,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS,
|
||||
[
|
||||
ofpp.OFPActionSetField(
|
||||
vlan_vid=888 | ofp.OFPVID_PRESENT
|
||||
),
|
||||
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
|
||||
]
|
||||
)
|
||||
],
|
||||
match=ofpp.OFPMatch(in_port=666, vlan_vid=999),
|
||||
priority=3)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__reclaim_local_vlan_outbound(self):
|
||||
lvm = mock.Mock()
|
||||
lvm.network_type = p_const.TYPE_VLAN
|
||||
lvm.segmentation_id = 555
|
||||
lvm.vlan = 444
|
||||
lvm.physical_network = 'phys-net1'
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._reclaim_local_vlan_outbound(lvm)
|
||||
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.phys_brs['phys-net1'].datapath,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(
|
||||
in_port=777,
|
||||
vlan_vid=444 | ofp.OFPVID_PRESENT
|
||||
),
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY,
|
||||
table_id=ofp.OFPTT_ALL)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__reclaim_local_vlan_inbound(self):
|
||||
lvm = mock.Mock()
|
||||
lvm.network_type = p_const.TYPE_VLAN
|
||||
lvm.segmentation_id = 555
|
||||
lvm.vlan = 444
|
||||
lvm.physical_network = 'phys-net1'
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._reclaim_local_vlan_inbound(lvm)
|
||||
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.int_br.datapath,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(
|
||||
in_port=666,
|
||||
vlan_vid=555 | ofp.OFPVID_PRESENT
|
||||
),
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY,
|
||||
table_id=ofp.OFPTT_ALL)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__provision_local_vlan_outbound_flat(self):
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._provision_local_vlan_outbound(888, ofp.OFPVID_NONE,
|
||||
'phys-net1')
|
||||
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.phys_brs['phys-net1'].datapath,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS,
|
||||
[
|
||||
ofpp.OFPActionPopVlan(),
|
||||
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
|
||||
]
|
||||
)
|
||||
],
|
||||
match=ofpp.OFPMatch(
|
||||
in_port=777,
|
||||
vlan_vid=888 | ofp.OFPVID_PRESENT
|
||||
),
|
||||
priority=4)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__provision_local_vlan_inbound_flat(self):
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._provision_local_vlan_inbound(888, ofp.OFPVID_NONE,
|
||||
'phys-net1')
|
||||
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.int_br.datapath,
|
||||
instructions=[
|
||||
ofpp.OFPInstructionActions(
|
||||
ofp.OFPIT_APPLY_ACTIONS,
|
||||
[
|
||||
ofpp.OFPActionPushVlan(),
|
||||
ofpp.OFPActionSetField(
|
||||
vlan_vid=888 | ofp.OFPVID_PRESENT
|
||||
),
|
||||
ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0),
|
||||
]
|
||||
)
|
||||
],
|
||||
match=ofpp.OFPMatch(in_port=666, vlan_vid=ofp.OFPVID_NONE),
|
||||
priority=3)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__reclaim_local_vlan_outbound_flat(self):
|
||||
lvm = mock.Mock()
|
||||
lvm.network_type = p_const.TYPE_FLAT
|
||||
lvm.segmentation_id = 555
|
||||
lvm.vlan = 444
|
||||
lvm.physical_network = 'phys-net1'
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._reclaim_local_vlan_outbound(lvm)
|
||||
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.phys_brs['phys-net1'].datapath,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(
|
||||
in_port=777,
|
||||
vlan_vid=444 | ofp.OFPVID_PRESENT
|
||||
),
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY,
|
||||
table_id=ofp.OFPTT_ALL)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__reclaim_local_vlan_inbound_flat(self):
|
||||
lvm = mock.Mock()
|
||||
lvm.network_type = p_const.TYPE_FLAT
|
||||
lvm.segmentation_id = 555
|
||||
lvm.vlan = 444
|
||||
lvm.physical_network = 'phys-net1'
|
||||
with mock.patch.object(self.agent, 'ryu_send_msg') as sendmsg:
|
||||
self.agent._reclaim_local_vlan_inbound(lvm)
|
||||
|
||||
ofp = importutils.import_module('ryu.ofproto.ofproto_v1_3')
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
expected_msg = ofpp.OFPFlowMod(
|
||||
self.agent.int_br.datapath,
|
||||
command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(
|
||||
in_port=666,
|
||||
vlan_vid=ofp.OFPVID_NONE
|
||||
),
|
||||
out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY,
|
||||
table_id=ofp.OFPTT_ALL)
|
||||
sendmsg.assert_has_calls([mock.call(expected_msg)])
|
||||
|
||||
def test__get_ports(self):
|
||||
ofpp = importutils.import_module('ryu.ofproto.ofproto_v1_3_parser')
|
||||
reply = [ofpp.OFPPortDescStatsReply(body=[ofpp.OFPPort(name='hoge',
|
||||
@ -1081,18 +757,3 @@ class TestOFANeutronAgent(ofa_test_base.OFAAgentTestBase):
|
||||
result = self.agent._get_ofport_names('hoge')
|
||||
_get_ports.assert_called_once_with('hoge')
|
||||
self.assertEqual(set(names), result)
|
||||
|
||||
def test_setup_tunnel_br(self):
|
||||
with contextlib.nested(
|
||||
mock.patch.object(self.agent.int_br,
|
||||
'add_patch_port', return_value='1'),
|
||||
mock.patch.object(self.agent.tun_br,
|
||||
'add_patch_port', return_value='2'),
|
||||
mock.patch.object(self.mod_agent, 'OVSBridge',
|
||||
return_value=self.agent.tun_br),
|
||||
mock.patch.object(self.agent,
|
||||
'_tun_br_output_arp_packet_to_controller')
|
||||
) as (int_add_patch_port, tun_add_patch_port,
|
||||
ovs_br_class, tun_output_ctrl):
|
||||
self.agent.setup_tunnel_br(cfg.CONF.OVS.tunnel_bridge)
|
||||
tun_output_ctrl.assert_called_once_with(self.agent.tun_br)
|
||||
|
82
neutron/tests/unit/ofagent/test_ofswitch.py
Normal file
82
neutron/tests/unit/ofagent/test_ofswitch.py
Normal file
@ -0,0 +1,82 @@
|
||||
# Copyright (C) 2014 VA Linux Systems Japan K.K.
|
||||
# Copyright (C) 2014 YAMAMOTO Takashi <yamamoto at valinux co jp>
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import mock
|
||||
|
||||
from neutron.openstack.common import importutils
|
||||
from neutron.tests.unit.ofagent import ofa_test_base
|
||||
|
||||
|
||||
class TestOFAgentFlows(ofa_test_base.OFATestBase):
|
||||
|
||||
_MOD = 'neutron.plugins.ofagent.agent.ofswitch'
|
||||
|
||||
def setUp(self):
|
||||
super(TestOFAgentFlows, self).setUp()
|
||||
self.mod = importutils.import_module(self._MOD)
|
||||
self.br = self.mod.OpenFlowSwitch()
|
||||
self.br.set_dp(self._mk_test_dp("dp"))
|
||||
|
||||
def test_delete_flows(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.delete_flows()
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, command=ofp.OFPFC_DELETE,
|
||||
match=ofpp.OFPMatch(), out_group=ofp.OFPG_ANY,
|
||||
out_port=ofp.OFPP_ANY, priority=0, table_id=ofp.OFPTT_ALL)),
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_install_default_drop(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.install_default_drop(table_id=98)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, priority=0, table_id=98)),
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_install_default_goto(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.install_default_goto(table_id=98, dest_table_id=150)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=150)],
|
||||
priority=0, table_id=98)),
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
||||
|
||||
def test_install_default_goto_next(self):
|
||||
br = self.br
|
||||
with mock.patch.object(br, '_send_msg') as sendmsg:
|
||||
br.install_default_goto_next(table_id=100)
|
||||
(dp, ofp, ofpp) = br._get_dp()
|
||||
call = mock.call
|
||||
expected_calls = [
|
||||
call(ofpp.OFPFlowMod(dp, instructions=[
|
||||
ofpp.OFPInstructionGotoTable(table_id=101)],
|
||||
priority=0, table_id=100)),
|
||||
]
|
||||
sendmsg.assert_has_calls(expected_calls)
|
Loading…
x
Reference in New Issue
Block a user