Initial oftest skeleton with wrapper generators and pylibopenflow
diff --git a/src/python/oftest/ofmsg/action_list.py b/src/python/oftest/ofmsg/action_list.py
new file mode 100644
index 0000000..83dc850
--- /dev/null
+++ b/src/python/oftest/ofmsg/action_list.py
@@ -0,0 +1,140 @@
+"""
+OpenFlow actions list class
+"""
+
+from action import *
+from ofp import ofp_header
+
+# # Map OFP action identifiers to the actual structures used on the wire
+# action_object_map = {
+#     OFPAT_OUTPUT                        : ofp_action_output,
+#     OFPAT_SET_VLAN_VID                  : ofp_action_vlan_vid,
+#     OFPAT_SET_VLAN_PCP                  : ofp_action_vlan_pcp,
+#     OFPAT_STRIP_VLAN                    : ofp_action_header,
+#     OFPAT_SET_DL_SRC                    : ofp_action_dl_addr,
+#     OFPAT_SET_DL_DST                    : ofp_action_dl_addr,
+#     OFPAT_SET_NW_SRC                    : ofp_action_nw_addr,
+#     OFPAT_SET_NW_DST                    : ofp_action_nw_addr,
+#     OFPAT_SET_NW_TOS                    : ofp_action_nw_tos,
+#     OFPAT_SET_TP_SRC                    : ofp_action_tp_port,
+#     OFPAT_SET_TP_DST                    : ofp_action_tp_port,
+#     OFPAT_ENQUEUE                       : ofp_action_enqueue
+# }
+
+# For debugging
+action_object_map = {
+    OFPAT_OUTPUT                        : action_output,
+    OFPAT_SET_VLAN_VID                  : action_set_vlan_vid,
+    OFPAT_SET_VLAN_PCP                  : action_set_vlan_pcp,
+    OFPAT_STRIP_VLAN                    : action_strip_vlan,
+    OFPAT_SET_DL_SRC                    : action_set_dl_src,
+    OFPAT_SET_DL_DST                    : action_set_dl_dst,
+    OFPAT_SET_NW_SRC                    : action_set_nw_src,
+    OFPAT_SET_NW_DST                    : action_set_nw_dst,
+    OFPAT_SET_NW_TOS                    : action_set_nw_tos,
+    OFPAT_SET_TP_SRC                    : action_set_tp_src,
+    OFPAT_SET_TP_DST                    : action_set_tp_dst,
+    OFPAT_ENQUEUE                       : action_enqueue
+}
+
+class action_list(object):
+    """
+    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):
+        self.actions = []
+
+    def pack(self):
+        """
+        Pack a list of actions
+
+        Returns the packed string
+        """
+
+        packed = ""
+        for act in self.actions:
+            packed += act.pack()
+        return packed
+
+    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 not hdr.type in action_object_map.keys():
+                print "WARNING: Skipping unknown action ", hdr.type
+            else:
+                print "DEBUG: Found action of type ", hdr.type
+                self.actions.append(action_object_map[hdr.type]())
+                self.actions[count].unpack(binary_string)
+                count += 1
+            cur_string = cur_string[hdr.len:]
+            bytes_done += hdr.len
+        return cur_string
+
+    def add(self, action):
+        """
+        Add an action to an action list
+
+        @param action The action to add
+
+        @return True if successful, False if not an action object
+
+        """
+        if isinstance(action, action_class_list):
+            self.actions.append(action)
+            return True
+        return False
+
+    def __len__(self):
+        length = 0
+        for act in self.actions:
+            length += act.__len__()
+        return length
+
+    def __eq__(self, other):
+        if type(self) != type(other): return False
+        if self.actions != other.actions: return False
+        return True
+
+    def __ne__(self, other): return not self.__eq__(other)
+        
+    def show(self, prefix=''):
+        print prefix + "Action List with " + str(len(self.actions)) + \
+            " actions"
+        count = 0
+        for obj in self.actions:
+            count += 1
+            print "  Action " + str(count) + ": "
+            obj.show(prefix + '    ')
diff --git a/src/python/oftest/ofmsg/of_message.py b/src/python/oftest/ofmsg/of_message.py
new file mode 100644
index 0000000..bd6d639
--- /dev/null
+++ b/src/python/oftest/ofmsg/of_message.py
@@ -0,0 +1,127 @@
+
+from message import *
+from error import *
+from action import *
+from action_list import action_list
+from ofp import *
+
+"""
+of_message.py
+Contains wrapper functions and classes for the of_message namespace
+that are generated by hand.  It includes the rest of the wrapper
+function information into the of_message namespace
+"""
+
+# These message types are subclassed
+msg_type_subclassed = [
+    OFPT_STATS_REQUEST,
+    OFPT_STATS_REPLY,
+    OFPT_ERROR
+]
+
+# Maps from sub-types to classes
+stats_reply_to_class_map = {
+    OFPST_DESC                      : desc_stats_reply,
+    OFPST_AGGREGATE                 : aggregate_stats_reply,
+    OFPST_FLOW                      : flow_stats_reply,
+    OFPST_TABLE                     : table_stats_reply,
+    OFPST_PORT                      : port_stats_reply,
+    OFPST_QUEUE                     : queue_stats_reply
+}
+
+stats_request_to_class_map = {
+    OFPST_DESC                      : desc_stats_request,
+    OFPST_AGGREGATE                 : aggregate_stats_request,
+    OFPST_FLOW                      : flow_stats_request,
+    OFPST_TABLE                     : table_stats_request,
+    OFPST_PORT                      : port_stats_request,
+    OFPST_QUEUE                     : queue_stats_request
+}
+
+error_to_class_map = {
+    OFPET_HELLO_FAILED              : hello_failed_error_msg,
+    OFPET_BAD_REQUEST               : bad_request_error_msg,
+    OFPET_BAD_ACTION                : bad_action_error_msg,
+    OFPET_FLOW_MOD_FAILED           : flow_mod_failed_error_msg,
+    OFPET_PORT_MOD_FAILED           : port_mod_failed_error_msg,
+    OFPET_QUEUE_OP_FAILED           : queue_op_failed_error_msg
+}
+
+# Map from header type value to the underlieing message class
+msg_type_to_class_map = {
+    OFPT_HELLO                      : hello,
+    OFPT_ERROR                      : error,
+    OFPT_ECHO_REQUEST               : echo_request,
+    OFPT_ECHO_REPLY                 : echo_reply,
+    OFPT_VENDOR                     : vendor,
+    OFPT_FEATURES_REQUEST           : features_request,
+    OFPT_FEATURES_REPLY             : features_reply,
+    OFPT_GET_CONFIG_REQUEST         : get_config_request,
+    OFPT_GET_CONFIG_REPLY           : get_config_reply,
+    OFPT_SET_CONFIG                 : set_config,
+    OFPT_PACKET_IN                  : packet_in,
+    OFPT_FLOW_REMOVED               : flow_removed,
+    OFPT_PORT_STATUS                : port_status,
+    OFPT_PACKET_OUT                 : packet_out,
+    OFPT_FLOW_MOD                   : flow_mod,
+    OFPT_PORT_MOD                   : port_mod,
+    OFPT_STATS_REQUEST              : stats_request,
+    OFPT_STATS_REPLY                : stats_reply,
+    OFPT_BARRIER_REQUEST            : barrier_request,
+    OFPT_BARRIER_REPLY              : barrier_reply,
+    OFPT_QUEUE_GET_CONFIG_REQUEST   : queue_get_config_request,
+    OFPT_QUEUE_GET_CONFIG_REPLY     : queue_get_config_reply
+}
+
+def _of_message_to_object(binary_string):
+    """
+    Map a binary string to the corresponding class.
+
+    Appropriately resolves subclasses
+    """
+    hdr = ofp_header()
+    hdr.unpack(binary_string)
+    # FIXME: Add error detection
+    if not hdr.type in msg_type_subclassed:
+        return msg_type_to_class_map[hdr.type]()
+    if hdr.type == OFPT_STATS_REQUEST:
+        st_hdr = ofp_stats_request()
+        st_hdr.unpack(binary_string)
+        return stats_request_to_class_map[st_hdr.type]()
+    elif hdr.type == OFPT_STATS_REPLY:
+        st_hdr = ofp_stats_reply()
+        st_hdr.unpack(binary_string)
+        return stats_reply_to_class_map[st_hdr.type]()
+    elif hdr.type == OFPT_STATS_REPLY:
+        st_hdr = ofp_error_msg()
+        st_hdr.unpack(binary_string)
+        return error_to_class_map[st_hdr.type]()
+    else:
+        print "ERROR parsing packet to object"
+        return None
+
+def of_message_parse(binary_string, raw=False):
+    """
+    Parse an OpenFlow packet
+
+    Parses a raw OpenFlow packet into a Python class, with class
+    members fully populated.
+
+    @param binary_string The packet (string) to be parsed
+
+    @param raw If true, interpret the packet as an L2 packet.  Not
+    yet supported.
+
+    @return An object of some message class or None if fails
+
+    """
+
+    if raw:
+        print "raw packet message parsing not supported"
+        return None
+
+    obj = _of_message_to_object(binary_string)
+    if obj != None:
+        obj.unpack(binary_string)
+    return obj
+