pyloxi: generate virtual class unpack methods
This is done completely from the IR now.
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
index 610e5a8..c84ceea 100644
--- a/py_gen/templates/_ofclass.py
+++ b/py_gen/templates/_ofclass.py
@@ -42,3 +42,10 @@
def pretty_print(self, q):
:: include('_pretty_print.py', ofclass=ofclass)
+
+:: # Register with our superclass
+:: if ofclass.superclass:
+:: type_field_name = ofclass.superclass.discriminator.name
+:: type_value = ofclass.member_by_name(type_field_name).value
+${superclass_pyname}.subtypes[${type_value}] = ${ofclass.pyname}
+:: #endif
diff --git a/py_gen/templates/_virtual_ofclass.py b/py_gen/templates/_virtual_ofclass.py
index 29f01f7..1e5dcc2 100644
--- a/py_gen/templates/_virtual_ofclass.py
+++ b/py_gen/templates/_virtual_ofclass.py
@@ -1,3 +1,23 @@
+:: import py_gen.util as util
:: superclass_pyname = ofclass.superclass.pyname if ofclass.superclass else "loxi.OFObject"
+:: fmts = { 1: "B", 2: "!H", 4: "!L" }
+:: fmt = fmts[ofclass.discriminator.length]
+:: trail = ' '.join([x.pyname for x in util.ancestors(ofclass)])
class ${ofclass.pyname}(${superclass_pyname}):
- pass
+ subtypes = {}
+
+ @staticmethod
+ def unpack(reader):
+ subtype, = reader.peek(${repr(fmt)}, ${ofclass.discriminator.offset})
+ try:
+ subclass = ${ofclass.pyname}.subtypes[subtype]
+ except KeyError:
+ raise loxi.ProtocolError("unknown ${trail} subtype %#x" % subtype)
+ return subclass.unpack(reader)
+
+:: # Register with our superclass
+:: if ofclass.superclass:
+:: type_field_name = ofclass.superclass.discriminator.name
+:: type_value = ofclass.member_by_name(type_field_name).value
+${superclass_pyname}.subtypes[${type_value}] = ${ofclass.pyname}
+:: #endif
diff --git a/py_gen/templates/action.py b/py_gen/templates/action.py
index 2f1d020..9f80243 100644
--- a/py_gen/templates/action.py
+++ b/py_gen/templates/action.py
@@ -43,9 +43,7 @@
def unpack_list(reader):
def deserializer(reader, typ):
- parser = parsers.get(typ)
- if not parser: raise loxi.ProtocolError("unknown action type %d" % typ)
- return parser(reader)
+ return action.unpack(reader)
return loxi.generic_util.unpack_list_tlv16(reader, deserializer)
:: for ofclass in ofclasses:
@@ -56,46 +54,3 @@
:: #endif
:: #endfor
-
-def parse_experimenter(reader):
- experimenter, = reader.peek("!4xL")
- if experimenter == 0x005c16c7: # Big Switch Networks
- subtype, = reader.peek("!8xL")
- elif experimenter == 0x00002320: # Nicira
- subtype, = reader.peek("!8xH")
- else:
- raise loxi.ProtocolError("unexpected experimenter id %#x" % experimenter)
-
- if subtype in experimenter_parsers[experimenter]:
- return experimenter_parsers[experimenter][subtype](reader)
- else:
- raise loxi.ProtocolError("unexpected BSN experimenter subtype %#x" % subtype)
-
-parsers = {
-:: concrete_ofclasses = [x for x in ofclasses if not x.virtual]
-:: sort_key = lambda x: x.member_by_name('type').value
-:: msgtype_groups = itertools.groupby(sorted(concrete_ofclasses, key=sort_key), sort_key)
-:: for (k, v) in msgtype_groups:
-:: k = util.constant_for_value(version, "ofp_action_type", k)
-:: v = list(v)
-:: if len(v) == 1:
- ${k} : ${v[0].pyname}.unpack,
-:: else:
- ${k} : parse_${k[12:].lower()},
-:: #endif
-:: #endfor
-}
-
-:: experimenter_ofclasses = [x for x in concrete_ofclasses if x.member_by_name('type').value == 0xffff]
-:: sort_key = lambda x: x.member_by_name('experimenter').value
-:: experimenter_ofclasses.sort(key=sort_key)
-:: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
-experimenter_parsers = {
-:: for (experimenter, v) in grouped:
- ${experimenter} : {
-:: for ofclass in v:
- ${ofclass.member_by_name('subtype').value}: ${ofclass.pyname}.unpack,
-:: #endfor
- },
-:: #endfor
-}
diff --git a/py_gen/templates/common.py b/py_gen/templates/common.py
index ddffdf7..6569c77 100644
--- a/py_gen/templates/common.py
+++ b/py_gen/templates/common.py
@@ -54,10 +54,7 @@
def unpack_list_queue_prop(reader):
def deserializer(reader, typ):
- if typ == const.OFPQT_MIN_RATE:
- return queue_prop_min_rate.unpack(reader)
- else:
- raise loxi.ProtocolError("unknown queue prop %d" % typ)
+ return queue_prop.unpack(reader)
return loxi.generic_util.unpack_list_tlv16(reader, deserializer)
def unpack_list_packet_queue(reader):
@@ -68,9 +65,9 @@
def unpack_list_hello_elem(reader):
def deserializer(reader, typ):
- if typ == const.OFPHET_VERSIONBITMAP:
- return hello_elem_versionbitmap.unpack(reader)
- else:
+ try:
+ return hello_elem.unpack(reader)
+ except loxi.ProtocolError:
return None
return [x for x in loxi.generic_util.unpack_list_tlv16(reader, deserializer) if x != None]
diff --git a/py_gen/templates/generic_util.py b/py_gen/templates/generic_util.py
index 28d3b5f..cd62057 100644
--- a/py_gen/templates/generic_util.py
+++ b/py_gen/templates/generic_util.py
@@ -97,11 +97,11 @@
self.offset += len(buf)
return str(buf)
- def peek(self, fmt):
+ def peek(self, fmt, offset=0):
st = struct.Struct(fmt)
- if self.offset + st.size > len(self.buf):
+ if self.offset + offset + st.size > len(self.buf):
raise loxi.ProtocolError("Buffer too short")
- result = st.unpack_from(self.buf, self.offset)
+ result = st.unpack_from(self.buf, self.offset + offset)
return result
def skip(self, length):
diff --git a/py_gen/templates/instruction.py b/py_gen/templates/instruction.py
index 21ab84a..9842f95 100644
--- a/py_gen/templates/instruction.py
+++ b/py_gen/templates/instruction.py
@@ -40,9 +40,7 @@
def unpack_list(reader):
def deserializer(reader, typ):
- parser = parsers.get(typ)
- if not parser: raise loxi.ProtocolError("unknown instruction type %d" % typ)
- return parser(reader)
+ return instruction.unpack(reader)
return loxi.generic_util.unpack_list_tlv16(reader, deserializer)
:: for ofclass in ofclasses:
@@ -53,44 +51,3 @@
:: #endif
:: #endfor
-
-def parse_experimenter(reader):
- experimenter, = reader.peek("!4xL")
- if experimenter == 0x005c16c7: # Big Switch Networks
- subtype, = reader.peek("!8xL")
- else:
- raise loxi.ProtocolError("unexpected experimenter id %#x" % experimenter)
-
- if subtype in experimenter_parsers[experimenter]:
- return experimenter_parsers[experimenter][subtype](reader)
- else:
- raise loxi.ProtocolError("unexpected experimenter id %#x subtype %#x" % (experimenter, subtype))
-
-parsers = {
-:: concrete_ofclasses = [x for x in ofclasses if not x.virtual]
-:: sort_key = lambda x: x.member_by_name('type').value
-:: msgtype_groups = itertools.groupby(sorted(concrete_ofclasses, key=sort_key), sort_key)
-:: for (k, v) in msgtype_groups:
-:: k = util.constant_for_value(version, "ofp_instruction_type", k)
-:: v = list(v)
-:: if len(v) == 1 and k != 'const.OFPIT_EXPERIMENTER':
- ${k} : ${v[0].pyname}.unpack,
-:: else:
- ${k} : parse_${k[12:].lower()},
-:: #endif
-:: #endfor
-}
-
-:: experimenter_ofclasses = [x for x in concrete_ofclasses if x.member_by_name('type').value == 0xffff]
-:: sort_key = lambda x: x.member_by_name('experimenter').value
-:: experimenter_ofclasses.sort(key=sort_key)
-:: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
-experimenter_parsers = {
-:: for (experimenter, v) in grouped:
- ${experimenter} : {
-:: for ofclass in v:
- ${ofclass.member_by_name('subtype').value}: ${ofclass.pyname}.unpack,
-:: #endfor
- },
-:: #endfor
-}
diff --git a/py_gen/templates/message.py b/py_gen/templates/message.py
index 9307676..11e302f 100644
--- a/py_gen/templates/message.py
+++ b/py_gen/templates/message.py
@@ -68,235 +68,4 @@
raise loxi.ProtocolError("wrong OpenFlow version (expected %d, got %d)" % (const.OFP_VERSION, msg_ver))
if len(buf) != msg_len:
raise loxi.ProtocolError("incorrect message size")
- if msg_type in parsers:
- return parsers[msg_type](buf)
- else:
- raise loxi.ProtocolError("unexpected message type")
-
-def parse_error(buf):
- if len(buf) < 8 + 2:
- raise loxi.ProtocolError("message too short")
- err_type, = struct.unpack_from("!H", buf, 8)
- if err_type in error_msg_parsers:
- return error_msg_parsers[err_type](buf)
- else:
- raise loxi.ProtocolError("unexpected error type %u" % err_type)
-
-def parse_flow_mod(buf):
-:: if version == OFVersions.VERSION_1_0:
-:: offset = 57
-:: elif version >= OFVersions.VERSION_1_1:
-:: offset = 25
-:: #endif
- if len(buf) < ${offset} + 1:
- raise loxi.ProtocolError("message too short")
- # Technically uint16_t for OF 1.0
- cmd, = struct.unpack_from("!B", buf, ${offset})
- if cmd in flow_mod_parsers:
- return flow_mod_parsers[cmd](buf)
- else:
- raise loxi.ProtocolError("unexpected flow mod cmd %u" % cmd)
-
-:: if version >= OFVersions.VERSION_1_0:
-def parse_group_mod(buf):
-:: offset = 8
- if len(buf) < ${offset} + 2:
- raise loxi.ProtocolError("message too short")
- cmd, = struct.unpack_from("!H", buf, ${offset})
- if cmd in flow_mod_parsers:
- return group_mod_parsers[cmd](buf)
- else:
- raise loxi.ProtocolError("unexpected group mod cmd %u" % cmd)
-:: #endif
-
-def parse_stats_reply(buf):
- if len(buf) < 8 + 2:
- raise loxi.ProtocolError("message too short")
- stats_type, = struct.unpack_from("!H", buf, 8)
- if stats_type in stats_reply_parsers:
- return stats_reply_parsers[stats_type](buf)
- else:
- raise loxi.ProtocolError("unexpected stats type %u" % stats_type)
-
-def parse_stats_request(buf):
- if len(buf) < 8 + 2:
- raise loxi.ProtocolError("message too short")
- stats_type, = struct.unpack_from("!H", buf, 8)
- if stats_type in stats_request_parsers:
- return stats_request_parsers[stats_type](buf)
- else:
- raise loxi.ProtocolError("unexpected stats type %u" % stats_type)
-
-def parse_experimenter_stats_request(buf):
- if len(buf) < 24:
- raise loxi.ProtocolError("experimenter stats request message too short")
-
- experimenter, exp_type = struct.unpack_from("!LL", buf, 16)
-
- if experimenter in experimenter_stats_request_parsers and \
- exp_type in experimenter_stats_request_parsers[experimenter]:
- return experimenter_stats_request_parsers[experimenter][exp_type](buf)
- else:
- raise loxi.ProtocolError("unexpected stats request experimenter %#x exp_type %#x" % (experimenter, exp_type))
-
-def parse_experimenter_stats_reply(buf):
- if len(buf) < 24:
- raise loxi.ProtocolError("experimenter stats reply message too short")
-
- experimenter, exp_type = struct.unpack_from("!LL", buf, 16)
-
- if experimenter in experimenter_stats_reply_parsers and \
- exp_type in experimenter_stats_reply_parsers[experimenter]:
- return experimenter_stats_reply_parsers[experimenter][exp_type](buf)
- else:
- raise loxi.ProtocolError("unexpected stats reply experimenter %#x exp_type %#x" % (experimenter, exp_type))
-
-def parse_experimenter(buf):
- if len(buf) < 16:
- raise loxi.ProtocolError("experimenter message too short")
-
- experimenter, = struct.unpack_from("!L", buf, 8)
- if experimenter == 0x005c16c7: # Big Switch Networks
- subtype, = struct.unpack_from("!L", buf, 12)
- elif experimenter == 0x00002320: # Nicira
- subtype, = struct.unpack_from("!L", buf, 12)
- else:
- raise loxi.ProtocolError("unexpected experimenter id %#x" % experimenter)
-
- if subtype in experimenter_parsers[experimenter]:
- return experimenter_parsers[experimenter][subtype](buf)
- else:
- raise loxi.ProtocolError("unexpected experimenter %#x subtype %#x" % (experimenter, subtype))
-
-parsers = {
-:: concrete_ofclasses = [x for x in ofclasses if not x.virtual]
-:: sort_key = lambda x: x.member_by_name('type').value
-:: msgtype_groups = itertools.groupby(sorted(concrete_ofclasses, key=sort_key), sort_key)
-:: for (k, v) in msgtype_groups:
-:: k = util.constant_for_value(version, "ofp_type", k)
-:: v = list(v)
-:: if len(v) == 1:
- ${k} : ${v[0].pyname}.unpack,
-:: else:
- ${k} : parse_${k[11:].lower()},
-:: #endif
-:: #endfor
-}
-
-error_msg_parsers = {
- const.OFPET_HELLO_FAILED : hello_failed_error_msg.unpack,
- const.OFPET_BAD_REQUEST : bad_request_error_msg.unpack,
- const.OFPET_BAD_ACTION : bad_action_error_msg.unpack,
- const.OFPET_FLOW_MOD_FAILED : flow_mod_failed_error_msg.unpack,
- const.OFPET_PORT_MOD_FAILED : port_mod_failed_error_msg.unpack,
- const.OFPET_QUEUE_OP_FAILED : queue_op_failed_error_msg.unpack,
-:: if version >= OFVersions.VERSION_1_1:
- const.OFPET_BAD_INSTRUCTION : bad_instruction_error_msg.unpack,
- const.OFPET_BAD_MATCH : bad_match_error_msg.unpack,
- const.OFPET_GROUP_MOD_FAILED : group_mod_failed_error_msg.unpack,
- const.OFPET_TABLE_MOD_FAILED : table_mod_failed_error_msg.unpack,
- const.OFPET_SWITCH_CONFIG_FAILED : switch_config_failed_error_msg.unpack,
-:: #endif
-:: if version >= OFVersions.VERSION_1_2:
- const.OFPET_ROLE_REQUEST_FAILED : role_request_failed_error_msg.unpack,
- const.OFPET_EXPERIMENTER : experimenter_error_msg.unpack,
-:: #endif
-:: if version >= OFVersions.VERSION_1_3:
- const.OFPET_METER_MOD_FAILED : meter_mod_failed_error_msg.unpack,
- const.OFPET_TABLE_FEATURES_FAILED : table_features_failed_error_msg.unpack,
-:: #endif
-}
-
-flow_mod_parsers = {
- const.OFPFC_ADD : flow_add.unpack,
- const.OFPFC_MODIFY : flow_modify.unpack,
- const.OFPFC_MODIFY_STRICT : flow_modify_strict.unpack,
- const.OFPFC_DELETE : flow_delete.unpack,
- const.OFPFC_DELETE_STRICT : flow_delete_strict.unpack,
-}
-
-:: if version >= OFVersions.VERSION_1_1:
-group_mod_parsers = {
- const.OFPGC_ADD : group_add.unpack,
- const.OFPGC_MODIFY : group_modify.unpack,
- const.OFPGC_DELETE : group_delete.unpack,
-}
-:: #endif
-
-stats_reply_parsers = {
- const.OFPST_DESC : desc_stats_reply.unpack,
- const.OFPST_FLOW : flow_stats_reply.unpack,
- const.OFPST_AGGREGATE : aggregate_stats_reply.unpack,
- const.OFPST_TABLE : table_stats_reply.unpack,
- const.OFPST_PORT : port_stats_reply.unpack,
- const.OFPST_QUEUE : queue_stats_reply.unpack,
- const.OFPST_EXPERIMENTER : parse_experimenter_stats_reply,
-:: if version >= OFVersions.VERSION_1_1:
- const.OFPST_GROUP : group_stats_reply.unpack,
- const.OFPST_GROUP_DESC : group_desc_stats_reply.unpack,
-:: #endif
-:: if version >= OFVersions.VERSION_1_2:
- const.OFPST_GROUP_FEATURES : group_features_stats_reply.unpack,
-:: #endif
-:: if version >= OFVersions.VERSION_1_3:
- const.OFPST_METER : meter_stats_reply.unpack,
- const.OFPST_METER_CONFIG : meter_config_stats_reply.unpack,
- const.OFPST_METER_FEATURES : meter_features_stats_reply.unpack,
- const.OFPST_TABLE_FEATURES : table_features_stats_reply.unpack,
- const.OFPST_PORT_DESC : port_desc_stats_reply.unpack,
-:: #endif
-}
-
-stats_request_parsers = {
- const.OFPST_DESC : desc_stats_request.unpack,
- const.OFPST_FLOW : flow_stats_request.unpack,
- const.OFPST_AGGREGATE : aggregate_stats_request.unpack,
- const.OFPST_TABLE : table_stats_request.unpack,
- const.OFPST_PORT : port_stats_request.unpack,
- const.OFPST_QUEUE : queue_stats_request.unpack,
- const.OFPST_EXPERIMENTER : parse_experimenter_stats_request,
-:: if version >= OFVersions.VERSION_1_1:
- const.OFPST_GROUP : group_stats_request.unpack,
- const.OFPST_GROUP_DESC : group_desc_stats_request.unpack,
-:: #endif
-:: if version >= OFVersions.VERSION_1_2:
- const.OFPST_GROUP_FEATURES : group_features_stats_request.unpack,
-:: #endif
-:: if version >= OFVersions.VERSION_1_3:
- const.OFPST_METER : meter_stats_request.unpack,
- const.OFPST_METER_CONFIG : meter_config_stats_request.unpack,
- const.OFPST_METER_FEATURES : meter_features_stats_request.unpack,
- const.OFPST_TABLE_FEATURES : table_features_stats_request.unpack,
- const.OFPST_PORT_DESC : port_desc_stats_request.unpack,
-:: #endif
-}
-
-:: experimenter_ofclasses = [x for x in concrete_ofclasses if x.member_by_name('type').value == 4]
-:: sort_key = lambda x: x.member_by_name('experimenter').value
-:: experimenter_ofclasses.sort(key=sort_key)
-:: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
-experimenter_parsers = {
-:: for (experimenter, v) in grouped:
- ${experimenter} : {
-:: for ofclass in v:
- ${ofclass.member_by_name('subtype').value}: ${ofclass.pyname}.unpack,
-:: #endfor
- },
-:: #endfor
-}
-
-experimenter_stats_request_parsers = {
- 0x005c16c7: {
-:: if version >= OFVersions.VERSION_1_3:
- 1: bsn_lacp_stats_request.unpack,
-:: #endif
- },
-}
-
-experimenter_stats_reply_parsers = {
- 0x005c16c7: {
-:: if version >= OFVersions.VERSION_1_3:
- 1: bsn_lacp_stats_reply.unpack,
-:: #endif
- },
-}
+ return message.unpack(loxi.generic_util.OFReader(buf))
diff --git a/py_gen/templates/meter_band.py b/py_gen/templates/meter_band.py
index ef73978..38607e7 100644
--- a/py_gen/templates/meter_band.py
+++ b/py_gen/templates/meter_band.py
@@ -39,9 +39,7 @@
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 meter_band.unpack(reader)
return loxi.generic_util.unpack_list_tlv16(reader, deserializer)
:: for ofclass in ofclasses:
@@ -52,18 +50,3 @@
:: #endif
:: #endfor
-
-parsers = {
-:: concrete_ofclasses = [x for x in ofclasses if not x.virtual]
-:: sort_key = lambda x: x.member_by_name('type').value
-:: msgtype_groups = itertools.groupby(sorted(concrete_ofclasses, key=sort_key), sort_key)
-:: for (k, v) in msgtype_groups:
-:: k = util.constant_for_value(version, "ofp_meter_band_type", k)
-:: 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/templates/oxm.py b/py_gen/templates/oxm.py
index 4adfab0..71bf7df 100644
--- a/py_gen/templates/oxm.py
+++ b/py_gen/templates/oxm.py
@@ -38,12 +38,7 @@
import loxi
def unpack(reader):
- type_len, = reader.peek('!L')
- if type_len in parsers:
- return parsers[type_len](reader)
- else:
- raise loxi.ProtocolError("unknown OXM cls=%#x type=%#x masked=%d len=%d (%#x)" % \
- ((type_len >> 16) & 0xffff, (type_len >> 9) & 0x7f, (type_len >> 8) & 1, type_len & 0xff, type_len))
+ return oxm.unpack(reader)
def unpack_list(reader):
return loxi.generic_util.unpack_list(reader, unpack)
@@ -56,11 +51,3 @@
:: #endif
:: #endfor
-
-parsers = {
-:: concrete_ofclasses = [x for x in ofclasses if not x.virtual]
-:: key = lambda x: x.member_by_name('type_len').value
-:: for ofclass in sorted(concrete_ofclasses, key=key):
- ${key(ofclass)} : ${ofclass.pyname}.unpack,
-:: #endfor
-}
diff --git a/py_gen/tests/of10.py b/py_gen/tests/of10.py
index 818f123..876926e 100644
--- a/py_gen/tests/of10.py
+++ b/py_gen/tests/of10.py
@@ -114,7 +114,7 @@
def test_invalid_action_type(self):
buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
- with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action type'):
+ with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action subtype'):
ofp.action.unpack_list(OFReader(buf))
class TestConstants(unittest.TestCase):
diff --git a/py_gen/util.py b/py_gen/util.py
index b405441..2a0dc54 100644
--- a/py_gen/util.py
+++ b/py_gen/util.py
@@ -49,3 +49,10 @@
if value == value2:
return "const." + name
return repr(value)
+
+def ancestors(ofclass):
+ r = []
+ while ofclass:
+ r.append(ofclass)
+ ofclass = ofclass.superclass
+ return r