pyloxi: support meter bands
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 7037398..dc6b999 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -91,6 +91,8 @@
         type_values['type'] = 0
     elif cls == "of_match_v3":
         type_values['type'] = 1
+    elif utils.class_is_meter_band(cls):
+        type_values['type'] = util.constant_for_value(version, "ofp_meter_band_type", util.primary_wire_type(cls, version))
 
     return type_values
 
@@ -102,6 +104,8 @@
                  "of_hello_elem", "of_hello_elem_header"]
     ofclasses = []
     for cls in of_g.standard_class_order:
+        if type_maps.class_is_virtual(cls):
+            continue
         if version not in of_g.unified[cls] or cls in blacklist:
             continue
         unified_class = util.lookup_unified_class(cls, version)
@@ -111,6 +115,8 @@
             pyname = cls[10:]
         elif utils.class_is_oxm(cls):
             pyname = cls[7:]
+        elif utils.class_is_meter_band(cls):
+            pyname = cls[14:]
         else:
             pyname = cls[3:]
 
@@ -170,6 +176,7 @@
     ofclasses = [x for x in build_ofclasses(version)
                  if not utils.class_is_message(x.name)
                     and not utils.class_is_action(x.name)
+                    and not utils.class_is_meter_band(x.name)
                     and not utils.class_is_oxm(x.name)
                     and not utils.class_is_list(x.name)]
     util.render_template(out, 'common.py', ofclasses=ofclasses, version=version)
@@ -191,6 +198,11 @@
                  if utils.class_is_message(x.name)]
     util.render_template(out, 'message.py', ofclasses=ofclasses, version=version)
 
+def generate_meter_band(out, name, version):
+    ofclasses = [x for x in build_ofclasses(version)
+                 if utils.class_is_meter_band(x.name)]
+    util.render_template(out, 'meter_band.py', ofclasses=ofclasses, version=version)
+
 def generate_pp(out, name, version):
     util.render_template(out, 'pp.py')
 
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index ca3106e..cb3de7c 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -134,6 +134,8 @@
             return 'common.unpack_list_group_desc_stats_entry(%s)' % (reader_expr)
         elif self.base == 'of_list_group_stats_entry_t':
             return 'common.unpack_list_group_stats_entry(%s)' % (reader_expr)
+        elif self.base == 'of_list_meter_band_t':
+            return 'meter_band.unpack_list(%s)' % (reader_expr)
         elif self.base == 'of_port_name_t':
             return self._gen_string_unpack_expr(reader_expr, 16)
         elif self.base == 'of_table_name_t' or self.base == 'of_serial_num_t':
diff --git a/py_gen/templates/common.py b/py_gen/templates/common.py
index 737ed64..c694b9b 100644
--- a/py_gen/templates/common.py
+++ b/py_gen/templates/common.py
@@ -32,6 +32,9 @@
 import sys
 import struct
 import action
+:: if version >= 4:
+import meter_band # for unpack_list
+:: #endif
 import const
 import util
 import loxi.generic_util
diff --git a/py_gen/templates/init.py b/py_gen/templates/init.py
index 75d52be..190d65e 100644
--- a/py_gen/templates/init.py
+++ b/py_gen/templates/init.py
@@ -33,6 +33,9 @@
 :: if version >= 3:
 import oxm
 :: #endif
+:: if version >= 4:
+import meter_band
+:: #endif
 from const import *
 from common import *
 from loxi import ProtocolError
diff --git a/py_gen/templates/message.py b/py_gen/templates/message.py
index d81d5b7..228d2d0 100644
--- a/py_gen/templates/message.py
+++ b/py_gen/templates/message.py
@@ -36,6 +36,9 @@
 import const
 import common
 import action # for unpack_list
+:: if version >= 4:
+import meter_band # for unpack_list
+:: #endif
 import util
 import loxi.generic_util
 
