Merge branch 'master' of github.com:/floodlight/oftest
diff --git a/tests/basic.py b/tests/basic.py
index 220f7c8..9dec947 100644
--- a/tests/basic.py
+++ b/tests/basic.py
@@ -90,6 +90,27 @@
             sys.exit(1)
         basic_logger.info("Connected " + str(self.controller.switch_addr))
 
+    def inheritSetup(self, parent):
+        """
+        Inherit the setup of a parent
+
+        This allows running at test from within another test.  Do the
+        following:
+
+        sub_test = SomeTestClass()  # Create an instance of the test class
+        sub_test.inheritSetup(self) # Inherit setup of parent
+        sub_test.runTest()          # Run the test
+
+        Normally, only the parent's setUp and tearDown are called and
+        the state after the sub_test is run must be taken into account
+        by subsequent operations.
+        """
+        self.logger = parent.logger
+        self.config = parent.config
+        basic_logger.info("** Setup " + str(self) + " inheriting from "
+                          + str(parent))
+        self.controller = parent.controller
+        
     def tearDown(self):
         basic_logger.info("** END TEST CASE " + str(self))
         self.controller.shutdown()
@@ -120,6 +141,15 @@
         for of_port, ifname in basic_port_map.items():
             self.dataplane.port_add(ifname, of_port)
 
+    def inheritSetup(self, parent):
+        """
+        Inherit the setup of a parent
+
+        See SimpleProtocol.inheritSetup
+        """
+        SimpleProtocol.inheritSetup(self, parent)
+        self.dataplane = parent.dataplane
+
     def tearDown(self):
         basic_logger.info("Teardown for simple dataplane test")
         SimpleProtocol.tearDown(self)
diff --git a/tests/flow_stats.py b/tests/flow_stats.py
index f7a8ad2..91c67a6 100644
--- a/tests/flow_stats.py
+++ b/tests/flow_stats.py
@@ -20,13 +20,13 @@
 from testutils import *
 from time import sleep
 
-#@var port_map Local copy of the configuration map from OF port
+#@var fs_port_map Local copy of the configuration map from OF port
 # numbers to OS interfaces
