Added relax option to oftest

This option relaxes restrictions around receiving unexpected packets.
It's main use case is in evironments where in-band control is enabled and
as a consequence packets are forwarded to dataplane ports.
diff --git a/src/python/oftest/dataplane.py b/src/python/oftest/dataplane.py
index 40bda9c..7d788cb 100644
--- a/src/python/oftest/dataplane.py
+++ b/src/python/oftest/dataplane.py
@@ -134,14 +134,20 @@
             else:
                 self.parent.packets_pending += 1
             # Check if parent is waiting on this (or any) port
+            drop_pkt = False
             if self.parent.want_pkt:
                 if (not self.parent.want_pkt_port or
                         self.parent.want_pkt_port == self.port_number):
-                    self.parent.got_pkt_port = self.port_number
-                    self.parent.want_pkt = False
-                    self.parent.pkt_sync.notify()
-            self.packets.append((rcvmsg, rcvtime))
-            self.packets_total += 1
+                    if self.parent.exp_pkt:
+                        if str(self.parent.exp_pkt) != str(rcvmsg):
+                            drop_pkt = True
+                    if not drop_pkt:
+                        self.parent.got_pkt_port = self.port_number
+                        self.parent.want_pkt = False
+                        self.parent.pkt_sync.notify()
+            if not drop_pkt:
+                self.packets.append((rcvmsg, rcvtime))
+                self.packets_total += 1
             self.pkt_sync.release()
 
         self.logger.info("Thread exit ")
@@ -238,6 +244,7 @@
 
         # These are used to signal async pkt arrival for polling
         self.want_pkt = False
+        self.exp_pkt = None
         self.want_pkt_port = None # What port required (or None)
         self.got_pkt_port = None # On what port received?
         self.packets_pending = 0 # Total pkts in all port queues
@@ -295,7 +302,7 @@
 
         return min_port
 
