pyloxi: parse unknown subclasses as the superclass

Previously we would throw an exception if we encountered a (for example)
experimenter action subclass that we didn't know existed. This was fine for
OFTest but causes problems for other tools that use pyloxi.

This change generates a full set of pack/unpack/pretty_print functions for
virtual classes. If a discriminator field contains a subtype we don't know
about, the virtual class is instantiated instead.

This also removes the special cases around unknown hello elements.
diff --git a/py_gen/tests/of10.py b/py_gen/tests/of10.py
index 250000f..7e10335 100644
--- a/py_gen/tests/of10.py
+++ b/py_gen/tests/of10.py
@@ -112,10 +112,10 @@
         with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
             loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
 
-    def test_invalid_action_type(self):
+    def test_unknown_action_type(self):
         buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
-        with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action subtype'):
-            loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
+        result = loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
+        self.assertEquals(result, [ofp.action.action(type=0xfffe)])
 
 class TestConstants(unittest.TestCase):
     def test_ports(self):
@@ -200,11 +200,19 @@
         test_klasses = [x for x in ofp.message.__dict__.values()
                         if type(x) == type
                            and issubclass(x, ofp.message.message)
-                           and hasattr(x, 'pack')]
+                           and not hasattr(x, 'subtypes')]
 
         for klass in test_klasses:
             self.assertIsInstance(ofp.message.parse_message(klass(xid=1).pack()), klass)
 
+    def test_parse_unknown_message(self):
+        import loxi
+        import loxi.of10 as ofp
+
+        buf = "\x01\xfe\x00\x08\x12\x34\x56\x78"
+        msg = ofp.message.parse_message(buf)
+        self.assertIsInstance(msg, ofp.message.message)
+
 class TestUtils(unittest.TestCase):
     def test_pretty_wildcards(self):
         self.assertEquals("OFPFW_ALL", ofp.util.pretty_wildcards(ofp.OFPFW_ALL))
@@ -231,7 +239,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):
diff --git a/py_gen/tests/of11.py b/py_gen/tests/of11.py
index d620509..7ae6d8a 100644
--- a/py_gen/tests/of11.py
+++ b/py_gen/tests/of11.py
@@ -69,7 +69,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):
diff --git a/py_gen/tests/of12.py b/py_gen/tests/of12.py
index 4774672..c463c50 100644
--- a/py_gen/tests/of12.py
+++ b/py_gen/tests/of12.py
@@ -78,7 +78,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):
diff --git a/py_gen/tests/of13.py b/py_gen/tests/of13.py
index 8e258df..c5a16b2 100644
--- a/py_gen/tests/of13.py
+++ b/py_gen/tests/of13.py
@@ -60,18 +60,6 @@
         self.assertTrue(hasattr(loxi.of13, "message"))
         self.assertTrue(hasattr(loxi.of13, "oxm"))
 
-class TestCommon(unittest.TestCase):
-    def test_list_hello_elem_unpack(self):
-        buf = ''.join([
-            '\x00\x01\x00\x04', # versionbitmap
-            '\x00\x00\x00\x04', # unknown type
-            '\x00\x01\x00\x04', # versionbitmap
-        ])
-        l = ofp.util.unpack_list_hello_elem(OFReader(buf))
-        self.assertEquals(len(l), 2)
-        self.assertTrue(isinstance(l[0], ofp.hello_elem_versionbitmap))
-        self.assertTrue(isinstance(l[1], ofp.hello_elem_versionbitmap))
-
 # The majority of the serialization tests are created here using the files in
 # the test_data directory.
 class TestDataFiles(unittest.TestCase):
@@ -91,7 +79,7 @@
                               for klass in mod.__dict__.values()
                               if isinstance(klass, type) and
                                  issubclass(klass, loxi.OFObject) and
-                                 hasattr(klass, 'pack')]
+                                 not hasattr(klass, 'subtypes')]
         self.klasses.sort(key=lambda x: str(x))
 
     def test_serialization(self):