diff --git a/py_gen/templates/meter_band.py b/py_gen/templates/meter_band.py
new file mode 100644
index 0000000..ca111d5
--- /dev/null
+++ b/py_gen/templates/meter_band.py
@@ -0,0 +1,67 @@
+:: # Copyright 2013, Big Switch Networks, Inc.
+:: #
+:: # LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
+:: # the following special exception:
+:: #
+:: # LOXI Exception
+:: #
+:: # As a special exception to the terms of the EPL, you may distribute libraries
+:: # generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
+:: # that copyright and licensing notices generated by LoxiGen are not altered or removed
+:: # from the LoxiGen Libraries and the notice provided below is (i) included in
+:: # the LoxiGen Libraries, if distributed in source code form and (ii) included in any
+:: # documentation for the LoxiGen Libraries, if distributed in binary form.
+:: #
+:: # Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
+:: #
+:: # You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
+:: # a copy of the EPL at:
+:: #
+:: # http://www.eclipse.org/legal/epl-v10.html
+:: #
+:: # Unless required by applicable law or agreed to in writing, software
+:: # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+:: # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+:: # EPL for the specific language governing permissions and limitations
+:: # under the EPL.
+::
+:: import itertools
+:: import of_g
+:: include('_copyright.py')
+
+:: include('_autogen.py')
+
+import struct
+import const
+import util
+import loxi.generic_util
+import loxi
+
+def unpack_list(reader):
+    def deserializer(reader, typ):
+        parser = parsers.get(typ)
+        if not parser: raise loxi.ProtocolError("unknown meter band type %d" % typ)
+        return parser(reader)
+    return loxi.generic_util.unpack_list_tlv16(reader, deserializer)
+
+class MeterBand(object):
+    type = None # override in subclass
+    pass
+
+:: for ofclass in ofclasses:
+:: include('_ofclass.py', ofclass=ofclass, superclass="MeterBand")
+
+:: #endfor
+
+parsers = {
+:: sort_key = lambda x: x.type_members[0].value
+:: msgtype_groups = itertools.groupby(sorted(ofclasses, key=sort_key), sort_key)
+:: for (k, v) in msgtype_groups:
+:: v = list(v)
+:: if len(v) == 1:
+    ${k} : ${v[0].pyname}.unpack,
+:: else:
+    ${k} : parse_${k[12:].lower()},
+:: #endif
+:: #endfor
+}
diff --git a/py_gen/tests/of13.py b/py_gen/tests/of13.py
index 814dfd6..0648d81 100644
--- a/py_gen/tests/of13.py
+++ b/py_gen/tests/of13.py
@@ -684,8 +684,32 @@
         pass
 
     def test_meter_config_stats_reply(self):
-        # TODO
-        pass
+        obj = ofp.message.meter_config_stats_reply(
+            xid=0x12345678,
+            flags=0,
+            entries=[
+                ofp.meter_band.drop(rate=1, burst_size=2),
+                ofp.meter_band.dscp_remark(rate=3, burst_size=4, prec_level=5)])
+        buf = ''.join([
+            '\x04', '\x13', # version, type
+            '\x00\x30', # length
+            '\x12\x34\x56\x78', # xid
+            '\x00\x0a', # stats_type
+            '\x00\x00', # flags
+            '\x00' * 4, # pad
+            '\x00\x01', # entries[0].type
+            '\x00\x10', # entries[0].length
+            '\x00\x00\x00\x01', # entries[0].rate
+            '\x00\x00\x00\x02', # entries[0].burst_size
+            '\x00' * 4, # pad
+            '\x00\x02', # entries[1].type
+            '\x00\x10', # entries[1].length
+            '\x00\x00\x00\x03', # entries[1].rate
+            '\x00\x00\x00\x04', # entries[1].burst_size
+            '\x05', # entries[1].prec_level
+            '\x00' * 3, # pad
+        ])
+        test_serialization(obj, buf)
 
     def test_meter_features_stats_request(self):
         # TODO
@@ -807,7 +831,6 @@
     def test_serialization(self):
         expected_failures = [
             ofp.common.flow_stats_entry,
-            ofp.common.meter_config,
             ofp.common.table_feature_prop_apply_actions,
             ofp.common.table_feature_prop_apply_actions_miss,
             ofp.common.table_feature_prop_instructions,
@@ -820,9 +843,7 @@
             ofp.message.flow_delete_strict,
             ofp.message.flow_modify,
             ofp.message.flow_modify_strict,
-            ofp.message.meter_config_stats_reply,
             ofp.message.meter_features_stats_reply,
-            ofp.message.meter_mod,
             ofp.message.meter_stats_reply,
             ofp.message.table_features_stats_reply,
             ofp.message.table_features_stats_request,