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/base_list.py b/src/python/of12/base_list.py
new file mode 100644
index 0000000..938c68e
--- /dev/null
+++ b/src/python/of12/base_list.py
@@ -0,0 +1,158 @@
+
+"""
+Base list class for inheritance.
+Most of the list stuff is common; unpacking is the only thing that
+is left pure virtual.
+"""
+
+import copy
+
+class ofp_base_list(object):
+    """
+    Container type to maintain a list of ofp objects
+
+    Data members:
+    @arg items An array of objects
+    @arg class_list A tuple of classes that may be added to the list;
+         If None, no checking is done
+    @arg name The name to use when displaying the list
+
+    Methods:
+    @arg pack Pack the structure into a string
+    @arg unpack Unpack a string to objects, with proper typing
+    @arg add Add an item to the list; you can directly access
+    the item member, but add will validate that the added object 
+    is of the right type.
+    @arg extend Add the items for another list to this list
+
+    """
+
+    def __init__(self):
+        self.items = []
+        self.class_list = None
+        self.name = "unspecified"
+
+    def pack(self):
+        """
+        Pack a list of items
+
+        Returns the packed string
+        """
+        packed = ""
+        for obj in self.items:
+            packed += obj.pack()
+        return packed
+
+    def unpack(self, binary_string, bytes=None):
+        """
+        Pure virtual function for a list of items
+
+        Unpack items 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 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
+        """
+        pass
+
+    def add(self, item):
+        """
+        Add an item to a list
+
+        @param item The item to add
+        @return True if successful, False if not proper type object
+
+        """
+
+        # Note that the second arg of isinstance can be a list which
+        # checks that the type of item is in the list
+        if (self.class_list is not None) and \
+                not isinstance(item, tuple(self.class_list)):
+            return False
+
+        tmp = copy.deepcopy(item)
+        self.items.append(tmp)
+        return True
+
+    def remove_type(self, target):
+        """
+        Remove the first item on the list of the given type
+
+        @param target The type of item to search
+
+        @return The object removed, if any; otherwise None
+
+        """
+        for index in xrange(len(self.items)):
+            if self.items[index].type == target:
+                return self.items.pop(index)
+        return None
+
+    def find_type(self, target):
+        """
+        Find the first item on the list of the given type
+
+        @param target The type of item to search
+
+        @return The object with the matching type if any; otherwise None
+
+        """
+        for index in xrange(len(self.items)):
+            if self.items[index].type == target:
+                return self.items[index]
+        return None
+
+    def extend(self, other):
+        """
+        Add the items in other to this list
+
+        @param other An object of the same type of list whose
+        entries are to be merged into this list
+
+        @return True if successful.  If not successful, the list
+        may have been modified.
+
+        @todo Check if this is proper deep copy or not
+
+        """
+        for act in other.items:
+            if not self.add(act):
+                return False
+        return True
+
+    def __len__(self):
+        """
+        Length of the list packed as a string
+        """
+        length = 0
+        for item in self.items:
+            length += item.__len__()
+        return length
+
+    def __eq__(self, other):
+        if type(self) != type(other):
+            return False
+        if self.items != other.items:
+            return False
+        return True
+
+    def __ne__(self, other): return not self.__eq__(other)
+
+    # Methods to make class iterable
+    def __iter__(self):
+        return self.items.__iter__()
+
+    def show(self, prefix=''):
+        outstr = prefix + self.name + "list with " + str(len(self.items)) + \
+            " items\n"
+        count = 0
+        for obj in self.items:
+            count += 1
+            outstr += prefix + " " + self.name + " " + str(count) + ": \n"
+            outstr += obj.show(prefix + '    ')
+        return outstr