]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
ofagent: merge br-tun into br-int
authorYAMAMOTO Takashi <yamamoto@valinux.co.jp>
Fri, 18 Apr 2014 06:48:20 +0000 (15:48 +0900)
committerYAMAMOTO Takashi <yamamoto@valinux.co.jp>
Mon, 1 Sep 2014 00:00:03 +0000 (09:00 +0900)
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

15 files changed:
neutron/plugins/ofagent/README
neutron/plugins/ofagent/agent/arp_lib.py
neutron/plugins/ofagent/agent/constants.py [new file with mode: 0644]
neutron/plugins/ofagent/agent/flows.py [new file with mode: 0644]
neutron/plugins/ofagent/agent/metadata.py [new file with mode: 0644]
neutron/plugins/ofagent/agent/ofa_neutron_agent.py
neutron/plugins/ofagent/agent/ofswitch.py [new file with mode: 0644]
neutron/plugins/ofagent/agent/ports.py
neutron/plugins/ofagent/agent/tables.py [new file with mode: 0644]
neutron/tests/unit/ofagent/fake_oflib.py
neutron/tests/unit/ofagent/ofa_test_base.py
neutron/tests/unit/ofagent/test_arp_lib.py
neutron/tests/unit/ofagent/test_ofa_flows.py [new file with mode: 0644]
neutron/tests/unit/ofagent/test_ofa_neutron_agent.py
neutron/tests/unit/ofagent/test_ofswitch.py [new file with mode: 0644]

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