Merge pull request #48 from floodlight/det-monitor-pkt-in

Test to monitor and report packet-in rate.  Disabled by default as it is "blocking".  Note that this only supports a single controller (determined by the command line parameters).
diff --git a/src/python/oftest/controller.py b/src/python/oftest/controller.py
index 1191c37..d802717 100644
--- a/src/python/oftest/controller.py
+++ b/src/python/oftest/controller.py
@@ -125,6 +125,7 @@
         # Protected by the packets_cv lock / condition variable
         self.packets = []
         self.packets_cv = Condition()
+        self.packet_in_count = 0
 
         # Settings
         self.max_pkts = max_pkts
@@ -258,6 +259,10 @@
                         self.message_send(rep.pack())
                         continue
 
+                # Generalize to counters for all packet types?
+                if msg.type == ofp.OFPT_PACKET_IN:
+                    self.packet_in_count += 1
+
                 # Log error messages
                 if hdr_type == ofp.OFPT_ERROR:
                     if msg.err_type in ofp.ofp_error_type_map:
@@ -582,7 +587,10 @@
         If an error occurs, (None, None) is returned
         """
 
-        exp_msg_str = ofp.ofp_type_map.get(exp_msg, "unknown (%d)" % exp_msg)
+        exp_msg_str = "unspecified"
+        if exp_msg:
+            exp_msg_str = ofp.ofp_type_map.get(exp_msg, "unknown (%d)" % 
+                                               exp_msg)
 
         if exp_msg is not None:
             self.logger.debug("Poll for %s", exp_msg_str)
@@ -688,6 +696,16 @@
 
         return 0 # for backwards compatibility
 
+    def clear_queue(self):
+        """
+        Clear the input queue and report the number of messages
+        that were in it
+        """
+        enqueued_pkt_count = len(self.packets)
+        with self.packets_cv:
+            self.packets = []
+        return enqueued_pkt_count
+
     def __str__(self):
         string = "Controller:\n"
         string += "  state           " + self.dbg_state + "\n"
diff --git a/tests/cxn.py b/tests/cxn.py
index 1a00f1f..d1f5db8 100644
--- a/tests/cxn.py
+++ b/tests/cxn.py
@@ -121,7 +121,8 @@
 
     def __init__(self, keep_alive=True, cxn_cycles=5,
                  controller_timeout=-1, hello_timeout=5, 
-                 features_req_timeout=5, disconnected_timeout=3):
+                 features_req_timeout=5, disconnected_timeout=3,
+                 report_pkts=False):
         BaseHandshake.__init__(self)
         self.buildControllerList()
         self.keep_alive = keep_alive
@@ -135,7 +136,52 @@
             or features_req_timeout
         self.disconnected_timeout = test_param_get('disconnected_timeout') \
             or disconnected_timeout
+        self.report_pkts = report_pkts
 
+    # These functions provide per-tick processing
+    def periodic_task_init(self, tick_time):
+        """
+        Assumes tick_time is in seconds, usually 1/10 of a sec
+        """
+        if not self.report_pkts:
+            return
+        self.start_time = time.time()
+        self.last_report = self.start_time
+        self.pkt_in_count = 0 # Total packet in count
+        self.periodic_pkt_in_count = 0 # Packet-ins this cycle
+
+    def periodic_task_tick(self, con):
+        """
+        Process one tick.  Currently this just counts pkt-in msgs
+        """
+        if not self.report_pkts:
+            return
+        if con.cstate != 4:
+            return
+
+        # Gather packets from control cxn
+        current_time = time.time()
+        new_pkts = con.packet_in_count - self.pkt_in_count
+        self.pkt_in_count = con.packet_in_count
+        self.periodic_pkt_in_count += new_pkts
+        con.clear_queue()
+
+        # Report every second or so
+        if (current_time - self.last_report >= 1):
+            if self.periodic_pkt_in_count:
+                print "%7.2f: pkt/sec last period:  %6d.  Total %10d." % (
+                    current_time - self.start_time,
+                    self.periodic_pkt_in_count/(current_time - self.last_report),
+                    self.pkt_in_count)
+            self.last_report = current_time
+            self.periodic_pkt_in_count = 0
+
+    def periodic_task_done(self):
+        if not self.report_pkts:
+            return
+        print "Received %d pkt-ins over %d seconds" % (
+            self.pkt_in_count, time.time() - self.start_time)
+        
     def runTest(self):
         for conspec in self.controller_list:
             self.controllerSetup(conspec[0], conspec[1])
@@ -144,6 +190,7 @@
             self.controllers[i].keep_alive = self.keep_alive
             self.controllers[i].saved_switch_addr = None
         tick = 0.1  # time period in seconds at which controllers are handled
+        self.periodic_task_init(tick)
 
         disconnected_count = 0
         cycle = 0
@@ -191,7 +238,7 @@
                                               timeout=0)
                         if reply is not None:
                             logging.info(condesc + 
-                                         "Features request received from " +
+                                         "Features reply received from " +
                                          str(con.switch_addr))
                             con.cstate = 4
                             con.count = 0
@@ -222,6 +269,7 @@
                     con.cstate = 0
             
                 states.append(con.cstate)
+                self.periodic_task_tick(con)
 
             logging.debug("Cycle " + str(cycle) +
                           ", states " + str(states) +
@@ -241,6 +289,7 @@
             if cycle > self.cxn_cycles:
                break
             time.sleep(tick)
+        self.periodic_task_done()
 
 @disabled
 class HandshakeAndKeepalive(CompleteHandshake):
@@ -253,6 +302,16 @@
        CompleteHandshake.__init__(self, keep_alive=True)
 
 @disabled
+class MonitorPacketIn(CompleteHandshake):
+    """
+    Complete handshake and respond to echo request.  As packet-in messages
+    arrive, report the count and pkts/second
+    """
+
+    def __init__(self):
+       CompleteHandshake.__init__(self, keep_alive=True, report_pkts=True)
+
+@disabled
 class HandshakeNoEcho(CompleteHandshake):
     """
     Complete handshake, but otherwise do nothing, and do not respond to echo.