From 35f3a42f495ce766c02fa582f1d5a1cfcc5f9d6c Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Fri, 18 Apr 2014 15:48:20 +0900 Subject: [PATCH] ofagent: merge br-tun into br-int Highlights: - Deprecate br-tun. - Reduce the use of OFPP_NORMAL action. Use mac address info obtained from get_device_details and l2-pop to reduce flooding. - Use OpenFlow metadata instead of "internal" VLANs. Now tenant networks are VLAN transparent. Implements: blueprint ofagent-port-monitor Implements: blueprint ofagent-merge-bridges Change-Id: I21ee1c6d141863182b487e10c7bfe911b1a472ab --- neutron/plugins/ofagent/README | 15 + neutron/plugins/ofagent/agent/arp_lib.py | 74 +-- neutron/plugins/ofagent/agent/constants.py | 19 + neutron/plugins/ofagent/agent/flows.py | 407 ++++++++++++ neutron/plugins/ofagent/agent/metadata.py | 26 + .../ofagent/agent/ofa_neutron_agent.py | 601 +++--------------- neutron/plugins/ofagent/agent/ofswitch.py | 78 +++ neutron/plugins/ofagent/agent/ports.py | 7 +- neutron/plugins/ofagent/agent/tables.py | 62 ++ neutron/tests/unit/ofagent/fake_oflib.py | 3 + neutron/tests/unit/ofagent/ofa_test_base.py | 42 +- neutron/tests/unit/ofagent/test_arp_lib.py | 42 +- neutron/tests/unit/ofagent/test_ofa_flows.py | 351 ++++++++++ .../unit/ofagent/test_ofa_neutron_agent.py | 481 +++----------- neutron/tests/unit/ofagent/test_ofswitch.py | 82 +++ 15 files changed, 1294 insertions(+), 996 deletions(-) create mode 100644 neutron/plugins/ofagent/agent/constants.py create mode 100644 neutron/plugins/ofagent/agent/flows.py create mode 100644 neutron/plugins/ofagent/agent/metadata.py create mode 100644 neutron/plugins/ofagent/agent/ofswitch.py create mode 100644 neutron/plugins/ofagent/agent/tables.py create mode 100644 neutron/tests/unit/ofagent/test_ofa_flows.py create mode 100644 neutron/tests/unit/ofagent/test_ofswitch.py diff --git a/neutron/plugins/ofagent/README b/neutron/plugins/ofagent/README index 5341563a6..51164a1ef 100644 --- a/neutron/plugins/ofagent/README +++ b/neutron/plugins/ofagent/README @@ -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) diff --git a/neutron/plugins/ofagent/agent/arp_lib.py b/neutron/plugins/ofagent/agent/arp_lib.py index 279ad5019..ceb34c63e 100644 --- a/neutron/plugins/ofagent/agent/arp_lib.py +++ b/neutron/plugins/ofagent/agent/arp_lib.py @@ -20,10 +20,9 @@ from ryu.lib.packet import arp from ryu.lib.packet import ethernet from ryu.lib.packet import packet from ryu.lib.packet import vlan -from ryu.ofproto import ether from neutron.openstack.common import log as logging -from neutron.plugins.openvswitch.common import constants +import neutron.plugins.ofagent.agent.metadata as meta LOG = logging.getLogger(__name__) @@ -45,6 +44,10 @@ class ArpLib(object): """ self.ryuapp = ryuapp self._arp_tbl = {} + self.br = None + + def set_bridge(self, br): + self.br = br def _send_arp_reply(self, datapath, port, pkt): LOG.debug("packet-out %s", pkt) @@ -60,21 +63,6 @@ class ArpLib(object): data=data) ryu_api.send_msg(self.ryuapp, out) - def _add_flow_to_avoid_unknown_packet(self, datapath, match): - LOG.debug("add flow to avoid an unknown packet from packet-in") - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - instructions = [ofpp.OFPInstructionGotoTable( - table_id=constants.FLOOD_TO_TUN)] - out = ofpp.OFPFlowMod(datapath, - table_id=constants.PATCH_LV_TO_TUN, - command=ofp.OFPFC_ADD, - idle_timeout=5, - priority=20, - match=match, - instructions=instructions) - ryu_api.send_msg(self.ryuapp, out) - def _send_unknown_packet(self, msg, in_port, out_port): LOG.debug("unknown packet-out in-port %(in_port)s " "out-port %(out_port)s msg %(msg)s", @@ -109,10 +97,11 @@ class ArpLib(object): pkt.add_protocol(ethernet.ethernet(ethertype=pkt_ethernet.ethertype, dst=pkt_ethernet.src, src=hw_addr)) - pkt.add_protocol(vlan.vlan(cfi=pkt_vlan.cfi, - ethertype=pkt_vlan.ethertype, - pcp=pkt_vlan.pcp, - vid=pkt_vlan.vid)) + if pkt_vlan: + pkt.add_protocol(vlan.vlan(cfi=pkt_vlan.cfi, + ethertype=pkt_vlan.ethertype, + pcp=pkt_vlan.pcp, + vid=pkt_vlan.vid)) pkt.add_protocol(arp.arp(opcode=arp.ARP_REPLY, src_mac=hw_addr, src_ip=ip_addr, @@ -146,31 +135,33 @@ class ArpLib(object): msg = ev.msg LOG.debug("packet-in msg %s", msg) datapath = msg.datapath + if self.br is None: + LOG.info(_("No bridge is set")) + return + if self.br.datapath.id != datapath.id: + LOG.info(_("Unknown bridge %(dpid)s ours %(ours)s"), + {"dpid": datapath.id, "ours": self.br.datapath.id}) + return ofp = datapath.ofproto - ofpp = datapath.ofproto_parser port = msg.match['in_port'] + metadata = msg.match.get('metadata') pkt = packet.Packet(msg.data) LOG.info(_("packet-in dpid %(dpid)s in_port %(port)s pkt %(pkt)s"), {'dpid': dpid_lib.dpid_to_str(datapath.id), 'port': port, 'pkt': pkt}) - pkt_vlan = None - pkt_arp = None + + if metadata is None: + LOG.info(_("drop non tenant packet")) + return + network = metadata & meta.NETWORK_MASK pkt_ethernet = pkt.get_protocol(ethernet.ethernet) if not pkt_ethernet: - LOG.info(_("non-ethernet packet")) - else: - pkt_vlan = pkt.get_protocol(vlan.vlan) - if not pkt_vlan: - LOG.info(_("non-vlan packet")) - if pkt_vlan: - network = pkt_vlan.vid - pkt_arp = pkt.get_protocol(arp.arp) - if not pkt_arp: - LOG.info(_("drop non-arp packet")) - return - else: - # drop an unknown packet. - LOG.info(_("drop unknown packet")) + LOG.info(_("drop non-ethernet packet")) + return + pkt_vlan = pkt.get_protocol(vlan.vlan) + pkt_arp = pkt.get_protocol(arp.arp) + if not pkt_arp: + LOG.info(_("drop non-arp packet")) return arptbl = self._arp_tbl.get(network) @@ -180,11 +171,8 @@ class ArpLib(object): return else: LOG.info(_("unknown network %s"), network) + # add a flow to skip a packet-in to a controller. - match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP, - vlan_vid=network | ofp.OFPVID_PRESENT, - arp_op=arp.ARP_REQUEST, - arp_tpa=pkt_arp.dst_ip) - self._add_flow_to_avoid_unknown_packet(datapath, match) + self.br.arp_passthrough(network=network, tpa=pkt_arp.dst_ip) # send an unknown arp packet to the table. self._send_unknown_packet(msg, port, ofp.OFPP_TABLE) diff --git a/neutron/plugins/ofagent/agent/constants.py b/neutron/plugins/ofagent/agent/constants.py new file mode 100644 index 000000000..633979d8b --- /dev/null +++ b/neutron/plugins/ofagent/agent/constants.py @@ -0,0 +1,19 @@ +# Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi +# 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 diff --git a/neutron/plugins/ofagent/agent/flows.py b/neutron/plugins/ofagent/agent/flows.py new file mode 100644 index 000000000..b699a6842 --- /dev/null +++ b/neutron/plugins/ofagent/agent/flows.py @@ -0,0 +1,407 @@ +# Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi +# 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) diff --git a/neutron/plugins/ofagent/agent/metadata.py b/neutron/plugins/ofagent/agent/metadata.py new file mode 100644 index 000000000..e9d56909d --- /dev/null +++ b/neutron/plugins/ofagent/agent/metadata.py @@ -0,0 +1,26 @@ +# Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi +# 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) diff --git a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py index 11780793d..fe7331911 100644 --- a/neutron/plugins/ofagent/agent/ofa_neutron_agent.py +++ b/neutron/plugins/ofagent/agent/ofa_neutron_agent.py @@ -1,4 +1,5 @@ # Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi # Based on openvswitch agent. # # Copyright 2011 VMware, Inc. @@ -26,8 +27,6 @@ from ryu.base import app_manager from ryu.controller import handler from ryu.controller import ofp_event from ryu.lib import hub -from ryu.lib.packet import arp -from ryu.ofproto import ether from ryu.ofproto import ofproto_v1_3 as ryu_ofp13 from neutron.agent import l2population_rpc @@ -45,22 +44,23 @@ from neutron.openstack.common import log as logging from neutron.openstack.common import loopingcall from neutron.plugins.common import constants as p_const from neutron.plugins.ofagent.agent import arp_lib +from neutron.plugins.ofagent.agent import constants as ofa_const +from neutron.plugins.ofagent.agent import flows from neutron.plugins.ofagent.agent import ports +from neutron.plugins.ofagent.agent import tables from neutron.plugins.ofagent.common import config # noqa from neutron.plugins.openvswitch.common import constants LOG = logging.getLogger(__name__) -# A placeholder for dead vlans. -DEAD_VLAN_TAG = str(n_const.MAX_VLAN_TAG + 1) - # A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac' # attributes set). class LocalVLANMapping: def __init__(self, vlan, network_type, physical_network, segmentation_id, vif_ports=None): + assert(isinstance(vlan, (int, long))) if vif_ports is None: vif_ports = {} self.vlan = vlan @@ -77,13 +77,13 @@ class LocalVLANMapping: self.segmentation_id)) -class OVSBridge(ovs_lib.OVSBridge): +class Bridge(flows.OFAgentIntegrationBridge, ovs_lib.OVSBridge): def __init__(self, br_name, root_helper, ryuapp): - super(OVSBridge, self).__init__(br_name, root_helper) + super(Bridge, self).__init__(br_name, root_helper) self.datapath_id = None self.datapath = None - self.ofparser = None self.ryuapp = ryuapp + self.set_app(ryuapp) def find_datapath_id(self): self.datapath_id = self.get_datapath_id() @@ -98,7 +98,7 @@ class OVSBridge(ovs_lib.OVSBridge): LOG.error(_('Agent terminated!: Failed to get a datapath.')) raise SystemExit(1) time.sleep(1) - self.ofparser = self.datapath.ofproto_parser + self.set_dp(self.datapath) def setup_ofp(self, controller_names=None, protocols='OpenFlow13', @@ -162,6 +162,7 @@ class OFANeutronAgentRyuApp(app_manager.RyuApp): cfg.CONF.set_default('ip_lib_force_root', True) agent = OFANeutronAgent(ryuapp, **agent_config) + self.arplib.set_bridge(agent.int_br) # Start everything. LOG.info(_("Agent initialized successfully, now running... ")) @@ -196,7 +197,7 @@ class OFANeutronAgent(n_rpc.RpcCallback, # 1.1 Support Security Group RPC RPC_API_VERSION = '1.1' - def __init__(self, ryuapp, integ_br, tun_br, local_ip, + def __init__(self, ryuapp, integ_br, local_ip, bridge_mappings, root_helper, polling_interval, tunnel_types=None, veth_mtu=None): @@ -204,7 +205,6 @@ class OFANeutronAgent(n_rpc.RpcCallback, :param ryuapp: object of the ryu app. :param integ_br: name of the integration bridge. - :param tun_br: name of the tunnel bridge. :param local_ip: local IP address of this hypervisor. :param bridge_mappings: mappings from physical network name to bridge. :param root_helper: utility to use when running shell cmds. @@ -218,8 +218,9 @@ class OFANeutronAgent(n_rpc.RpcCallback, self.ryuapp = ryuapp self.veth_mtu = veth_mtu self.root_helper = root_helper - self.available_local_vlans = set(xrange(n_const.MIN_VLAN_TAG, - n_const.MAX_VLAN_TAG)) + # TODO(yamamoto): Remove this VLAN leftover + self.available_local_vlans = set(xrange(ofa_const.LOCAL_VLAN_MIN, + ofa_const.LOCAL_VLAN_MAX)) self.tunnel_types = tunnel_types or [] self.agent_state = { 'binary': 'neutron-ofa-agent', @@ -235,16 +236,16 @@ class OFANeutronAgent(n_rpc.RpcCallback, # Keep track of int_br's device count for use by _report_state() self.int_br_device_count = 0 - self.int_br = OVSBridge(integ_br, self.root_helper, self.ryuapp) + self.int_br = Bridge(integ_br, self.root_helper, self.ryuapp) # Stores port update notifications for processing in main loop self.updated_ports = set() self.setup_rpc() self.setup_integration_br() self.setup_physical_bridges(bridge_mappings) self.local_vlan_map = {} - self.tun_br_ofports = {p_const.TYPE_GRE: {}, - p_const.TYPE_VXLAN: {}} - + self.tun_ofports = {} + for t in tables.TUNNEL_TYPES: + self.tun_ofports[t] = {} self.polling_interval = polling_interval self.enable_tunneling = bool(self.tunnel_types) @@ -252,8 +253,6 @@ class OFANeutronAgent(n_rpc.RpcCallback, self.tunnel_count = 0 self.vxlan_udp_port = cfg.CONF.AGENT.vxlan_udp_port self.dont_fragment = cfg.CONF.AGENT.dont_fragment - if self.enable_tunneling: - self.setup_tunnel_br(tun_br) # Security group agent support self.sg_agent = OFASecurityGroupAgent(self.context, @@ -281,10 +280,6 @@ class OFANeutronAgent(n_rpc.RpcCallback, LOG.warn(_("Unable to create tunnel port. Invalid remote IP: %s"), ip_address) - def ryu_send_msg(self, msg): - result = ryu_api.send_msg(self.ryuapp, msg) - LOG.info(_("ryu send_msg() result: %s"), result) - def setup_rpc(self): mac = self.int_br.get_local_port_mac() self.agent_id = '%s%s' % ('ovs', (mac.replace(":", ""))) @@ -346,8 +341,8 @@ class OFANeutronAgent(n_rpc.RpcCallback, self.local_vlan_map): agent_ports.pop(self.local_ip, None) if len(agent_ports): - self.fdb_add_tun(context, self.tun_br, lvm, agent_ports, - self.tun_br_ofports) + self.fdb_add_tun(context, self.int_br, lvm, agent_ports, + self.tun_ofports) def fdb_remove(self, context, fdb_entries): LOG.debug("fdb_remove received") @@ -355,86 +350,40 @@ class OFANeutronAgent(n_rpc.RpcCallback, self.local_vlan_map): agent_ports.pop(self.local_ip, None) if len(agent_ports): - self.fdb_remove_tun(context, self.tun_br, lvm, agent_ports, - self.tun_br_ofports) - - def _add_fdb_flooding_flow(self, br, lvm): - datapath = br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - match = ofpp.OFPMatch( - vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT) - actions = [ofpp.OFPActionPopVlan(), - ofpp.OFPActionSetField( - tunnel_id=int(lvm.segmentation_id))] - for tun_ofport in lvm.tun_ofports: - actions.append(ofpp.OFPActionOutput(int(tun_ofport), 0)) - instructions = [ofpp.OFPInstructionActions( - ofp.OFPIT_APPLY_ACTIONS, actions)] - msg = ofpp.OFPFlowMod(datapath, - table_id=constants.FLOOD_TO_TUN, - command=ofp.OFPFC_ADD, - priority=1, - match=match, instructions=instructions) - self.ryu_send_msg(msg) + self.fdb_remove_tun(context, self.int_br, lvm, agent_ports, + self.tun_ofports) def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): - datapath = br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser if port_info == n_const.FLOODING_ENTRY: lvm.tun_ofports.add(ofport) - self._add_fdb_flooding_flow(br, lvm) + br.install_tunnel_output( + tables.TUNNEL_FLOOD[lvm.network_type], + lvm.vlan, lvm.segmentation_id, + lvm.tun_ofports, goto_next=True) else: self.ryuapp.add_arp_table_entry( lvm.vlan, port_info[1], port_info[0]) - match = ofpp.OFPMatch( - vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT, - eth_dst=port_info[0]) - actions = [ofpp.OFPActionPopVlan(), - ofpp.OFPActionSetField( - tunnel_id=int(lvm.segmentation_id)), - ofpp.OFPActionOutput(int(ofport), 0)] - instructions = [ofpp.OFPInstructionActions( - ofp.OFPIT_APPLY_ACTIONS, actions)] - msg = ofpp.OFPFlowMod(datapath, - table_id=constants.UCAST_TO_TUN, - command=ofp.OFPFC_ADD, - priority=2, - match=match, instructions=instructions) - self.ryu_send_msg(msg) + br.install_tunnel_output( + tables.TUNNEL_OUT, + lvm.vlan, lvm.segmentation_id, + set([ofport]), goto_next=False, eth_dst=port_info[0]) def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport): - datapath = br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser if port_info == n_const.FLOODING_ENTRY: lvm.tun_ofports.remove(ofport) if len(lvm.tun_ofports) > 0: - self._add_fdb_flooding_flow(br, lvm) + br.install_tunnel_output( + tables.TUNNEL_FLOOD[lvm.network_type], + lvm.vlan, lvm.segmentation_id, + lvm.tun_ofports, goto_next=True) else: - # This local vlan doesn't require any more tunelling - match = ofpp.OFPMatch( - vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT) - msg = ofpp.OFPFlowMod(datapath, - table_id=constants.FLOOD_TO_TUN, - command=ofp.OFPFC_DELETE, - out_group=ofp.OFPG_ANY, - out_port=ofp.OFPP_ANY, - match=match) - self.ryu_send_msg(msg) + br.delete_tunnel_output( + tables.TUNNEL_FLOOD[lvm.network_type], + lvm.vlan) else: self.ryuapp.del_arp_table_entry(lvm.vlan, port_info[1]) - match = ofpp.OFPMatch( - vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT, - eth_dst=port_info[0]) - msg = ofpp.OFPFlowMod(datapath, - table_id=constants.UCAST_TO_TUN, - command=ofp.OFPFC_DELETE, - out_group=ofp.OFPG_ANY, - out_port=ofp.OFPP_ANY, - match=match) - self.ryu_send_msg(msg) + br.delete_tunnel_output(tables.TUNNEL_OUT, + lvm.vlan, eth_dst=port_info[0]) def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address, ip_address): @@ -445,85 +394,9 @@ class OFANeutronAgent(n_rpc.RpcCallback, def _fdb_chg_ip(self, context, fdb_entries): LOG.debug("update chg_ip received") - self.fdb_chg_ip_tun(context, self.tun_br, fdb_entries, self.local_ip, + self.fdb_chg_ip_tun(context, self.int_br, fdb_entries, self.local_ip, self.local_vlan_map) - def _provision_local_vlan_inbound_for_tunnel(self, lvid, network_type, - segmentation_id): - br = self.tun_br - match = br.ofparser.OFPMatch( - tunnel_id=int(segmentation_id)) - actions = [ - br.ofparser.OFPActionPushVlan(), - br.ofparser.OFPActionSetField( - vlan_vid=int(lvid) | ryu_ofp13.OFPVID_PRESENT)] - instructions = [ - br.ofparser.OFPInstructionActions( - ryu_ofp13.OFPIT_APPLY_ACTIONS, actions), - br.ofparser.OFPInstructionGotoTable( - table_id=constants.LEARN_FROM_TUN)] - msg = br.ofparser.OFPFlowMod( - br.datapath, - table_id=constants.TUN_TABLE[network_type], - priority=1, - match=match, - instructions=instructions) - self.ryu_send_msg(msg) - - def _local_vlan_for_tunnel(self, lvid, network_type, segmentation_id): - self._provision_local_vlan_inbound_for_tunnel(lvid, network_type, - segmentation_id) - - def _provision_local_vlan_outbound(self, lvid, vlan_vid, physical_network): - br = self.phys_brs[physical_network] - datapath = br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - match = ofpp.OFPMatch(in_port=int(self.phys_ofports[physical_network]), - vlan_vid=int(lvid) | ofp.OFPVID_PRESENT) - if vlan_vid == ofp.OFPVID_NONE: - actions = [ofpp.OFPActionPopVlan()] - else: - actions = [ofpp.OFPActionSetField(vlan_vid=vlan_vid)] - actions += [ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0)] - instructions = [ - ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions), - ] - msg = ofpp.OFPFlowMod(datapath, priority=4, match=match, - instructions=instructions) - self.ryu_send_msg(msg) - - def _provision_local_vlan_inbound(self, lvid, vlan_vid, physical_network): - datapath = self.int_br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - match = ofpp.OFPMatch(in_port=int(self.int_ofports[physical_network]), - vlan_vid=vlan_vid) - if vlan_vid == ofp.OFPVID_NONE: - actions = [ofpp.OFPActionPushVlan()] - else: - actions = [] - actions += [ - ofpp.OFPActionSetField(vlan_vid=int(lvid) | ofp.OFPVID_PRESENT), - ofpp.OFPActionOutput(ofp.OFPP_NORMAL, 0), - ] - instructions = [ - ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions), - ] - msg = ofpp.OFPFlowMod(datapath, priority=3, match=match, - instructions=instructions) - self.ryu_send_msg(msg) - - def _local_vlan_for_flat(self, lvid, physical_network): - vlan_vid = ryu_ofp13.OFPVID_NONE - self._provision_local_vlan_outbound(lvid, vlan_vid, physical_network) - self._provision_local_vlan_inbound(lvid, vlan_vid, physical_network) - - def _local_vlan_for_vlan(self, lvid, physical_network, segmentation_id): - vlan_vid = int(segmentation_id) | ryu_ofp13.OFPVID_PRESENT - self._provision_local_vlan_outbound(lvid, vlan_vid, physical_network) - self._provision_local_vlan_inbound(lvid, vlan_vid, physical_network) - def provision_local_vlan(self, net_uuid, network_type, physical_network, segmentation_id): """Provisions a local VLAN. @@ -548,31 +421,25 @@ class OFANeutronAgent(n_rpc.RpcCallback, if network_type in constants.TUNNEL_NETWORK_TYPES: if self.enable_tunneling: - self._local_vlan_for_tunnel(lvid, network_type, - segmentation_id) + self.int_br.provision_tenant_tunnel(network_type, lvid, + segmentation_id) else: LOG.error(_("Cannot provision %(network_type)s network for " "net-id=%(net_uuid)s - tunneling disabled"), {'network_type': network_type, 'net_uuid': net_uuid}) - elif network_type == p_const.TYPE_FLAT: - if physical_network in self.phys_brs: - self._local_vlan_for_flat(lvid, physical_network) + elif network_type in [p_const.TYPE_VLAN, p_const.TYPE_FLAT]: + if physical_network in self.int_ofports: + phys_port = self.int_ofports[physical_network] + self.int_br.provision_tenant_physnet(network_type, lvid, + segmentation_id, + phys_port) else: - LOG.error(_("Cannot provision flat network for " - "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 " + 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, + {'network_type': network_type, + 'net_uuid': net_uuid, 'physical_network': physical_network}) elif network_type == p_const.TYPE_LOCAL: # no flows needed for local networks @@ -583,35 +450,6 @@ class OFANeutronAgent(n_rpc.RpcCallback, {'network_type': network_type, 'net_uuid': net_uuid}) - def _reclaim_local_vlan_outbound(self, lvm): - br = self.phys_brs[lvm.physical_network] - datapath = br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - match = ofpp.OFPMatch( - in_port=int(self.phys_ofports[lvm.physical_network]), - vlan_vid=int(lvm.vlan) | ofp.OFPVID_PRESENT) - msg = ofpp.OFPFlowMod(datapath, table_id=ofp.OFPTT_ALL, - command=ofp.OFPFC_DELETE, out_group=ofp.OFPG_ANY, - out_port=ofp.OFPP_ANY, match=match) - self.ryu_send_msg(msg) - - def _reclaim_local_vlan_inbound(self, lvm): - datapath = self.int_br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - if lvm.network_type == p_const.TYPE_FLAT: - vid = ofp.OFPVID_NONE - else: # p_const.TYPE_VLAN - vid = lvm.segmentation_id | ofp.OFPVID_PRESENT - match = ofpp.OFPMatch( - in_port=int(self.int_ofports[lvm.physical_network]), - vlan_vid=vid) - msg = ofpp.OFPFlowMod(datapath, table_id=ofp.OFPTT_ALL, - command=ofp.OFPFC_DELETE, out_group=ofp.OFPG_ANY, - out_port=ofp.OFPP_ANY, match=match) - self.ryu_send_msg(msg) - def reclaim_local_vlan(self, net_uuid): """Reclaim a local VLAN. @@ -630,34 +468,16 @@ class OFANeutronAgent(n_rpc.RpcCallback, if lvm.network_type in constants.TUNNEL_NETWORK_TYPES: if self.enable_tunneling: - match = self.tun_br.ofparser.OFPMatch( - tunnel_id=int(lvm.segmentation_id)) - msg = self.tun_br.ofparser.OFPFlowMod( - self.tun_br.datapath, - table_id=constants.TUN_TABLE[lvm.network_type], - command=ryu_ofp13.OFPFC_DELETE, - out_group=ryu_ofp13.OFPG_ANY, - out_port=ryu_ofp13.OFPP_ANY, - match=match) - self.ryu_send_msg(msg) - match = self.tun_br.ofparser.OFPMatch( - vlan_vid=int(lvm.vlan) | ryu_ofp13.OFPVID_PRESENT) - msg = self.tun_br.ofparser.OFPFlowMod( - self.tun_br.datapath, - table_id=ryu_ofp13.OFPTT_ALL, - command=ryu_ofp13.OFPFC_DELETE, - out_group=ryu_ofp13.OFPG_ANY, - out_port=ryu_ofp13.OFPP_ANY, - match=match) - self.ryu_send_msg(msg) + self.int_br.reclaim_tenant_tunnel(lvm.network_type, lvm.vlan, + lvm.segmentation_id) # Try to remove tunnel ports if not used by other networks for ofport in lvm.tun_ofports: - self.cleanup_tunnel_port(self.tun_br, ofport, + self.cleanup_tunnel_port(self.int_br, ofport, lvm.network_type) - elif lvm.network_type in (p_const.TYPE_FLAT, p_const.TYPE_VLAN): - if lvm.physical_network in self.phys_brs: - self._reclaim_local_vlan_outbound(lvm) - self._reclaim_local_vlan_inbound(lvm) + elif lvm.network_type in [p_const.TYPE_FLAT, p_const.TYPE_VLAN]: + phys_port = self.int_ofports[lvm.physical_network] + self.int_br.reclaim_tenant_physnet(lvm.network_type, lvm.vlan, + lvm.segmentation_id, phys_port) elif lvm.network_type == p_const.TYPE_LOCAL: # no flows needed for local networks pass @@ -684,22 +504,18 @@ class OFANeutronAgent(n_rpc.RpcCallback, self.provision_local_vlan(net_uuid, network_type, physical_network, segmentation_id) lvm = self.local_vlan_map[net_uuid] + lvm.vif_ports[port.normalized_port_name()] = port - # Do not bind a port if it's already bound - cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag") - if cur_tag != str(lvm.vlan): - self.int_br.set_db_attribute("Port", port.port_name, "tag", - str(lvm.vlan)) - if port.ofport != -1: - match = self.int_br.ofparser.OFPMatch(in_port=port.ofport) - msg = self.int_br.ofparser.OFPFlowMod( - self.int_br.datapath, - table_id=ryu_ofp13.OFPTT_ALL, - command=ryu_ofp13.OFPFC_DELETE, - out_group=ryu_ofp13.OFPG_ANY, - out_port=ryu_ofp13.OFPP_ANY, - match=match) - self.ryu_send_msg(msg) + self.int_br.check_in_port_add_local_port(lvm.vlan, port.ofport) + + # if any of vif mac is unknown, flood unicasts as well + flood_unicast = any(map(lambda x: x.vif_mac is None, + lvm.vif_ports.values())) + ofports = (vp.ofport for vp in lvm.vif_ports.values()) + self.int_br.local_flood_update(lvm.vlan, ofports, flood_unicast) + if port.vif_mac is None: + return + self.int_br.local_out_add_port(lvm.vlan, port.ofport, port.vif_mac) def port_unbound(self, vif_id, net_uuid=None): """Unbind port. @@ -718,178 +534,29 @@ class OFANeutronAgent(n_rpc.RpcCallback, return lvm = self.local_vlan_map[net_uuid] - lvm.vif_ports.pop(vif_id, None) + port = lvm.vif_ports.pop(vif_id, None) + self.int_br.check_in_port_delete_port(port.ofport) if not lvm.vif_ports: self.reclaim_local_vlan(net_uuid) + if port.vif_mac is None: + return + self.int_br.local_out_delete_port(lvm.vlan, port.vif_mac) def port_dead(self, port): """Once a port has no binding, put it on the "dead vlan". :param port: a ovs_lib.VifPort object. """ - # Don't kill a port if it's already dead - cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag") - if cur_tag != DEAD_VLAN_TAG: - self.int_br.set_db_attribute("Port", port.port_name, "tag", - DEAD_VLAN_TAG) - match = self.int_br.ofparser.OFPMatch(in_port=port.ofport) - msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath, - priority=2, match=match) - self.ryu_send_msg(msg) + pass def setup_integration_br(self): """Setup the integration bridge. - - Create patch ports and remove all existing flows. - - :param bridge_name: the name of the integration bridge. - :returns: the integration bridge """ - self.int_br.setup_ofp() - self.int_br.delete_port(cfg.CONF.OVS.int_peer_patch_port) - msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath, - table_id=ryu_ofp13.OFPTT_ALL, - command=ryu_ofp13.OFPFC_DELETE, - out_group=ryu_ofp13.OFPG_ANY, - out_port=ryu_ofp13.OFPP_ANY) - self.ryu_send_msg(msg) - # switch all traffic using L2 learning - actions = [self.int_br.ofparser.OFPActionOutput( - ryu_ofp13.OFPP_NORMAL, 0)] - instructions = [self.int_br.ofparser.OFPInstructionActions( - ryu_ofp13.OFPIT_APPLY_ACTIONS, - actions)] - msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath, - priority=1, - instructions=instructions) - self.ryu_send_msg(msg) - - def _tun_br_sort_incoming_traffic_depend_in_port(self, br): - match = br.ofparser.OFPMatch( - in_port=int(self.patch_int_ofport)) - instructions = [br.ofparser.OFPInstructionGotoTable( - table_id=constants.PATCH_LV_TO_TUN)] - msg = br.ofparser.OFPFlowMod(br.datapath, - priority=1, - match=match, - instructions=instructions) - self.ryu_send_msg(msg) - msg = br.ofparser.OFPFlowMod(br.datapath, priority=0) - self.ryu_send_msg(msg) - - def _tun_br_output_arp_packet_to_controller(self, br): - datapath = br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - match = ofpp.OFPMatch(eth_type=ether.ETH_TYPE_ARP, - arp_op=arp.ARP_REQUEST) - actions = [ofpp.OFPActionOutput(ofp.OFPP_CONTROLLER)] - instructions = [ - ofpp.OFPInstructionActions(ofp.OFPIT_APPLY_ACTIONS, actions)] - msg = ofpp.OFPFlowMod(datapath, - table_id=constants.PATCH_LV_TO_TUN, - priority=10, - match=match, - instructions=instructions) - self.ryu_send_msg(msg) - - def _tun_br_goto_table_ucast_unicast(self, br): - match = br.ofparser.OFPMatch(eth_dst=('00:00:00:00:00:00', - '01:00:00:00:00:00')) - instructions = [br.ofparser.OFPInstructionGotoTable( - table_id=constants.UCAST_TO_TUN)] - msg = br.ofparser.OFPFlowMod(br.datapath, - table_id=constants.PATCH_LV_TO_TUN, - priority=0, - match=match, - instructions=instructions) - self.ryu_send_msg(msg) - - def _tun_br_goto_table_flood_broad_multi_cast(self, br): - match = br.ofparser.OFPMatch(eth_dst=('01:00:00:00:00:00', - '01:00:00:00:00:00')) - instructions = [br.ofparser.OFPInstructionGotoTable( - table_id=constants.FLOOD_TO_TUN)] - msg = br.ofparser.OFPFlowMod(br.datapath, - table_id=constants.PATCH_LV_TO_TUN, - priority=0, - match=match, - instructions=instructions) - self.ryu_send_msg(msg) - - def _tun_br_set_table_tun_by_tunnel_type(self, br): - for tunnel_type in constants.TUNNEL_NETWORK_TYPES: - msg = br.ofparser.OFPFlowMod( - br.datapath, - table_id=constants.TUN_TABLE[tunnel_type], - priority=0) - self.ryu_send_msg(msg) - - def _tun_br_output_patch_int(self, br): - actions = [br.ofparser.OFPActionOutput( - int(self.patch_int_ofport), 0)] - instructions = [br.ofparser.OFPInstructionActions( - ryu_ofp13.OFPIT_APPLY_ACTIONS, - actions)] - msg = br.ofparser.OFPFlowMod(br.datapath, - table_id=constants.LEARN_FROM_TUN, - priority=1, - instructions=instructions) - self.ryu_send_msg(msg) - - def _tun_br_goto_table_flood_unknown_unicast(self, br): - instructions = [br.ofparser.OFPInstructionGotoTable( - table_id=constants.FLOOD_TO_TUN)] - msg = br.ofparser.OFPFlowMod(br.datapath, - table_id=constants.UCAST_TO_TUN, - priority=0, - instructions=instructions) - self.ryu_send_msg(msg) - - def _tun_br_default_drop(self, br): - msg = br.ofparser.OFPFlowMod( - br.datapath, - table_id=constants.FLOOD_TO_TUN, - priority=0) - self.ryu_send_msg(msg) - - def setup_tunnel_br(self, tun_br): - """Setup the tunnel bridge. - - Creates tunnel bridge, and links it to the integration bridge - using a patch port. - - :param tun_br: the name of the tunnel bridge. - """ - self.tun_br = OVSBridge(tun_br, self.root_helper, self.ryuapp) - self.tun_br.reset_bridge() - self.tun_br.setup_ofp() - self.patch_tun_ofport = self.int_br.add_patch_port( - cfg.CONF.OVS.int_peer_patch_port, cfg.CONF.OVS.tun_peer_patch_port) - self.patch_int_ofport = self.tun_br.add_patch_port( - cfg.CONF.OVS.tun_peer_patch_port, cfg.CONF.OVS.int_peer_patch_port) - if int(self.patch_tun_ofport) < 0 or int(self.patch_int_ofport) < 0: - LOG.error(_("Failed to create OVS patch port. Cannot have " - "tunneling enabled on this agent, since this version " - "of OVS does not support tunnels or patch ports. " - "Agent terminated!")) - raise SystemExit(1) - msg = self.tun_br.ofparser.OFPFlowMod(self.tun_br.datapath, - table_id=ryu_ofp13.OFPTT_ALL, - command=ryu_ofp13.OFPFC_DELETE, - out_group=ryu_ofp13.OFPG_ANY, - out_port=ryu_ofp13.OFPP_ANY) - self.ryu_send_msg(msg) - - self._tun_br_sort_incoming_traffic_depend_in_port(self.tun_br) - self._tun_br_output_arp_packet_to_controller(self.tun_br) - self._tun_br_goto_table_ucast_unicast(self.tun_br) - self._tun_br_goto_table_flood_broad_multi_cast(self.tun_br) - self._tun_br_set_table_tun_by_tunnel_type(self.tun_br) - self._tun_br_output_patch_int(self.tun_br) - self._tun_br_goto_table_flood_unknown_unicast(self.tun_br) - self._tun_br_default_drop(self.tun_br) + + br = self.int_br + br.setup_ofp() + br.setup_default_table() def _phys_br_prepare_create_veth(self, br, int_veth_name, phys_veth_name): self.int_br.delete_port(int_veth_name) @@ -905,21 +572,11 @@ class OFANeutronAgent(n_rpc.RpcCallback, phys_veth_name, physical_network, ip_wrapper): int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name, phys_veth_name) - self.int_ofports[physical_network] = self.int_br.add_port(int_veth) - self.phys_ofports[physical_network] = br.add_port(phys_veth) + int_br = self.int_br + self.int_ofports[physical_network] = int(int_br.add_port(int_veth)) + self.phys_ofports[physical_network] = int(br.add_port(phys_veth)) return (int_veth, phys_veth) - def _phys_br_block_untranslated_traffic(self, br, physical_network): - match = self.int_br.ofparser.OFPMatch(in_port=int( - self.int_ofports[physical_network])) - msg = self.int_br.ofparser.OFPFlowMod(self.int_br.datapath, - priority=2, match=match) - self.ryu_send_msg(msg) - match = br.ofparser.OFPMatch(in_port=int( - self.phys_ofports[physical_network])) - msg = br.ofparser.OFPFlowMod(br.datapath, priority=2, match=match) - self.ryu_send_msg(msg) - def _phys_br_enable_veth_to_pass_traffic(self, int_veth, phys_veth): # enable veth to pass traffic int_veth.link.set_up() @@ -939,7 +596,6 @@ class OFANeutronAgent(n_rpc.RpcCallback, phys_veth_name, physical_network, ip_wrapper) - self._phys_br_block_untranslated_traffic(br, physical_network) self._phys_br_enable_veth_to_pass_traffic(int_veth, phys_veth) def setup_physical_bridges(self, bridge_mappings): @@ -967,22 +623,7 @@ class OFANeutronAgent(n_rpc.RpcCallback, {'physical_network': physical_network, 'bridge': bridge}) raise SystemExit(1) - br = OVSBridge(bridge, self.root_helper, self.ryuapp) - br.setup_ofp() - msg = br.ofparser.OFPFlowMod(br.datapath, - table_id=ryu_ofp13.OFPTT_ALL, - command=ryu_ofp13.OFPFC_DELETE, - out_group=ryu_ofp13.OFPG_ANY, - out_port=ryu_ofp13.OFPP_ANY) - self.ryu_send_msg(msg) - actions = [br.ofparser.OFPActionOutput(ryu_ofp13.OFPP_NORMAL, 0)] - instructions = [br.ofparser.OFPInstructionActions( - ryu_ofp13.OFPIT_APPLY_ACTIONS, - actions)] - msg = br.ofparser.OFPFlowMod(br.datapath, - priority=1, - instructions=instructions) - self.ryu_send_msg(msg) + br = Bridge(bridge, self.root_helper, self.ryuapp) self.phys_brs[physical_network] = br self._phys_br_patch_physical_bridge_with_integration_bridge( @@ -994,7 +635,6 @@ class OFANeutronAgent(n_rpc.RpcCallback, port_info = {'current': cur_ports} if updated_ports is None: updated_ports = set() - updated_ports.update(self._find_lost_vlan_port(registered_ports)) if updated_ports: # Some updated ports might have been removed in the # meanwhile, and therefore should not be processed. @@ -1013,33 +653,6 @@ class OFANeutronAgent(n_rpc.RpcCallback, port_info['removed'] = registered_ports - cur_ports return port_info - def _find_lost_vlan_port(self, registered_ports): - """Return ports which have lost their vlan tag. - - The returned value is a set of port ids of the ports concerned by a - vlan tag loss. - """ - # TODO(yamamoto): stop using ovsdb - # an idea is to use metadata instead of tagged vlans. - # cf. blueprint ofagent-merge-bridges - port_tags = self.int_br.get_port_tag_dict() - changed_ports = set() - for lvm in self.local_vlan_map.values(): - for port in registered_ports: - if ( - port in lvm.vif_ports - and port in port_tags - and port_tags[port] != lvm.vlan - ): - LOG.info( - _("Port '%(port_name)s' has lost " - "its vlan tag '%(vlan_tag)d'!"), - {'port_name': port, - 'vlan_tag': lvm.vlan} - ) - changed_ports.add(port) - return changed_ports - def treat_vif_port(self, vif_port, port_id, network_id, network_type, physical_network, segmentation_id, admin_state_up): if vif_port: @@ -1059,34 +672,25 @@ class OFANeutronAgent(n_rpc.RpcCallback, LOG.debug(_("No VIF port for port %s defined on agent."), port_id) def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type): - ofport = br.add_tunnel_port(port_name, - remote_ip, - self.local_ip, - tunnel_type, - self.vxlan_udp_port, - self.dont_fragment) - ofport_int = -1 + ofport_str = br.add_tunnel_port(port_name, + remote_ip, + self.local_ip, + tunnel_type, + self.vxlan_udp_port, + self.dont_fragment) + ofport = -1 try: - ofport_int = int(ofport) + ofport = int(ofport_str) except (TypeError, ValueError): LOG.exception(_("ofport should have a value that can be " "interpreted as an integer")) - if ofport_int < 0: + if ofport < 0: LOG.error(_("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': tunnel_type, 'ip': remote_ip}) return 0 - self.tun_br_ofports[tunnel_type][remote_ip] = ofport - # Add flow in default table to resubmit to the right - # tunelling table (lvid will be set in the latter) - match = br.ofparser.OFPMatch(in_port=int(ofport)) - instructions = [br.ofparser.OFPInstructionGotoTable( - table_id=constants.TUN_TABLE[tunnel_type])] - msg = br.ofparser.OFPFlowMod(br.datapath, - priority=1, - match=match, - instructions=instructions) - self.ryu_send_msg(msg) + self.tun_ofports[tunnel_type][remote_ip] = ofport + br.check_in_port_add_tunnel_port(tunnel_type, ofport) return ofport def setup_tunnel_port(self, br, remote_ip, network_type): @@ -1100,23 +704,14 @@ class OFANeutronAgent(n_rpc.RpcCallback, return ofport def _remove_tunnel_port(self, br, tun_ofport, tunnel_type): - datapath = br.datapath - ofp = datapath.ofproto - ofpp = datapath.ofproto_parser - for remote_ip, ofport in self.tun_br_ofports[tunnel_type].items(): + for remote_ip, ofport in self.tun_ofports[tunnel_type].items(): if ofport == tun_ofport: + br.check_in_port_delete_port(ofport) port_name = self._create_tunnel_port_name(tunnel_type, remote_ip) if port_name: br.delete_port(port_name) - match = ofpp.OFPMatch(in_port=int(ofport)) - msg = ofpp.OFPFlowMod(datapath, - command=ofp.OFPFC_DELETE, - out_group=ofp.OFPG_ANY, - out_port=ofp.OFPP_ANY, - match=match) - self.ryu_send_msg(msg) - self.tun_br_ofports[tunnel_type].pop(remote_ip, None) + self.tun_ofports[tunnel_type].pop(remote_ip, None) def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type): # Check if this tunnel port is still used @@ -1154,6 +749,7 @@ class OFANeutronAgent(n_rpc.RpcCallback, if 'port_id' in details: LOG.info(_("Port %(device)s updated. Details: %(details)s"), {'device': device, 'details': details}) + port.vif_mac = details.get('mac_address') self.treat_vif_port(port, details['port_id'], details['network_id'], details['network_type'], @@ -1344,7 +940,6 @@ def create_agent_config_map(config): kwargs = dict( integ_br=config.OVS.integration_bridge, - tun_br=config.OVS.tunnel_bridge, local_ip=config.OVS.local_ip, bridge_mappings=bridge_mappings, root_helper=config.AGENT.root_helper, diff --git a/neutron/plugins/ofagent/agent/ofswitch.py b/neutron/plugins/ofagent/agent/ofswitch.py new file mode 100644 index 000000000..eafe33d55 --- /dev/null +++ b/neutron/plugins/ofagent/agent/ofswitch.py @@ -0,0 +1,78 @@ +# Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi +# 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) diff --git a/neutron/plugins/ofagent/agent/ports.py b/neutron/plugins/ofagent/agent/ports.py index 995118fbf..4389b7957 100644 --- a/neutron/plugins/ofagent/agent/ports.py +++ b/neutron/plugins/ofagent/agent/ports.py @@ -1,4 +1,5 @@ # Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi # 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) diff --git a/neutron/plugins/ofagent/agent/tables.py b/neutron/plugins/ofagent/agent/tables.py new file mode 100644 index 000000000..f78c68f94 --- /dev/null +++ b/neutron/plugins/ofagent/agent/tables.py @@ -0,0 +1,62 @@ +# Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi +# 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() diff --git a/neutron/tests/unit/ofagent/fake_oflib.py b/neutron/tests/unit/ofagent/fake_oflib.py index 01d60d2a4..5c3d6fef4 100644 --- a/neutron/tests/unit/ofagent/fake_oflib.py +++ b/neutron/tests/unit/ofagent/fake_oflib.py @@ -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, diff --git a/neutron/tests/unit/ofagent/ofa_test_base.py b/neutron/tests/unit/ofagent/ofa_test_base.py index 9b4643a20..160e152ed 100644 --- a/neutron/tests/unit/ofagent/ofa_test_base.py +++ b/neutron/tests/unit/ofagent/ofa_test_base.py @@ -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') diff --git a/neutron/tests/unit/ofagent/test_arp_lib.py b/neutron/tests/unit/ofagent/test_arp_lib.py index 18078d464..a7b6bd5b3 100644 --- a/neutron/tests/unit/ofagent/test_arp_lib.py +++ b/neutron/tests/unit/ofagent/test_arp_lib.py @@ -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) diff --git a/neutron/tests/unit/ofagent/test_ofa_flows.py b/neutron/tests/unit/ofagent/test_ofa_flows.py new file mode 100644 index 000000000..2f07448aa --- /dev/null +++ b/neutron/tests/unit/ofagent/test_ofa_flows.py @@ -0,0 +1,351 @@ +# Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi +# 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) diff --git a/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py b/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py index e0bdaf709..18edc97cf 100644 --- a/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py +++ b/neutron/tests/unit/ofagent/test_ofa_neutron_agent.py @@ -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) diff --git a/neutron/tests/unit/ofagent/test_ofswitch.py b/neutron/tests/unit/ofagent/test_ofswitch.py new file mode 100644 index 000000000..bf35e2ed6 --- /dev/null +++ b/neutron/tests/unit/ofagent/test_ofswitch.py @@ -0,0 +1,82 @@ +# Copyright (C) 2014 VA Linux Systems Japan K.K. +# Copyright (C) 2014 YAMAMOTO Takashi +# 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) -- 2.45.2