testutils: add openflow_ports()

This function replaces the common boilerplate code that gets a list of port
numbers from the config dictionary and checks that enough ports are available
for the test.

I changed the OF 1.3 tests to use this function.
diff --git a/src/python/oftest/testutils.py b/src/python/oftest/testutils.py
index bf2b24a..e87b7a0 100644
--- a/src/python/oftest/testutils.py
+++ b/src/python/oftest/testutils.py
@@ -1598,4 +1598,17 @@
 
     test.assertTrue(msg == None, "Did not expect a packet-in message on port %d" % in_port)
 
+def openflow_ports(num=None):
+    """
+    Return a list of 'num' OpenFlow port numbers
+
+    If 'num' is None, return all available ports. Otherwise, limit the length
+    of the result to 'num' and raise an exception if not enough ports are
+    available.
+    """
+    ports = sorted(oftest.config["port_map"].keys())
+    if num != None and len(ports) < num:
+        raise Exception("test requires %d ports but only %d are available" % (num, len(ports)))
+    return ports[:num]
+
 __all__ = list(set(locals()) - _import_blacklist)
diff --git a/tests-1.3/actions.py b/tests-1.3/actions.py
index d213beb..39369d0 100644
--- a/tests-1.3/actions.py
+++ b/tests-1.3/actions.py
@@ -24,9 +24,7 @@
     Output to a single port
     """
     def runTest(self):
-        ports = sorted(config["port_map"].keys())
-        in_port = ports[0]
-        out_port = ports[1]
+        in_port, out_port = openflow_ports(2)
 
         actions = [ofp.action.output(out_port)]
 
@@ -52,14 +50,15 @@
 
         logging.info("Sending packet, expecting output to port %d", out_port)
         self.dataplane.send(in_port, pktstr)
-        receive_pkt_check(self.dataplane, pktstr, [out_port], set(ports) - set([out_port]), self)
+        receive_pkt_check(self.dataplane, pktstr, [out_port],
+                          set(openflow_ports()) - set([out_port]), self)
 
 class OutputMultiple(base_tests.SimpleDataPlane):
     """
     Output to three ports
     """
     def runTest(self):
-        ports = sorted(config["port_map"].keys())
+        ports = openflow_ports(4)
         in_port = ports[0]
         out_ports = ports[1:4]
 
@@ -87,7 +86,8 @@
 
         logging.info("Sending packet, expecting output to ports %r", out_ports)
         self.dataplane.send(in_port, pktstr)
-        receive_pkt_check(self.dataplane, pktstr, out_ports, set(ports) - set(out_ports), self)
+        receive_pkt_check(self.dataplane, pktstr, out_ports,
+                          set(openflow_ports()) - set(out_ports), self)
 
 class BaseModifyPacketTest(base_tests.SimpleDataPlane):
     """
@@ -95,9 +95,7 @@
     """
 
     def verify_modify(self, actions, pkt, exp_pkt):
-        ports = sorted(config["port_map"].keys())
-        in_port = ports[0]
-        out_port = ports[1]
+        in_port, out_port = openflow_ports(2)
 
         actions = actions + [ofp.action.output(out_port)]
 
@@ -119,7 +117,8 @@
 
         logging.info("Sending packet, expecting output to port %d", out_port)
         self.dataplane.send(in_port, str(pkt))
-        receive_pkt_check(self.dataplane, str(exp_pkt), [out_port], set(ports) - set([out_port]), self)
+        receive_pkt_check(self.dataplane, str(exp_pkt), [out_port],
+                          set(openflow_ports()) - set([out_port]), self)
 
 class PushVlan(BaseModifyPacketTest):
     """
diff --git a/tests-1.3/match.py b/tests-1.3/match.py
index c8bf51d..c807ee8 100644
--- a/tests-1.3/match.py
+++ b/tests-1.3/match.py
@@ -34,9 +34,7 @@
         dicts mapping from string names (used in log messages) to string
         packet data.
         """
-        ports = sorted(config["port_map"].keys())
-        in_port = ports[0]
-        out_port = ports[1]
+        in_port, out_port = openflow_ports(2)
 
         logging.info("Running match test for %s", match.show())
 
@@ -89,10 +87,7 @@
     Match on ingress port
     """
     def runTest(self):
-        ports = sorted(config["port_map"].keys())
-        in_port = ports[0]
-        out_port = ports[1]
-        bad_port = ports[2]
+        in_port, out_port, bad_port = openflow_ports(3)
 
         match = ofp.match([
             ofp.oxm.in_port(in_port)