Merge into master from pull request #17:
Move PyLoxi onto the new IR (https://github.com/floodlight/loxigen/pull/17)
diff --git a/openflow_input/bsn_l2_table b/openflow_input/bsn_l2_table
index e3938a8..8075d7f 100644
--- a/openflow_input/bsn_l2_table
+++ b/openflow_input/bsn_l2_table
@@ -36,9 +36,9 @@
uint32_t experimenter == 0x5c16c7;
uint32_t subtype == 12;
uint8_t l2_table_enable; // 1 == enabled, 0 == disabled
- uint8_t pad;
+ pad(1);
uint16_t l2_table_priority; // priority of all flows in L2 table
- uint8_t[4] pad;
+ pad(4);
};
struct of_bsn_set_l2_table_reply {
@@ -49,7 +49,7 @@
uint32_t experimenter == 0x5c16c7;
uint32_t subtype == 24;
uint8_t l2_table_enable; // Resulting state: 1 == enabled, 0 == disabled
- uint8_t pad;
+ pad(1);
uint16_t l2_table_priority; // priority used, must match request if ok
uint32_t status; // 0 means success
};
@@ -71,7 +71,7 @@
uint32_t experimenter == 0x5c16c7;
uint32_t subtype == 14;
uint8_t l2_table_enable; // 1 == enabled, 0 == disabled
- uint8_t pad;
+ pad(1);
uint16_t l2_table_priority; // priority of all flows in L2 table
- uint8_t[4] pad;
+ pad(4);
};
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index d6d8b32..4e93c4c 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -32,135 +32,67 @@
import loxi_utils.loxi_utils as utils
import util
import oftype
+from loxi_ir import *
-OFClass = namedtuple('OFClass', ['name', 'pyname', 'members', 'type_members',
- 'min_length', 'is_fixed_length'])
-Member = namedtuple('Member', ['name', 'oftype'])
-LengthMember = namedtuple('LengthMember', ['name', 'oftype'])
-FieldLengthMember = namedtuple('FieldLengthMember', ['name', 'oftype', 'field_name'])
-TypeMember = namedtuple('TypeMember', ['name', 'oftype', 'value'])
-PadMember = namedtuple('PadMember', ['length'])
+PyOFClass = namedtuple('PyOFClass', ['name', 'pyname', 'members', 'type_members',
+ 'min_length', 'is_fixed_length'])
-# XXX move to frontend
-field_length_members = {
- ('of_packet_out', 1, 'actions_len') : 'actions',
- ('of_packet_out', 2, 'actions_len') : 'actions',
- ('of_packet_out', 3, 'actions_len') : 'actions',
- ('of_packet_out', 4, 'actions_len') : 'actions',
-}
-
-def get_type_values(cls, version):
- """
- Returns a map from the name of the type member to its value.
- """
- type_values = {}
-
- # Primary wire type
- if utils.class_is_message(cls):
- type_values['version'] = 'const.OFP_VERSION'
- type_values['type'] = util.constant_for_value(version, "ofp_type", util.primary_wire_type(cls, version))
- if cls in type_maps.flow_mod_list:
- type_values['_command'] = util.constant_for_value(version, "ofp_flow_mod_command",
- type_maps.flow_mod_types[version][cls[8:]])
- if cls in type_maps.stats_request_list:
- type_values['stats_type'] = util.constant_for_value(version, "ofp_stats_types",
- type_maps.stats_types[version][cls[3:-14]])
- if cls in type_maps.stats_reply_list:
- type_values['stats_type'] = util.constant_for_value(version, "ofp_stats_types",
- type_maps.stats_types[version][cls[3:-12]])
- if type_maps.message_is_extension(cls, version):
- type_values['experimenter'] = '%#x' % type_maps.extension_to_experimenter_id(cls)
- type_values['subtype'] = type_maps.extension_message_to_subtype(cls, version)
- elif utils.class_is_action(cls):
- type_values['type'] = util.constant_for_value(version, "ofp_action_type", util.primary_wire_type(cls, version))
- if type_maps.action_is_extension(cls, version):
- type_values['experimenter'] = '%#x' % type_maps.extension_to_experimenter_id(cls)
- type_values['subtype'] = type_maps.extension_action_to_subtype(cls, version)
- elif utils.class_is_queue_prop(cls):
- type_values['type'] = util.constant_for_value(version, "ofp_queue_properties", util.primary_wire_type(cls, version))
- elif utils.class_is_hello_elem(cls):
- type_values['type'] = util.constant_for_value(version, "ofp_hello_elem_type", util.primary_wire_type(cls, version))
+# Return the name for the generated Python class
+def generate_pyname(cls):
+ if utils.class_is_action(cls):
+ return cls[10:]
elif utils.class_is_oxm(cls):
- oxm_class = 0x8000
- oxm_type = util.primary_wire_type(cls, version)
- oxm_masked = cls.find('masked') != -1 and 1 or 0
- oxm_len = of_g.base_length[(cls, version)] - 4
- type_values['type_len'] = '%#x' % (oxm_class << 16 | oxm_type << 8 | \
- oxm_masked << 8 | oxm_len)
- elif cls == "of_match_v2":
- type_values['type'] = 0
- elif cls == "of_match_v3":
- type_values['type'] = 1
+ return cls[7:]
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 cls[14:]
elif utils.class_is_instruction(cls):
- type_values['type'] = util.constant_for_value(version, "ofp_instruction_type", util.primary_wire_type(cls, version))
+ return cls[15:]
+ else:
+ return cls[3:]
- return type_values
-
-# Create intermediate representation
+# Create intermediate representation, extended from the LOXI IR
+# HACK the oftype member attribute is replaced with an OFType instance
def build_ofclasses(version):
- blacklist = ["of_action", "of_action_header", "of_header", "of_queue_prop",
- "of_queue_prop_header", "of_experimenter", "of_action_experimenter",
- "of_oxm", "of_oxm_header", "of_oxm_experimenter_header",
- "of_hello_elem", "of_hello_elem_header"]
+ blacklist = ["of_experimenter", "of_action_experimenter"]
ofclasses = []
- for cls in of_g.standard_class_order:
+ for ofclass in of_g.ir[version].classes:
+ cls = ofclass.name
if type_maps.class_is_virtual(cls):
continue
- if version not in of_g.unified[cls] or cls in blacklist:
+ if cls in blacklist:
continue
- unified_class = util.lookup_unified_class(cls, version)
- # Name for the generated Python class
- if utils.class_is_action(cls):
- pyname = cls[10:]
- elif utils.class_is_oxm(cls):
- pyname = cls[7:]
- elif utils.class_is_meter_band(cls):
- pyname = cls[14:]
- elif utils.class_is_instruction(cls):
- pyname = cls[15:]
- else:
- pyname = cls[3:]
-
- type_values = get_type_values(cls, version)
members = []
type_members = []
- pad_count = 0
-
- for member in unified_class['members']:
- if member['name'] in ['length', 'len']:
- members.append(LengthMember(name=member['name'],
- oftype=oftype.OFType(member['m_type'], version)))
- elif (cls, version, member['name']) in field_length_members:
- field_name = field_length_members[(cls, version, member['name'])]
- members.append(FieldLengthMember(name=member['name'],
- oftype=oftype.OFType(member['m_type'], version),
- field_name=field_name))
- elif member['name'] in type_values:
- members.append(TypeMember(name=member['name'],
- oftype=oftype.OFType(member['m_type'], version),
- value=type_values[member['name']]))
+ for m in ofclass.members:
+ if type(m) == OFTypeMember:
+ members.append(m)
type_members.append(members[-1])
- elif member['name'].startswith("pad"):
- # HACK this should be moved to the frontend
- pad_oftype = oftype.OFType(member['m_type'], version)
- length = struct.calcsize("!" + pad_oftype._pack_fmt())
- if pad_oftype.is_array: length *= pad_oftype.array_length
- members.append(PadMember(length=length))
- else:
- members.append(Member(name=member['name'],
- oftype=oftype.OFType(member['m_type'], version)))
+ elif type(m) == OFLengthMember:
+ members.append(m)
+ elif type(m) == OFFieldLengthMember:
+ members.append(m)
+ elif type(m) == OFPadMember:
+ members.append(m)
+ elif type(m) == OFDataMember:
+ if utils.class_is_message(ofclass.name) and m.name == 'version':
+ # HACK move to frontend
+ members.append(OFTypeMember(
+ name=m.name,
+ oftype=m.oftype,
+ value=version))
+ type_members.append(members[-1])
+ else:
+ members.append(m)
ofclasses.append(
- OFClass(name=cls,
- pyname=pyname,
- members=members,
- type_members=type_members,
- min_length=of_g.base_length[(cls, version)],
- is_fixed_length=(cls, version) in of_g.is_fixed_length))
+ PyOFClass(name=cls,
+ pyname=generate_pyname(cls),
+ members=members,
+ type_members=type_members,
+ min_length=of_g.base_length[(cls, version)],
+ is_fixed_length=(cls, version) in of_g.is_fixed_length))
return ofclasses
def generate_init(out, name, version):
@@ -187,16 +119,8 @@
util.render_template(out, 'common.py', ofclasses=ofclasses, version=version)
def generate_const(out, name, version):
- groups = {}
- for (group, idents) in of_g.identifiers_by_group.items():
- items = []
- for ident in idents:
- info = of_g.identifiers[ident]
- if version in info["values_by_version"]:
- items.append((info["ofp_name"], info["values_by_version"][version]))
- if items:
- groups[group] = items
- util.render_template(out, 'const.py', version=version, groups=groups)
+ util.render_template(out, 'const.py', version=version,
+ enums=of_g.ir[version].enums)
def generate_instruction(out, name, version):
ofclasses = [x for x in build_ofclasses(version)
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index f90cff5..cf87ded 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -25,197 +25,203 @@
# EPL for the specific language governing permissions and limitations
# under the EPL.
-import of_g
-import loxi_utils.loxi_utils as utils
-import loxi_front_end.type_maps
-import unittest
+from collections import namedtuple
-class OFType(object):
- """
- Encapsulates knowledge about the OpenFlow type system.
- """
+OFTypeData = namedtuple("OFTypeData", ["init", "pack", "unpack"])
- version = None
- base = None
- is_array = False
- array_length = None
+# Map from LOXI type name to an object with templates for init, pack, and unpack
+# Most types are defined using the convenience code below. This dict should
+# only be used directly for special cases such as primitive types.
+type_data_map = {
+ 'char': OFTypeData(
+ init='0',
+ pack='struct.pack("!B", %s)',
+ unpack='%s.read("!B")[0]'),
- def __init__(self, string, version):
- self.version = version
- self.array_length, self.base = utils.type_dec_to_count_base(string)
- self.is_array = self.array_length != 1
+ 'uint8_t': OFTypeData(
+ init='0',
+ pack='struct.pack("!B", %s)',
+ unpack='%s.read("!B")[0]'),
- def gen_init_expr(self):
- if utils.class_is_list(self.base):
- v = "[]"
- elif self.base.find("uint") == 0 or self.base in ["char", "of_port_no_t"]:
- v = "0"
- elif self.base == 'of_mac_addr_t':
- v = '[0,0,0,0,0,0]'
- elif self.base == 'of_ipv6_t':
- v = repr('\x00' * 16)
- elif self.base == 'of_wc_bmap_t':
- if self.version in [1,2]:
- v = 'const.OFPFW_ALL'
- else:
- v = 0
- elif self.base == "of_match_bmap_t":
- if self.version in [1,2]:
- v = 'const.OFPFW_ALL'
- else:
- v = 0
- elif self.base in ['of_octets_t', 'of_port_name_t', 'of_table_name_t',
- 'of_desc_str_t', 'of_serial_num_t']:
- v = '""'
- elif self.base == 'of_match_t':
- v = 'common.match()'
- elif self.base == 'of_port_desc_t':
- v = 'common.port_desc()'
- elif self.base == 'of_meter_features_t':
- v = 'common.meter_features()'
- elif self.base == 'of_bsn_vport_q_in_q_t':
- v = 'common.bsn_vport_q_in_q()'
- else:
- v = "None"
+ 'uint16_t': OFTypeData(
+ init='0',
+ pack='struct.pack("!H", %s)',
+ unpack='%s.read("!H")[0]'),
- if self.is_array:
- return "[" + ','.join([v] * self.array_length) + "]"
- else:
- return v
+ 'uint32_t': OFTypeData(
+ init='0',
+ pack='struct.pack("!L", %s)',
+ unpack='%s.read("!L")[0]'),
- def gen_pack_expr(self, expr_expr):
- pack_fmt = self._pack_fmt()
- if pack_fmt and not self.is_array:
- return 'struct.pack("!%s", %s)' % (pack_fmt, expr_expr)
- elif pack_fmt and self.is_array:
- return 'struct.pack("!%s%s", *%s)' % (self.array_length, pack_fmt, expr_expr)
- elif self.base == 'of_octets_t':
- return expr_expr
- elif utils.class_is_list(self.base):
- return '"".join([x.pack() for x in %s])' % expr_expr
- elif self.base == 'of_mac_addr_t':
- return 'struct.pack("!6B", *%s)' % expr_expr
- elif self.base == 'of_ipv6_t':
- return 'struct.pack("!16s", %s)' % expr_expr
- elif self.base in ['of_match_t', 'of_port_desc_t', 'of_meter_features_t', 'of_bsn_vport_q_in_q_t']:
- return '%s.pack()' % expr_expr
- elif self.base == 'of_port_name_t':
- return self._gen_string_pack_expr(16, expr_expr)
- elif self.base == 'of_table_name_t' or self.base == 'of_serial_num_t':
- return self._gen_string_pack_expr(32, expr_expr)
- elif self.base == 'of_desc_str_t':
- return self._gen_string_pack_expr(256, expr_expr)
- else:
- return "loxi.unimplemented('pack %s')" % self.base
+ 'uint64_t': OFTypeData(
+ init='0',
+ pack='struct.pack("!Q", %s)',
+ unpack='%s.read("!Q")[0]'),
- def _gen_string_pack_expr(self, length, expr_expr):
- return 'struct.pack("!%ds", %s)' % (length, expr_expr)
+ 'of_port_no_t': OFTypeData(
+ init='0',
+ pack='util.pack_port_no(%s)',
+ unpack='util.unpack_port_no(%s)'),
- def gen_unpack_expr(self, reader_expr):
- pack_fmt = self._pack_fmt()
- if pack_fmt and not self.is_array:
- return "%s.read('!%s')[0]" % (reader_expr, pack_fmt)
- elif pack_fmt and self.is_array:
- return "list(%s.read('!%d%s'))" % (self.array_length, pack_fmt)
- elif self.base == 'of_octets_t':
- return "str(%s.read_all())" % (reader_expr)
- elif self.base == 'of_mac_addr_t':
- return "list(%s.read('!6B'))" % (reader_expr)
- elif self.base == 'of_ipv6_t':
- return "%s.read('!16s')[0]" % (reader_expr)
- elif self.base == 'of_match_t':
- return 'common.match.unpack(%s)' % (reader_expr)
- elif self.base == 'of_port_desc_t':
- return 'common.port_desc.unpack(%s)' % (reader_expr)
- elif self.base == 'of_list_action_t':
- return 'action.unpack_list(%s)' % (reader_expr)
- elif self.base == 'of_list_flow_stats_entry_t':
- return 'common.unpack_list_flow_stats_entry(%s)' % (reader_expr)
- elif self.base == 'of_list_queue_prop_t':
- return 'common.unpack_list_queue_prop(%s)' % (reader_expr)
- elif self.base == 'of_list_packet_queue_t':
- return 'common.unpack_list_packet_queue(%s)' % (reader_expr)
- elif self.base == 'of_list_hello_elem_t':
- return 'common.unpack_list_hello_elem(%s)' % (reader_expr)
- elif self.base == 'of_list_oxm_t':
- # HACK need the match_v3 length field
- return 'oxm.unpack_list(%s.slice(_length-4))' % (reader_expr)
- elif self.base == 'of_list_bucket_t':
- return 'common.unpack_list_bucket(%s)' % (reader_expr)
- elif self.base == 'of_list_group_desc_stats_entry_t':
- 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_list_meter_stats_t':
- return 'common.unpack_list_meter_stats(%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':
- return self._gen_string_unpack_expr(reader_expr, 32)
- elif self.base == 'of_desc_str_t':
- return self._gen_string_unpack_expr(reader_expr, 256)
- elif self.base == 'of_meter_features_t':
- return 'common.meter_features.unpack(%s)' % (reader_expr)
- elif self.base == 'of_bsn_vport_q_in_q_t':
- return 'common.bsn_vport_q_in_q.unpack(%s)' % (reader_expr)
- elif self.base == 'of_list_instruction_t':
- return 'instruction.unpack_list(%s)' % (reader_expr)
- elif utils.class_is_list(self.base):
- element_cls = utils.list_to_entry_type(self.base)[:-2]
- if ((element_cls, self.version) in of_g.is_fixed_length) \
- and not element_cls in loxi_front_end.type_maps.inheritance_map:
- klass_name = self.base[8:-2]
- element_size, = of_g.base_length[(element_cls, self.version)],
- return 'loxi.generic_util.unpack_list(%s, common.%s.unpack)' % (reader_expr, klass_name)
- else:
- return "loxi.unimplemented('unpack list %s')" % self.base
- else:
- return "loxi.unimplemented('unpack %s')" % self.base
+ 'of_fm_cmd_t': OFTypeData(
+ init='0',
+ pack='util.pack_fm_cmd(%s)',
+ unpack='util.unpack_fm_cmd(%s)'),
- def _gen_string_unpack_expr(self, reader_expr, length):
- return '%s.read("!%ds")[0].rstrip("\\x00")' % (reader_expr, length)
+ 'of_wc_bmap_t': OFTypeData(
+ init='util.init_wc_bmap()',
+ pack='util.pack_wc_bmap(%s)',
+ unpack='util.unpack_wc_bmap(%s)'),
- def _pack_fmt(self):
- if self.base == "char":
- return "B"
- if self.base == "uint8_t":
- return "B"
- if self.base == "uint16_t":
- return "H"
- if self.base == "uint32_t":
- return "L"
- if self.base == "uint64_t":
- return "Q"
- if self.base == "of_port_no_t":
- if self.version == of_g.VERSION_1_0:
- return "H"
- else:
- return "L"
- if self.base == "of_fm_cmd_t":
- if self.version == of_g.VERSION_1_0:
- return "H"
- else:
- return "B"
- if self.base in ["of_wc_bmap_t", "of_match_bmap_t"]:
- if self.version in [of_g.VERSION_1_0, of_g.VERSION_1_1]:
- return "L"
- else:
- return "Q"
- return None
+ 'of_match_bmap_t': OFTypeData(
+ init='util.init_match_bmap()',
+ pack='util.pack_match_bmap(%s)',
+ unpack='util.unpack_match_bmap(%s)'),
-class TestOFType(unittest.TestCase):
- def test_init(self):
- from oftype import OFType
- self.assertEquals("None", OFType("of_list_action_t", 1).gen_init_expr())
- self.assertEquals("[0,0,0]", OFType("uint32_t[3]", 1).gen_init_expr())
+ 'of_ipv6_t': OFTypeData(
+ init="'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'",
+ pack='struct.pack("!16s", %s)',
+ unpack="%s.read('!16s')[0]"),
- def test_pack(self):
- self.assertEquals('struct.pack("!16s", "foo")', OFType("of_port_name_t", 1).gen_pack_expr('"foo"'))
+ 'of_mac_addr_t': OFTypeData(
+ init='[0,0,0,0,0,0]',
+ pack='struct.pack("!6B", *%s)',
+ unpack="list(%s.read('!6B'))"),
- def test_unpack(self):
- self.assertEquals('str(buffer(buf, 8, 16)).rstrip("\\x00")', OFType("of_port_name_t", 1).gen_unpack_expr('buf', 8))
+ 'of_octets_t': OFTypeData(
+ init="''",
+ pack='%s',
+ unpack='str(%s.read_all())'),
-if __name__ == '__main__':
- unittest.main()
+ # HACK need the match_v3 length field
+ 'list(of_oxm_t)': OFTypeData(
+ init='[]',
+ pack='util.pack_list(%s)',
+ unpack='oxm.unpack_list(%s.slice(_length-4))'),
+
+ # TODO implement unpack
+ 'list(of_table_features_t)': OFTypeData(
+ init='[]',
+ pack='util.pack_list(%s)',
+ unpack=None),
+
+ # TODO implement unpack
+ 'list(of_action_id_t)': OFTypeData(
+ init='[]',
+ pack='util.pack_list(%s)',
+ unpack=None),
+
+ # TODO implement unpack
+ 'list(of_table_feature_prop_t)': OFTypeData(
+ init='[]',
+ pack='util.pack_list(%s)',
+ unpack=None),
+}
+
+## Fixed length strings
+
+# Map from class name to length
+fixed_length_strings = {
+ 'of_port_name_t': 16,
+ 'of_table_name_t': 32,
+ 'of_serial_num_t': 32,
+ 'of_desc_str_t': 256,
+}
+
+for (cls, length) in fixed_length_strings.items():
+ type_data_map[cls] = OFTypeData(
+ init='""',
+ pack='struct.pack("!%ds", %%s)' % length,
+ unpack='%%s.read("!%ds")[0].rstrip("\\x00")' % length)
+
+## Embedded structs
+
+# Map from class name to Python class name
+embedded_structs = {
+ 'of_match_t': 'common.match',
+ 'of_port_desc_t': 'common.port_desc',
+ 'of_meter_features_t': 'common.meter_features',
+ 'of_bsn_vport_q_in_q_t': 'common.bsn_vport_q_in_q',
+}
+
+for (cls, pyclass) in embedded_structs.items():
+ type_data_map[cls] = OFTypeData(
+ init='%s()' % pyclass,
+ pack='%s.pack()',
+ unpack='%s.unpack(%%s)' % pyclass)
+
+## Variable element length lists
+
+# Map from list class name to list deserializer
+variable_elem_len_lists = {
+ 'list(of_action_t)': 'action.unpack_list',
+ 'list(of_bucket_t)': 'common.unpack_list_bucket',
+ 'list(of_flow_stats_entry_t)': 'common.unpack_list_flow_stats_entry',
+ 'list(of_group_desc_stats_entry_t)': 'common.unpack_list_group_desc_stats_entry',
+ 'list(of_group_stats_entry_t)': 'common.unpack_list_group_stats_entry',
+ 'list(of_hello_elem_t)': 'common.unpack_list_hello_elem',
+ 'list(of_instruction_t)': 'instruction.unpack_list',
+ 'list(of_meter_band_t)': 'meter_band.unpack_list',
+ 'list(of_meter_stats_t)': 'common.unpack_list_meter_stats',
+ 'list(of_packet_queue_t)': 'common.unpack_list_packet_queue',
+ 'list(of_queue_prop_t)': 'common.unpack_list_queue_prop',
+}
+
+for (cls, deserializer) in variable_elem_len_lists.items():
+ type_data_map[cls] = OFTypeData(
+ init='[]',
+ pack='util.pack_list(%s)',
+ unpack='%s(%%s)' % deserializer)
+
+## Fixed element length lists
+
+# Map from list class name to list element deserializer
+fixed_elem_len_lists = {
+ 'list(of_bsn_interface_t)': 'common.bsn_interface.unpack',
+ 'list(of_bucket_counter_t)': 'common.bucket_counter.unpack',
+ 'list(of_meter_band_stats_t)': 'common.meter_band_stats.unpack',
+ 'list(of_port_desc_t)': 'common.port_desc.unpack',
+ 'list(of_port_stats_entry_t)': 'common.port_stats_entry.unpack',
+ 'list(of_queue_stats_entry_t)': 'common.queue_stats_entry.unpack',
+ 'list(of_table_stats_entry_t)': 'common.table_stats_entry.unpack',
+ 'list(of_uint32_t)': 'common.uint32.unpack',
+ 'list(of_uint8_t)': 'common.uint8.unpack',
+}
+
+for (cls, element_deserializer) in fixed_elem_len_lists.items():
+ type_data_map[cls] = OFTypeData(
+ init='[]',
+ pack='util.pack_list(%s)',
+ unpack='loxi.generic_util.unpack_list(%%s, %s)' % element_deserializer)
+
+## Public interface
+
+# Return an initializer expression for the given oftype
+def gen_init_expr(oftype):
+ type_data = type_data_map.get(oftype)
+ if type_data and type_data.init:
+ return type_data.init
+ else:
+ return "loxi.unimplemented('init %s')" % oftype
+
+# Return a pack expression for the given oftype
+#
+# 'value_expr' is a string of Python code which will evaluate to
+# the value to be packed.
+def gen_pack_expr(oftype, value_expr):
+ type_data = type_data_map.get(oftype)
+ if type_data and type_data.pack:
+ return type_data.pack % value_expr
+ else:
+ return "loxi.unimplemented('pack %s')" % oftype
+
+# Return an unpack expression for the given oftype
+#
+# 'reader_expr' is a string of Python code which will evaluate to
+# the OFReader instance used for deserialization.
+def gen_unpack_expr(oftype, reader_expr):
+ type_data = type_data_map.get(oftype)
+ if type_data and type_data.unpack:
+ return type_data.unpack % reader_expr
+ else:
+ return "loxi.unimplemented('unpack %s')" % oftype
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
index 8c21bb3..ec6b04e 100644
--- a/py_gen/templates/_ofclass.py
+++ b/py_gen/templates/_ofclass.py
@@ -1,5 +1,6 @@
-:: from py_gen.codegen import Member, LengthMember, TypeMember
-:: normal_members = [m for m in ofclass.members if type(m) == Member]
+:: from loxi_ir import *
+:: import py_gen.oftype
+:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
class ${ofclass.pyname}(${superclass}):
:: for m in ofclass.type_members:
${m.name} = ${m.value}
@@ -10,7 +11,7 @@
if ${m.name} != None:
self.${m.name} = ${m.name}
else:
- self.${m.name} = ${m.oftype.gen_init_expr()}
+ self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype)}
:: #endfor
return
diff --git a/py_gen/templates/_pack.py b/py_gen/templates/_pack.py
index 0c208f7..4714e97 100644
--- a/py_gen/templates/_pack.py
+++ b/py_gen/templates/_pack.py
@@ -26,36 +26,37 @@
:: # under the EPL.
::
:: # TODO coalesce format strings
-:: from py_gen.codegen import Member, LengthMember, FieldLengthMember, TypeMember, PadMember
+:: from loxi_ir import *
+:: from py_gen.oftype import gen_pack_expr
:: length_member = None
:: length_member_index = None
:: field_length_members = {}
:: field_length_indexes = {}
:: index = 0
:: for m in ofclass.members:
-:: if type(m) == LengthMember:
+:: if type(m) == OFLengthMember:
:: length_member = m
:: length_member_index = index
- packed.append(${m.oftype.gen_pack_expr('0')}) # placeholder for ${m.name} at index ${index}
-:: elif type(m) == FieldLengthMember:
+ packed.append(${gen_pack_expr(m.oftype, '0')}) # placeholder for ${m.name} at index ${index}
+:: elif type(m) == OFFieldLengthMember:
:: field_length_members[m.field_name] = m
:: field_length_indexes[m.field_name] = index
- packed.append(${m.oftype.gen_pack_expr('0')}) # placeholder for ${m.name} at index ${index}
-:: elif type(m) == PadMember:
+ packed.append(${gen_pack_expr(m.oftype, '0')}) # placeholder for ${m.name} at index ${index}
+:: elif type(m) == OFPadMember:
packed.append('\x00' * ${m.length})
:: else:
- packed.append(${m.oftype.gen_pack_expr('self.' + m.name)})
+ packed.append(${gen_pack_expr(m.oftype, 'self.' + m.name)})
:: if m.name in field_length_members:
:: field_length_member = field_length_members[m.name]
:: field_length_index = field_length_indexes[m.name]
- packed[${field_length_index}] = ${field_length_member.oftype.gen_pack_expr('len(packed[-1])')}
+ packed[${field_length_index}] = ${gen_pack_expr(field_length_member.oftype, 'len(packed[-1])')}
:: #endif
:: #endif
:: index += 1
:: #endfor
:: if length_member_index != None:
length = sum([len(x) for x in packed])
- packed[${length_member_index}] = ${length_member.oftype.gen_pack_expr('length')}
+ packed[${length_member_index}] = ${gen_pack_expr(length_member.oftype, 'length')}
:: #endif
:: if ofclass.name == 'of_match_v3':
packed.append('\x00' * ((length + 7)/8*8 - length))
diff --git a/py_gen/templates/_pretty_print.py b/py_gen/templates/_pretty_print.py
index 86cb237..7be7a14 100644
--- a/py_gen/templates/_pretty_print.py
+++ b/py_gen/templates/_pretty_print.py
@@ -29,8 +29,8 @@
with q.group():
with q.indent(2):
q.breakable()
-:: from py_gen.codegen import Member, LengthMember, TypeMember
-:: normal_members = [m for m in ofclass.members if type(m) == Member]
+:: from loxi_ir import *
+:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
:: first = True
:: for m in normal_members:
:: if not first:
@@ -44,15 +44,15 @@
q.text("%#x" % self.${m.name})
else:
q.text('None')
-:: elif m.oftype.base == 'of_mac_addr_t':
+:: elif m.oftype == 'of_mac_addr_t':
q.text(util.pretty_mac(self.${m.name}))
-:: elif m.oftype.base == 'uint32_t' and m.name.startswith("ipv4"):
+:: elif m.oftype == 'uint32_t' and m.name.startswith("ipv4"):
q.text(util.pretty_ipv4(self.${m.name}))
-:: elif m.oftype.base == 'of_wc_bmap_t' and version in [1,2]:
+:: elif m.oftype == 'of_wc_bmap_t' and version in [1,2]:
q.text(util.pretty_wildcards(self.${m.name}))
-:: elif m.oftype.base == 'of_port_no_t':
+:: elif m.oftype == 'of_port_no_t':
q.text(util.pretty_port(self.${m.name}))
-:: elif m.oftype.base.startswith("uint") and not m.oftype.is_array:
+:: elif m.oftype.startswith("uint"):
q.text("%#x" % self.${m.name})
:: else:
q.pp(self.${m.name})
diff --git a/py_gen/templates/_unpack.py b/py_gen/templates/_unpack.py
index 7f3ce2a..133e831 100644
--- a/py_gen/templates/_unpack.py
+++ b/py_gen/templates/_unpack.py
@@ -26,30 +26,31 @@
:: # under the EPL.
::
:: # TODO coalesce format strings
-:: from py_gen.codegen import Member, LengthMember, FieldLengthMember, TypeMember, PadMember
+:: from loxi_ir import *
+:: from py_gen.oftype import gen_unpack_expr
if type(buf) == loxi.generic_util.OFReader:
reader = buf
else:
reader = loxi.generic_util.OFReader(buf)
:: field_length_members = {}
:: for m in ofclass.members:
-:: if type(m) == PadMember:
+:: if type(m) == OFPadMember:
reader.skip(${m.length})
-:: elif type(m) == LengthMember:
- _${m.name} = ${m.oftype.gen_unpack_expr('reader')}
-:: elif type(m) == FieldLengthMember:
+:: elif type(m) == OFLengthMember:
+ _${m.name} = ${gen_unpack_expr(m.oftype, 'reader')}
+:: elif type(m) == OFFieldLengthMember:
:: field_length_members[m.field_name] = m
- _${m.name} = ${m.oftype.gen_unpack_expr('reader')}
-:: elif type(m) == TypeMember:
- _${m.name} = ${m.oftype.gen_unpack_expr('reader')}
+ _${m.name} = ${gen_unpack_expr(m.oftype, 'reader')}
+:: elif type(m) == OFTypeMember:
+ _${m.name} = ${gen_unpack_expr(m.oftype, 'reader')}
assert(_${m.name} == ${m.value})
-:: elif type(m) == Member:
+:: elif type(m) == OFDataMember:
:: if m.name in field_length_members:
:: reader_expr = 'reader.slice(_%s)' % field_length_members[m.name].name
:: else:
:: reader_expr = 'reader'
:: #endif
- obj.${m.name} = ${m.oftype.gen_unpack_expr(reader_expr)}
+ obj.${m.name} = ${gen_unpack_expr(m.oftype, reader_expr)}
:: #endif
:: #endfor
:: if ofclass.name == 'of_match_v3':
diff --git a/py_gen/templates/action.py b/py_gen/templates/action.py
index d8f7c80..dccb46e 100644
--- a/py_gen/templates/action.py
+++ b/py_gen/templates/action.py
@@ -27,6 +27,7 @@
::
:: import itertools
:: import of_g
+:: import py_gen.util as util
:: include('_copyright.py')
:: include('_autogen.py')
@@ -76,6 +77,7 @@
:: 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:
+:: k = util.constant_for_value(version, "ofp_action_type", k)
:: v = list(v)
:: if len(v) == 1:
${k} : ${v[0].pyname}.unpack,
@@ -85,7 +87,7 @@
:: #endfor
}
-:: experimenter_ofclasses = [x for x in ofclasses if x.type_members[0].value == 'const.OFPAT_VENDOR']
+:: experimenter_ofclasses = [x for x in ofclasses if x.type_members[0].value == 0xffff]
:: sort_key = lambda x: x.type_members[1].value
:: experimenter_ofclasses.sort(key=sort_key)
:: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
diff --git a/py_gen/templates/const.py b/py_gen/templates/const.py
index ef09585..4567d56 100644
--- a/py_gen/templates/const.py
+++ b/py_gen/templates/const.py
@@ -36,10 +36,9 @@
OFP_VERSION = ${version}
-:: for (group, idents) in sorted(groups.items()):
-:: idents.sort(key=lambda (ident, value): value)
-# Identifiers from group ${group}
-:: for (ident, value) in idents:
+:: for enum in sorted(enums, key=lambda enum: enum.name):
+# Identifiers from group ${enum.name}
+:: for (ident, value) in enum.values:
:: if version == 1 and ident.startswith('OFPP_'):
:: # HACK loxi converts these to 32-bit
${ident} = ${"%#x" % (value & 0xffff)}
@@ -48,9 +47,9 @@
:: #endif
:: #endfor
-:: if group not in blacklisted_map_groups:
-${group}_map = {
-:: for (ident, value) in idents:
+:: if enum.name not in blacklisted_map_groups:
+${enum.name}_map = {
+:: for (ident, value) in enum.values:
:: if ident in blacklisted_map_idents:
:: pass
:: elif version == 1 and ident.startswith('OFPP_'):
diff --git a/py_gen/templates/instruction.py b/py_gen/templates/instruction.py
index bfa0628..88b0f89 100644
--- a/py_gen/templates/instruction.py
+++ b/py_gen/templates/instruction.py
@@ -27,6 +27,7 @@
::
:: import itertools
:: import of_g
+:: import py_gen.util as util
:: include('_copyright.py')
:: include('_autogen.py')
@@ -58,6 +59,7 @@
:: 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:
+:: k = util.constant_for_value(version, "ofp_instruction_type", k)
:: v = list(v)
:: if len(v) == 1:
${k} : ${v[0].pyname}.unpack,
diff --git a/py_gen/templates/message.py b/py_gen/templates/message.py
index 8e2f861..8bf11f1 100644
--- a/py_gen/templates/message.py
+++ b/py_gen/templates/message.py
@@ -27,6 +27,8 @@
::
:: import itertools
:: import of_g
+:: import py_gen.util as util
+:: import py_gen.oftype
:: include('_copyright.py')
:: include('_autogen.py')
@@ -51,9 +53,9 @@
xid = None
:: for ofclass in ofclasses:
-:: from py_gen.codegen import Member, LengthMember, TypeMember
-:: normal_members = [m for m in ofclass.members if type(m) == Member]
-:: type_members = [m for m in ofclass.members if type(m) == TypeMember]
+:: from loxi_ir import *
+:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
+:: type_members = [m for m in ofclass.members if type(m) == OFTypeMember]
class ${ofclass.pyname}(Message):
:: for m in type_members:
${m.name} = ${m.value}
@@ -65,7 +67,7 @@
if ${m.name} != None:
self.${m.name} = ${m.name}
else:
- self.${m.name} = ${m.oftype.gen_init_expr()}
+ self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype)}
:: #endfor
def pack(self):
@@ -193,6 +195,7 @@
:: sort_key = lambda x: x.type_members[1].value
:: msgtype_groups = itertools.groupby(sorted(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,
@@ -256,7 +259,7 @@
# TODO OF 1.3 multipart messages
:: #endif
-:: experimenter_ofclasses = [x for x in ofclasses if x.type_members[1].value == 'const.OFPT_VENDOR']
+:: experimenter_ofclasses = [x for x in ofclasses if x.type_members[1].value == 4]
:: sort_key = lambda x: x.type_members[2].value
:: experimenter_ofclasses.sort(key=sort_key)
:: grouped = itertools.groupby(experimenter_ofclasses, sort_key)
diff --git a/py_gen/templates/meter_band.py b/py_gen/templates/meter_band.py
index ca111d5..eeb9ff2 100644
--- a/py_gen/templates/meter_band.py
+++ b/py_gen/templates/meter_band.py
@@ -27,6 +27,7 @@
::
:: import itertools
:: import of_g
+:: import py_gen.util as util
:: include('_copyright.py')
:: include('_autogen.py')
@@ -57,6 +58,7 @@
:: 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:
+:: k = util.constant_for_value(version, "ofp_meter_band_type", k)
:: v = list(v)
:: if len(v) == 1:
${k} : ${v[0].pyname}.unpack,
diff --git a/py_gen/templates/oxm.py b/py_gen/templates/oxm.py
index e11a0a4..d95dd8e 100644
--- a/py_gen/templates/oxm.py
+++ b/py_gen/templates/oxm.py
@@ -27,6 +27,7 @@
::
:: import itertools
:: import of_g
+:: import py_gen.oftype
:: include('_copyright.py')
:: include('_autogen.py')
@@ -52,9 +53,9 @@
pass
:: for ofclass in ofclasses:
-:: from py_gen.codegen import Member, LengthMember, TypeMember
-:: normal_members = [m for m in ofclass.members if type(m) == Member]
-:: type_members = [m for m in ofclass.members if type(m) == TypeMember]
+:: from loxi_ir import *
+:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
+:: type_members = [m for m in ofclass.members if type(m) == OFTypeMember]
class ${ofclass.pyname}(OXM):
:: for m in type_members:
${m.name} = ${m.value}
@@ -65,7 +66,7 @@
if ${m.name} != None:
self.${m.name} = ${m.name}
else:
- self.${m.name} = ${m.oftype.gen_init_expr()}
+ self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype)}
:: #endfor
def pack(self):
@@ -99,7 +100,7 @@
:: #endfor
parsers = {
-:: key = lambda x: int(x.type_members[0].value, 16)
+:: key = lambda x: x.type_members[0].value
:: for ofclass in sorted(ofclasses, key=key):
${key(ofclass)} : ${ofclass.pyname}.unpack,
:: #endfor
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index 397c184..c4de5a3 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -69,3 +69,76 @@
if v == v2:
return k
return v
+
+def pack_port_no(value):
+:: if version == 1:
+ return struct.pack("!H", value)
+:: else:
+ return struct.pack("!L", value)
+:: #endif
+
+def unpack_port_no(reader):
+:: if version == 1:
+ return reader.read("!H")[0]
+:: else:
+ return reader.read("!L")[0]
+:: #endif
+
+def pack_fm_cmd(value):
+:: if version == 1:
+ return struct.pack("!H", value)
+:: else:
+ return struct.pack("!B", value)
+:: #endif
+
+def unpack_fm_cmd(reader):
+:: if version == 1:
+ return reader.read("!H")[0]
+:: else:
+ return reader.read("!B")[0]
+:: #endif
+
+def init_wc_bmap():
+:: if version <= 2:
+ return const.OFPFW_ALL
+:: else:
+ return 0
+:: #endif
+
+def pack_wc_bmap(value):
+:: if version <= 2:
+ return struct.pack("!L", value)
+:: else:
+ return struct.pack("!Q", value)
+:: #endif
+
+def unpack_wc_bmap(reader):
+:: if version <= 2:
+ return reader.read("!L")[0]
+:: else:
+ return reader.read("!Q")[0]
+:: #endif
+
+def init_match_bmap():
+:: if version <= 2:
+ return const.OFPFW_ALL
+:: else:
+ return 0
+:: #endif
+
+def pack_match_bmap(value):
+:: if version <= 2:
+ return struct.pack("!L", value)
+:: else:
+ return struct.pack("!Q", value)
+:: #endif
+
+def unpack_match_bmap(reader):
+:: if version <= 2:
+ return reader.read("!L")[0]
+:: else:
+ return reader.read("!Q")[0]
+:: #endif
+
+def pack_list(values):
+ return "".join([x.pack() for x in values])
diff --git a/py_gen/util.py b/py_gen/util.py
index 30c5d50..f527964 100644
--- a/py_gen/util.py
+++ b/py_gen/util.py
@@ -31,7 +31,6 @@
import os
import of_g
-import loxi_front_end.type_maps as type_maps
import loxi_utils.loxi_utils as utils
templates_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates')
@@ -42,38 +41,10 @@
def render_static(out, name):
utils.render_static(out, name, [templates_dir])
-def lookup_unified_class(cls, version):
- unified_class = of_g.unified[cls][version]
- if "use_version" in unified_class: # deref version ref
- ref_version = unified_class["use_version"]
- unified_class = of_g.unified[cls][ref_version]
- return unified_class
-
-def primary_wire_type(cls, version):
- if cls in type_maps.stats_reply_list:
- return type_maps.type_val[("of_stats_reply", version)]
- elif cls in type_maps.stats_request_list:
- return type_maps.type_val[("of_stats_request", version)]
- elif cls in type_maps.flow_mod_list:
- return type_maps.type_val[("of_flow_mod", version)]
- elif (cls, version) in type_maps.type_val:
- return type_maps.type_val[(cls, version)]
- elif type_maps.message_is_extension(cls, version):
- return type_maps.type_val[("of_experimenter", version)]
- elif type_maps.action_is_extension(cls, version):
- return type_maps.type_val[("of_action_experimenter", version)]
- elif type_maps.action_id_is_extension(cls, version):
- return type_maps.type_val[("of_action_id_experimenter", version)]
- elif type_maps.instruction_is_extension(cls, version):
- return type_maps.type_val[("of_instruction_experimenter", version)]
- elif type_maps.queue_prop_is_extension(cls, version):
- return type_maps.type_val[("of_queue_prop_experimenter", version)]
- elif type_maps.table_feature_prop_is_extension(cls, version):
- return type_maps.type_val[("of_table_feature_prop_experimenter", version)]
- else:
- raise ValueError("No wiretype for %s in version %d" % (cls, version))
-
def constant_for_value(version, group, value):
- return (["const." + v["ofp_name"] for k, v in of_g.identifiers.items()
- if k in of_g.identifiers_by_group[group] and
- v["values_by_version"].get(version, None) == value] or [value])[0]
+ enums = of_g.ir[version].enums
+ enum = [x for x in enums if x.name == group][0]
+ for name, value2 in enum.values:
+ if value == value2:
+ return "const." + name
+ return repr(value)