-pa_port_map = None
-#@var pa_logger Local logger object
-pa_logger = None
-#@var pa_config Local copy of global configuration data
-pa_config = None
+fs_port_map = None
+#@var fs_logger Local logger object
+fs_logger = None
+#@var fs_config Local copy of global configuration data
+fs_config = None
 
 # TODO: ovs has problems with VLAN id?
 WILDCARD_VALUES = [ofp.OFPFW_IN_PORT,
@@ -49,14 +49,14 @@
     @param config The configuration dictionary; see oft
     """
 
-    global pa_port_map
-    global pa_logger
-    global pa_config
+    global fs_port_map
+    global fs_logger
+    global fs_config
 
-    pa_logger = logging.getLogger("pkt_act")
-    pa_logger.info("Initializing test set")
-    pa_port_map = config["port_map"]
-    pa_config = config
+    fs_logger = logging.getLogger("flow_stats")
+    fs_logger.info("Initializing test set")
+    fs_port_map = config["port_map"]
+    fs_config = config
 
 class SingleFlowStats(basic.SimpleDataPlane):
     """
@@ -77,13 +77,13 @@
 
         all_packets_received = 0
         for i in range(0,test_timeout):
-            pa_logger.info("Sending stats request")
-            rv = self.controller.message_send(stat_req)
-            self.assertTrue(rv != -1, "Error sending flow stat req")
-            do_barrier(self.controller)
-
-            (response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
-            self.assertTrue(len(response.stats) == 1, "Did not receive flow stats reply")
+            fs_logger.info("Sending stats request")
+            response, pkt = self.controller.transact(stat_req,
+                                                     timeout=test_timeout)
+            self.assertTrue(response is not None, 
+                            "No response to stats request")
+            self.assertTrue(len(response.stats) == 1,
+                            "Did not receive flow stats reply")
             for obj in response.stats:
                 # TODO: pad1 and pad2 fields may be nonzero, is this a bug?
                 # for now, just clear them so the assert is simpler
@@ -91,7 +91,7 @@
                 #obj.match.pad2 = [0, 0]
                 #self.assertEqual(match, obj.match,
                 #                 "Matches do not match")
-                pa_logger.info("Received " + str(obj.packet_count) + " packets")
+                fs_logger.info("Received " + str(obj.packet_count) + " packets")
                 if obj.packet_count == packet_count:
                     all_packets_received = 1
 
@@ -103,16 +103,16 @@
                         "Packet count does not match number sent")
 
     def runTest(self):
-        global pa_port_map
+        global fs_port_map
 
         # TODO: set from command-line parameter
         test_timeout = 60
 
-        of_ports = pa_port_map.keys()
+        of_ports = fs_port_map.keys()
         of_ports.sort()
         self.assertTrue(len(of_ports) > 1, "Not enough ports for test")
 
-        rc = delete_all_flows(self.controller, pa_logger)
+        rc = delete_all_flows(self.controller, fs_logger)
         self.assertEqual(rc, 0, "Failed to delete all flows")
 
         # build packet
@@ -126,7 +126,7 @@
         # build flow
         ingress_port = of_ports[0];
         egress_port = of_ports[1];
-        pa_logger.info("Ingress " + str(ingress_port) + 
+        fs_logger.info("Ingress " + str(ingress_port) + 
                        " to egress " + str(egress_port))
         match.in_port = ingress_port
         flow_mod_msg = message.flow_mod()
@@ -139,7 +139,7 @@
         self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
        
         # send flow
-        pa_logger.info("Inserting flow")
+        fs_logger.info("Inserting flow")
         rv = self.controller.message_send(flow_mod_msg)
         self.assertTrue(rv != -1, "Error installing flow mod")
         do_barrier(self.controller)
@@ -149,15 +149,15 @@
 
         # send packet N times
         num_sends = random.randint(10,20)
-        pa_logger.info("Sending " + str(num_sends) + " test packets")
+        fs_logger.info("Sending " + str(num_sends) + " test packets")
         for i in range(0,num_sends):
-            pa_logger.info("Sending packet to dp port " + 
+            fs_logger.info("Sending packet to dp port " + 
                            str(ingress_port))
             self.dataplane.send(ingress_port, str(pkt))
             (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
                                                                 test_timeout)
             self.assertTrue(rcv_pkt is not None, "Did not receive packet")
-            pa_logger.debug("Packet len " + str(len(pkt)) + " in on " + 
+            fs_logger.debug("Packet len " + str(len(pkt)) + " in on " + 
                             str(rcv_port))
             self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
             for j in range(0,test_timeout):
@@ -204,19 +204,19 @@
         act.port = egress_port
         self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
 
-        pa_logger.info("Ingress " + str(ingress_port) + 
+        fs_logger.info("Ingress " + str(ingress_port) + 
                        " to egress " + str(egress_port))
 
         return flow_mod_msg
 
     def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
-        pa_logger.info("Sending packet to dp port " + 
+        fs_logger.info("Sending packet to dp port " + 
                        str(ingress_port))
         self.dataplane.send(ingress_port, str(pkt))
         (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
                                                             test_timeout)
         self.assertTrue(rcv_pkt is not None, "Did not receive packet")
-        pa_logger.debug("Packet len " + str(len(pkt)) + " in on " + 
+        fs_logger.debug("Packet len " + str(len(pkt)) + " in on " + 
                         str(rcv_port))
         self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
         for j in range(0,test_timeout):
@@ -234,12 +234,11 @@
 
         all_packets_received = 0
         for i in range(0,test_timeout):
-            pa_logger.info("Sending stats request")
-            rv = self.controller.message_send(stat_req)
-            self.assertTrue(rv != -1, "Error sending flow stat req")
-            do_barrier(self.controller)
-
-            (response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
+            fs_logger.info("Sending stats request")
+            response, pkt = self.controller.transact(stat_req,
+                                                     timeout=test_timeout)
+            self.assertTrue(response is not None,
+                            "No response to stats request")
             self.assertTrue(len(response.stats) >= 1,
                             "Did not receive flow stats reply")
             total_packets = 0
@@ -250,7 +249,7 @@
                 #obj.match.pad2 = [0, 0]
                 #self.assertEqual(match, obj.match,
                 #                 "Matches do not match")
-                pa_logger.info("Received " + str(obj.packet_count) + " packets")
+                fs_logger.info("Received " + str(obj.packet_count) + " packets")
                 total_packets += obj.packet_count
             if total_packets == packet_count:
                 all_packets_received = 1
@@ -261,19 +260,19 @@
                         "Packet count does not match number sent")
 
     def runTest(self):
-        global pa_port_map
+        global fs_port_map
 
         # TODO: set from command-line parameter
         test_timeout = 60
 
-        of_ports = pa_port_map.keys()
+        of_ports = fs_port_map.keys()
         of_ports.sort()
         self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
         ingress_port = of_ports[0];
         egress_port1 = of_ports[1];
         egress_port2 = of_ports[2];
 
-        rc = delete_all_flows(self.controller, pa_logger)
+        rc = delete_all_flows(self.controller, fs_logger)
         self.assertEqual(rc, 0, "Failed to delete all flows")
 
         pkt1 = simple_tcp_packet()
@@ -282,18 +281,18 @@
         pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
         flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
        
-        pa_logger.info("Inserting flow1")
+        fs_logger.info("Inserting flow1")
         rv = self.controller.message_send(flow_mod_msg1)
         self.assertTrue(rv != -1, "Error installing flow mod")
-        pa_logger.info("Inserting flow2")
+        fs_logger.info("Inserting flow2")
         rv = self.controller.message_send(flow_mod_msg2)
         self.assertTrue(rv != -1, "Error installing flow mod")
         do_barrier(self.controller)
 
         num_pkt1s = random.randint(10,30)
-        pa_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
+        fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
         num_pkt2s = random.randint(10,30)
-        pa_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
+        fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
         for i in range(0,num_pkt1s):
             self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
         for i in range(0,num_pkt2s):
@@ -337,19 +336,19 @@
         act.port = egress_port
         self.assertTrue(flow_mod_msg.actions.add(act), "Could not add action")
 
-        pa_logger.info("Ingress " + str(ingress_port) + 
+        fs_logger.info("Ingress " + str(ingress_port) + 
                        " to egress " + str(egress_port))
 
         return flow_mod_msg
 
     def sendPacket(self, pkt, ingress_port, egress_port, test_timeout):
-        pa_logger.info("Sending packet to dp port " + 
+        fs_logger.info("Sending packet to dp port " + 
                        str(ingress_port))
         self.dataplane.send(ingress_port, str(pkt))
         (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=
                                                             test_timeout)
         self.assertTrue(rcv_pkt is not None, "Did not receive packet")
-        pa_logger.debug("Packet len " + str(len(pkt)) + " in on " + 
+        fs_logger.debug("Packet len " + str(len(pkt)) + " in on " + 
                         str(rcv_port))
         self.assertEqual(rcv_port, egress_port, "Unexpected receive port")
         for j in range(0,test_timeout):
@@ -368,19 +367,18 @@
 
         all_packets_received = 0
         for i in range(0,test_timeout):
-            pa_logger.info("Sending stats request")
-            rv = self.controller.message_send(stat_req)
-            self.assertTrue(rv != -1, "Error sending flow stat req")
-            do_barrier(self.controller)
-
-            (response, raw) = self.controller.poll(ofp.OFPT_STATS_REPLY, 2)
+            fs_logger.info("Sending stats request")
+            response, pkt = self.controller.transact(stat_req,
+                                                     timeout=test_timeout)
+            self.assertTrue(response is not None, 
+                            "No response to stats request")
             self.assertTrue(len(response.stats) == 1, 
                             "Did not receive flow stats reply")
             for obj in response.stats:
                 self.assertTrue(obj.flow_count == flow_count,
                                 "Flow count " + str(obj.flow_count) +
                                 " does not match expected " + str(flow_count))
-                pa_logger.info("Received " + str(obj.packet_count) + " packets")
+                fs_logger.info("Received " + str(obj.packet_count) + " packets")
                 if obj.packet_count == packet_count:
                     all_packets_received = 1
 
@@ -392,19 +390,19 @@
                         "Packet count does not match number sent")
 
     def runTest(self):
-        global pa_port_map
+        global fs_port_map
 
         # TODO: set from command-line parameter
         test_timeout = 60
 
-        of_ports = pa_port_map.keys()
+        of_ports = fs_port_map.keys()
         of_ports.sort()
         self.assertTrue(len(of_ports) >= 3, "Not enough ports for test")
         ingress_port = of_ports[0];
         egress_port1 = of_ports[1];
         egress_port2 = of_ports[2];
 
-        rc = delete_all_flows(self.controller, pa_logger)
+        rc = delete_all_flows(self.controller, fs_logger)
         self.assertEqual(rc, 0, "Failed to delete all flows")
 
         pkt1 = simple_tcp_packet()
@@ -413,18 +411,18 @@
         pkt2 = simple_tcp_packet(dl_src='0:7:7:7:7:7')
         flow_mod_msg2 = self.buildFlowModMsg(pkt2, ingress_port, egress_port2)
        
-        pa_logger.info("Inserting flow1")
+        fs_logger.info("Inserting flow1")
         rv = self.controller.message_send(flow_mod_msg1)
         self.assertTrue(rv != -1, "Error installing flow mod")
-        pa_logger.info("Inserting flow2")
+        fs_logger.info("Inserting flow2")
         rv = self.controller.message_send(flow_mod_msg2)
         self.assertTrue(rv != -1, "Error installing flow mod")
         do_barrier(self.controller)
 
         num_pkt1s = random.randint(10,30)
-        pa_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
+        fs_logger.info("Sending " + str(num_pkt1s) + " pkt1s")
         num_pkt2s = random.randint(10,30)
-        pa_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
+        fs_logger.info("Sending " + str(num_pkt2s) + " pkt2s")
         for i in range(0,num_pkt1s):
             self.sendPacket(pkt1, ingress_port, egress_port1, test_timeout)
         for i in range(0,num_pkt2s):
diff --git a/tests/pktact.py b/tests/pktact.py
index c045e5d..2ee1968 100644
--- a/tests/pktact.py
+++ b/tests/pktact.py
@@ -28,6 +28,7 @@
 import oftest.action as action
 import oftest.parse as parse
 import basic
+import time
 
 from testutils import *
 
@@ -79,8 +80,6 @@
     @param config The configuration dictionary; see oft
     """
 
-    basic.test_set_init(config)
-
     global pa_port_map
     global pa_logger
     global pa_config
@@ -1143,6 +1142,57 @@
         flow_match_test(self, pa_port_map, pkt=pkt, exp_pkt=exp_pkt, 
                         action_list=acts, max_test=2, egr_count=-1)
 
+# You can pick and choose these by commenting tests in or out
+iter_classes = [
+    basic.PacketIn,
+    basic.PacketOut,
+    DirectPacket,
+    DirectTwoPorts,
+    DirectMC,
+    AllWildcardMatch,
+    AllWildcardMatchTagged,
+    SingleWildcardMatch,
+    SingleWildcardMatchTagged,
+    ExactMatch,
+    ExactMatchTagged,
+    SingleWildcardMatch,
+    ModifyL2Src,
+    ModifyL2Dst,
+    ModifyL2SrcMC,
+    ModifyL2DstMC,
+    ModifyL2SrcDstMC
+    ]
+
+class IterCases(BaseMatchCase):
+    def runTest(self):
+        count = test_param_get(self.config, 'iter_count', default=10)
+        tests_done = 0
+        pa_logger.info("Running iteration test " + str(count) + " times")
+        start = time.time()
+        last = start
+        for idx in range(count):
+            pa_logger.info("Iteration " + str(idx + 1))
+            for cls in iter_classes:
+                test = cls()
+                test.inheritSetup(self)
+                test.runTest()
+                tests_done += 1
+                if time.time() - last > 60:
+                    last = time.time()
+                    print("IterCases: Ran %d tests in %d " %
+                          (tests_done, last - start) + 
+                          "seconds so far")
+        stats = all_stats_get(self)
+        last = time.time()
+        pa_logger.info("\nIterCases ran %d tests in %d seconds." %
+                       (tests_done, last - start))
+        pa_logger.info("    flows: %d. packets: %d. bytes: %d" %
+                       (stats["flows"], stats["packets"], stats["bytes"]))
+        pa_logger.info("    active: %d. lookups: %d. matched %d." %
+                       (stats["active"], stats["lookups"], stats["matched"]))
+
+# Don't run by default
+test_prio["IterCases"] = -1
 
 #@todo Need to implement tagged versions of the above tests
 #
diff --git a/tests/testutils.py b/tests/testutils.py
index 5a99a00..221fc67 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -455,7 +455,7 @@
 
     return request
 
-def flow_msg_install(parent, request, clear_table=True):
+def flow_msg_install(parent, request):
     """
     Install a flow mod message in the switch
 
@@ -463,6 +463,8 @@
     @param request The request, all set to go
     @param clear_table If true, clear the flow table before installing
     """
+
+    clear_table = test_param_get(parent.config, 'clear_table', default=True)
     if clear_table:
         parent.logger.debug("Clear flow table")
         rc = delete_all_flows(parent.controller, parent.logger)
@@ -777,3 +779,38 @@
     else:
         sys.stderr.write("(S)")
 
+
+def all_stats_get(parent):
+    """
+    Get the aggregate stats for all flows in the table
+    @param parent Test instance with controller connection and assert
+    @returns dict with keys flows, packets, bytes, active (flows), 
+    lookups, matched
+    """
+    stat_req = message.aggregate_stats_request()
+    stat_req.match = ofp.ofp_match()
+    stat_req.match.wildcards = ofp.OFPFW_ALL
+    stat_req.table_id = 0xff
+    stat_req.out_port = ofp.OFPP_NONE
+
+    rv = {}
+
+    (reply, pkt) = parent.controller.transact(stat_req, timeout=2)
+    parent.assertTrue(len(reply.stats) == 1, "Did not receive flow stats reply")
+
+    for obj in reply.stats:
+        (rv["flows"], rv["packets"], rv["bytes"]) = (obj.flow_count, 
+                                                  obj.packet_count, obj.byte_count)
+        break
+
+    request = message.table_stats_request()
+    (reply , pkt) = parent.controller.transact(request, timeout=2)
+
+    
+    (rv["active"], rv["lookups"], rv["matched"]) = (0,0,0)
+    for obj in reply.stats:
+        rv["active"] += obj.active_count
+        rv["lookups"] += obj.lookup_count
+        rv["matched"] += obj.matched_count
+
+    return rv