import OpenFlow 1.2 protocol module and basic test cases from CPqD/oftest12

For now these tests will live in a separate directory. The goal is to merge
them where possible but this has to wait for an OpenFlow protocol module that
supports all versions of OpenFlow.
diff --git a/src/python/of12/action_list.py b/src/python/of12/action_list.py
new file mode 100644
index 0000000..c34f6af
--- /dev/null
+++ b/src/python/of12/action_list.py
@@ -0,0 +1,86 @@
+"""
+OpenFlow action, instruction and bucket list classes
+"""
+
+from action import *
+from cstruct import ofp_header
+from base_list import ofp_base_list
+import copy
+
+action_object_map = {
+    OFPAT_OUTPUT                        : action_output,
+    OFPAT_SET_FIELD                     : action_set_field,
+    OFPAT_COPY_TTL_OUT                  : action_copy_ttl_out,
+    OFPAT_COPY_TTL_IN                   : action_copy_ttl_in,
+    OFPAT_SET_MPLS_TTL                  : action_set_mpls_ttl,
+    OFPAT_DEC_MPLS_TTL                  : action_dec_mpls_ttl,
+    OFPAT_PUSH_VLAN                     : action_push_vlan,
+    OFPAT_POP_VLAN                      : action_pop_vlan,
+    OFPAT_PUSH_MPLS                     : action_push_mpls,
+    OFPAT_POP_MPLS                      : action_pop_mpls,
+    OFPAT_SET_QUEUE                     : action_set_queue,
+    OFPAT_GROUP                         : action_group,
+    OFPAT_SET_NW_TTL                    : action_set_nw_ttl,
+    OFPAT_DEC_NW_TTL                    : action_dec_nw_ttl,
+    OFPAT_EXPERIMENTER                  : action_experimenter
+}
+
+class action_list(ofp_base_list):
+    """
+    Maintain a list of actions
+
+    Data members:
+    @arg actions: An array of action objects such as action_output, etc.
+
+    Methods:
+    @arg pack: Pack the structure into a string
+    @arg unpack: Unpack a string to objects, with proper typing
+    @arg add: Add an action to the list; you can directly access
+    the action member, but add will validate that the added object 
+    is an action.
+
+    """
+
+    def __init__(self):
+        ofp_base_list.__init__(self)
+        self.actions = self.items
+        self.name = "action"
+        self.class_list = action_class_list
+
+    def unpack(self, binary_string, bytes=None):
+        """
+        Unpack a list of actions
+        
+        Unpack actions from a binary string, creating an array
+        of objects of the appropriate type
+
+        @param binary_string The string to be unpacked
+
+        @param bytes The total length of the action list in bytes.  
+        Ignored if decode is True.  If None and decode is false, the
+        list is assumed to extend through the entire string.
+
+        @return The remainder of binary_string that was not parsed
+
+        """
+        if bytes == None:
+            bytes = len(binary_string)
+        bytes_done = 0
+        count = 0
+        cur_string = binary_string
+        while bytes_done < bytes:
+            hdr = ofp_action_header()
+            hdr.unpack(cur_string)
+            if hdr.len < OFP_ACTION_HEADER_BYTES:
+                print "ERROR: Action too short"
+                break
+            if not hdr.type in action_object_map.keys():
+                print "WARNING: Skipping unknown action ", hdr.type, hdr.len
+            else:
+                self.actions.append(action_object_map[hdr.type]())
+                self.actions[count].unpack(cur_string)
+                count += 1
+            cur_string = cur_string[hdr.len:]
+            bytes_done += hdr.len
+        return cur_string
+