Initial set of Fabric switch test cases

Change-Id: I86fd2b67d3b773aa496f5ef61f1e1fdf51fd9925
diff --git a/Fabric/Utilities/src/python/oftest/ofdpa_utils.py b/Fabric/Utilities/src/python/oftest/ofdpa_utils.py
new file mode 100755
index 0000000..8c97b1f
--- /dev/null
+++ b/Fabric/Utilities/src/python/oftest/ofdpa_utils.py
@@ -0,0 +1,184 @@
+
+# Copyright 2017-present Open Networking Foundation
+#
+# 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.
+
+
+"""
+
+A set of inter-test utility functions for dealing with OF-DPA
+
+
+"""
+
+import logging, subprocess, time
+import ofp
+
+from oftest import config
+from oftest.testutils import *
+
+def forceOfdpaRestart( user ):
+    output = 1;
+    credential = user;
+    test = subprocess.Popen(["ssh", credential, "service ofdpa restart &> /dev/null"]);
+    time.sleep(1);
+    while output < 10:
+        output = int(subprocess.check_output(["ssh", credential, "client_cfg_purge | wc -l"]));
+        time.sleep(1);
+    subprocess.Popen(["ssh", credential, "brcm-indigo-ofdpa-ofagent -t 10.128.0.220 &> /dev/null"], stdout=subprocess.PIPE);
+
+def forceOfdpaStop( user ):
+    subprocess.Popen(["ssh", user, "ps ax | grep 'brcm-indigo-ofdpa-ofagent' | awk '{print $1}' | xargs sudo kill"], stdout=subprocess.PIPE);
+
+
+class table(object):
+    """ Metadata on each OFDPA table """
+    def __init__(self, table_id, table_name):
+        self.table_id = table_id
+        self.table_name = table_name
+    # TODO consider adding type checking verification here
+
+INGRESS_TABLE           = table(0, "Ingress")
+VLAN_TABLE              = table(10, "VLAN")
+MACTERM_TABLE           = table(20, "MacTerm")
+UNICAST_ROUTING_TABLE   = table(30, "Unicast Routing")
+MULTICAST_ROUTING_TABLE = table(40, "Multicast Routing")
+BRIDGING_TABLE          = table(50, "Bridging Table")
+ACL_TABLE               = table(60, "ACL Policy Table")
+#.... FIXME add all tables
+
+DEFAULT_VLAN = 1
+
+def enableVlanOnPort(controller, vlan, port=ofp.OFPP_ALL, priority=0):
+    if port == ofp.OFPP_ALL:
+        ports = sorted(config["port_map"].keys())
+    else:
+        ports = [port]
+    for port in ports:
+        tagged_match = ofp.match([
+                ofp.oxm.in_port(port),
+                ofp.oxm.vlan_vid(vlan | ofp.OFPVID_PRESENT)
+                ])
+
+        request = ofp.message.flow_add(
+            table_id = VLAN_TABLE.table_id,
+            cookie = 0xdead,
+            match = tagged_match,
+            instructions = [
+                ofp.instruction.goto_table(MACTERM_TABLE.table_id),
+#                 ofp.instruction.apply_actions(
+#                     actions=[
+#                         ofp.action.push_vlan(ethertype=0x8100), # DO NOT PUT THIS FOR OF-DPA 2.0 EA1 - seems to not matter for EA2
+#                         ofp.action.set_field(ofp.oxm.vlan_vid( ofp.OFPVID_PRESENT | vlan))
+#                     ]),
+                    ],
+            buffer_id = ofp.OFP_NO_BUFFER,
+            priority = priority)
+
+        logging.info("Inserting vlan rule allowing tagged vlan %d on port %d" % (vlan, port))
+        controller.message_send(request)
+        do_barrier(controller)
+        verify_no_errors(controller)
+
+
+def installDefaultVlan(controller, vlan=DEFAULT_VLAN, port=ofp.OFPP_ALL, priority=0):
+    """ Insert a rule that maps all untagged traffic to vlan $vlan
+
+    In OFDPA, table 10 (the vlan table) requires that all traffic be
+    mapped to an internal vlan else the packets be dropped.  This function
+    sets up a default vlan mapping all untagged traffic to an internal VLAN.
+
+    With OF-DPA, before you can insert a 'untagged to X' rule on a
+    port, you must first insert a 'X --> X' rule for the same port.
+
+    Further, the 'X --> X' rule must set ofp.OFPVID_PRESENT even
+    though 'X' is non-zero.
+
+    The 'controller' variable is self.controller from a test
+    """
+    # OFDPA seems to be dumb and wants each port set individually
+    #       Can't set all ports by using OFPP_ALL
+    if port == ofp.OFPP_ALL:
+        ports = sorted(config["port_map"].keys())
+    else:
+        ports = [port]
+
+    for port in ports:
+        # enable this vlan on this port before we can map untagged packets to the vlan
+        enableVlanOnPort(controller, vlan, port)
+
+        untagged_match = ofp.match([
+                ofp.oxm.in_port(port),
+                # OFDPA 2.0 says untagged is vlan_id=0, mask=ofp.OFPVID_PRESENT
+                ofp.oxm.vlan_vid_masked(0,ofp.OFPVID_PRESENT)    # WTF OFDPA 2.0EA2 -- really!?
+                ])
+
+        request = ofp.message.flow_add(
+            table_id = VLAN_TABLE.table_id,
+            cookie = 0xbeef,
+            match = untagged_match,
+            instructions = [
+                ofp.instruction.apply_actions(
+                    actions=[
+                        #ofp.action.push_vlan(ethertype=0x8100),
+                        ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | vlan))
+                    ]),
+                ofp.instruction.goto_table(MACTERM_TABLE.table_id)   
+                    ],
+            buffer_id = ofp.OFP_NO_BUFFER,
+            priority = priority)
+
+        logging.info("Inserting default vlan sending all untagged traffic to vlan %d on port %d" % (vlan, port))
+        controller.message_send(request)
+        do_barrier(controller)
+        verify_no_errors(controller)
+
+
+_group_types = {
+    "L2 Interface": 0,
+    "L2 Rewrite" : 1,
+    "L3 Unicast" : 2,
+    "L2 Multicast" : 3,
+    "L2 Flood" : 4,
+    "L3 Interface" : 5,
+    "L3 Multicast": 6,
+    "L3 ECMP": 7,
+    "L2 Data Center Overlay": 8,
+    "MPLS Label" : 9,
+    "MPLS Forwarding" :10,
+    "L2 Unfiltered Interface": 11,
+    "L2 Loopback": 12,
+}
+
+
+def makeGroupID(groupType, local_id):
+    """ Group IDs in OF-DPA have rich meaning
+
+    @param groupType is a key in _group_types
+    @param local_id is an integer 0<= local_id < 2**27,
+        but it may have more semantic meaning depending on the
+        groupType
+
+
+    Read Section 4.3 of the OF-DPA manual on groups for 
+    details
+    """
+    if groupType not in _group_types:
+        raise KeyError("%s not a valid OF-DPA group type" % groupType)
+    if local_id < 0 or local_id >=134217728:
+        raise ValueError("local_id %d must be  0<= local_id < 2**27" % local_id)
+    return (_group_types[groupType] << 28) + local_id
+
+
+def delete_all_recursive_groups(controller):
+    pass