-    def poll(self, port_number=None, timeout=None):
+    def poll(self, port_number=None, timeout=None, exp_pkt=None):
         """
         Poll one or all dataplane ports for a packet
 
@@ -305,6 +312,8 @@
         @param port_number If set, get packet from this port
         @param timeout If positive and no packet is available, block
         until a packet is received or for this many seconds
+        @param exp_pkt If not None, look for this packet and ignore any
+        others received.  Requires port_number to be specified
         @return The triple port_number, packet, pkt_time where packet
         is received from port_number at time pkt_time.  If a timeout
         occurs, return None, None, None
@@ -312,13 +321,23 @@
 
         self.pkt_sync.acquire()
 
+        if exp_pkt and not port_number:
+            print "WARNING: Dataplane poll: exp_pkt without port number"
+
         # Check if requested specific port and it has a packet
         if port_number and len(self.port_list[port_number].packets) != 0:
-            pkt, time = self.port_list[port_number].dequeue(use_lock=False)
-            self.pkt_sync.release()
-            oft_assert(pkt, "Poll: packet not found on port " +
-                       str(port_number))
-            return port_number, pkt, time
+            while len(self.port_list[port_number].packets) != 0:
+                pkt, time = self.port_list[port_number].dequeue(use_lock=False)
+                if not exp_pkt:
+                    break
+                if str(pkt) == str(exp_pkt):
+                    break
+                pkt = None # Discard silently
+            if pkt:
+                self.pkt_sync.release()
+                oft_assert(pkt, "Poll: packet not found on port " +
+                           str(port_number))
+                return port_number, pkt, time
 
         # Check if requested any port and some packet pending
         if not port_number and self.packets_pending != 0:
@@ -336,10 +355,12 @@
         # Desired packet isn't available and timeout is specified
         # Already holding pkt_sync; wait on pkt_sync variable
         self.want_pkt = True
+        self.exp_pkt = exp_pkt
         self.want_pkt_port = port_number
         self.got_pkt_port = None
         self.pkt_sync.wait(timeout)
         self.want_pkt = False
+        self.exp_pkt = None
         if self.got_pkt_port:
             pkt, time = \
                 self.port_list[self.got_pkt_port].dequeue(use_lock=False)
diff --git a/tests/basic.py b/tests/basic.py
index a173a30..394ab0a 100644
--- a/tests/basic.py
+++ b/tests/basic.py
@@ -209,7 +209,18 @@
             pkt = simple_tcp_packet()
             self.dataplane.send(of_port, str(pkt))
             #@todo Check for unexpected messages?
-            (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
+            count = 0
+            while True:
+                (response, raw) = self.controller.poll(ofp.OFPT_PACKET_IN, 2)
+                if not response:  # Timeout
+                    break
+                if str(pkt) == response.data:  # Got match
+                    break
+                if not basic_config["relax"]:  # Only one attempt to match
+                    break
+                count += 1
+                if count > 10:   # Too many tries
+                    break
 
             self.assertTrue(response is not None, 
                             'Packet in message not received on port ' + 
@@ -255,7 +266,14 @@
             rv = self.controller.message_send(msg)
             self.assertTrue(rv == 0, "Error sending out message")
 
-            (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1)
+            exp_pkt_arg = None
+            exp_port = None
+            if basic_config["relax"]:
+                exp_pkt_arg = outpkt
+                exp_port = dp_port
+            (of_port, pkt, pkt_time) = self.dataplane.poll(timeout=1, 
+                                                           port_number=exp_port,
+                                                           exp_pkt=exp_pkt_arg)
 
             self.assertTrue(pkt is not None, 'Packet not received')
             basic_logger.info("PacketOut: got pkt from " + str(of_port))
diff --git a/tests/oft b/tests/oft
index f7b72f0..a56a9ea 100755
--- a/tests/oft
+++ b/tests/oft
@@ -159,6 +159,7 @@
     "port_count"         : 4,
     "base_of_port"       : 1,
     "base_if_index"      : 1,
+    "relax"              : False,
     "test_spec"          : "all",
     "test_dir"           : ".",
     "log_file"           : "oft.log",
@@ -240,6 +241,8 @@
                       help="List all tests and exit")
     parser.add_option("--verbose", action="store_true",
                       help="Short cut for --debug=verbose")
+    parser.add_option("--relax", action="store_true",
+                      help="Relax packet match checks allowing other packets")
     parser.add_option("--param", type="int",
                       help="Parameter sent to test (for debugging)")
     parser.add_option("-t", "--test-params",
@@ -370,6 +373,7 @@
 (config, args) = config_setup(config_default)
 
 test_list_generate(config)
+oft_config = config
 
 # Check if test list is requested; display and exit if so
 if config["list"]:
diff --git a/tests/pktact.py b/tests/pktact.py
index c2d4a68..a551d58 100644
--- a/tests/pktact.py
+++ b/tests/pktact.py
@@ -141,7 +141,16 @@
             pa_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=1)
+
+            exp_pkt_arg = None
+            exp_port = None
+            if pa_config["relax"]:
+                exp_pkt_arg = pkt
+                exp_port = egress_port
+
+            (rcv_port, rcv_pkt, pkt_time) = self.dataplane.poll(timeout=1, 
+                                                                port_number=exp_port,
+                                                                exp_pkt=exp_pkt_arg)
             self.assertTrue(rcv_pkt is not None, "Did not receive packet")
             pa_logger.debug("Packet len " + str(len(rcv_pkt)) + " in on " + 
                          str(rcv_port))
@@ -219,7 +228,7 @@
             no_ports = set(of_ports).difference(yes_ports)
 
             receive_pkt_check(self.dataplane, pkt, yes_ports, no_ports,
-                              self, pa_logger)
+                              self, pa_logger, pa_config)
 
 class DirectMCNonIngress(basic.SimpleDataPlane):
     """
@@ -273,7 +282,7 @@
             self.dataplane.send(ingress_port, str(pkt))
             yes_ports = set(of_ports).difference([ingress_port])
             receive_pkt_check(self.dataplane, pkt, yes_ports, [ingress_port],
-                              self, pa_logger)
+                              self, pa_logger, pa_config)
 
 
 class DirectMC(basic.SimpleDataPlane):
@@ -327,7 +336,7 @@
             pa_logger.info("Sending packet to dp port " + str(ingress_port))
             self.dataplane.send(ingress_port, str(pkt))
             receive_pkt_check(self.dataplane, pkt, of_ports, [], self,
-                              pa_logger)
+                              pa_logger, pa_config)
 
 class Flood(basic.SimpleDataPlane):
     """
@@ -375,7 +384,7 @@
             self.dataplane.send(ingress_port, str(pkt))
             yes_ports = set(of_ports).difference([ingress_port])
             receive_pkt_check(self.dataplane, pkt, yes_ports, [ingress_port],
-                              self, pa_logger)
+                              self, pa_logger, pa_config)
 
 class FloodPlusIngress(basic.SimpleDataPlane):
     """
@@ -426,7 +435,7 @@
             pa_logger.info("Sending packet to dp port " + str(ingress_port))
             self.dataplane.send(ingress_port, str(pkt))
             receive_pkt_check(self.dataplane, pkt, of_ports, [], self,
-                              pa_logger)
+                              pa_logger, pa_config)
 
 class All(basic.SimpleDataPlane):
     """
@@ -474,7 +483,7 @@
             self.dataplane.send(ingress_port, str(pkt))
             yes_ports = set(of_ports).difference([ingress_port])
             receive_pkt_check(self.dataplane, pkt, yes_ports, [ingress_port],
-                              self, pa_logger)
+                              self, pa_logger, pa_config)
 
 class AllPlusIngress(basic.SimpleDataPlane):
     """
@@ -525,7 +534,7 @@
             pa_logger.info("Sending packet to dp port " + str(ingress_port))
             self.dataplane.send(ingress_port, str(pkt))
             receive_pkt_check(self.dataplane, pkt, of_ports, [], self,
-                              pa_logger)
+                              pa_logger, pa_config)
             
 class FloodMinusPort(basic.SimpleDataPlane):
     """
@@ -584,7 +593,7 @@
             no_ports = set([ingress_port, no_flood_port])
             yes_ports = set(of_ports).difference(no_ports)
             receive_pkt_check(self.dataplane, pkt, yes_ports, no_ports, self,
-                              pa_logger)
+                              pa_logger, pa_config)
 
             # Turn no flood off again
             rv = port_config_set(self.controller, no_flood_port,
diff --git a/tests/testutils.py b/tests/testutils.py
index 084f74d..450b330 100644
--- a/tests/testutils.py
+++ b/tests/testutils.py
@@ -214,7 +214,8 @@
     rv = controller.message_send(mod)
     return rv
 
-def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger):
+def receive_pkt_check(dataplane, pkt, yes_ports, no_ports, assert_if, logger,
+                      config):
     """
     Check for proper receive packets across all ports
     @param dataplane The dataplane object
@@ -223,10 +224,14 @@
     @param no_ports Set or list of ports that should not receive packet
     @param assert_if Object that implements assertXXX
     """
+    exp_pkt_arg = None
+    if config and config["relax"]:
+        exp_pkt_arg = pkt
+
     for ofport in yes_ports:
         logger.debug("Checking for pkt on port " + str(ofport))
         (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
-            port_number=ofport, timeout=1)
+            port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
         assert_if.assertTrue(rcv_pkt is not None, 
                              "Did not receive pkt on " + str(ofport))
         assert_if.assertEqual(str(pkt), str(rcv_pkt),
@@ -236,7 +241,7 @@
     for ofport in no_ports:
         logger.debug("Negative check for pkt on port " + str(ofport))
         (rcv_port, rcv_pkt, pkt_time) = dataplane.poll(
-            port_number=ofport, timeout=1)
+            port_number=ofport, timeout=1, exp_pkt=exp_pkt_arg)
         assert_if.assertTrue(rcv_pkt is None, 
                              "Unexpected pkt on port " + str(ofport))
 
@@ -247,8 +252,14 @@
 
     parent must implement dataplane, assertTrue and assertEqual
     """
+    exp_pkt_arg = None
+    if parent.config["relax"]:
+        exp_pkt_arg = exp_pkt
+
     (rcv_port, rcv_pkt, pkt_time) = parent.dataplane.poll(port_number=egr_port,
-                                                          timeout=1)
+                                                          timeout=1, 
+                                                          exp_pkt=exp_pkt_arg)
+
     if rcv_pkt is None:
         parent.logger.error("ERROR: No packet received from " + str(egr_port))
 
@@ -592,7 +603,6 @@
 
     new_actions = []
 
-
     base_pkt_params = {}
     base_pkt_params['pktlen'] = 100
     base_pkt_params['dl_dst'] = '00:DE:F0:12:34:56'