Merge into master from pull request #147:
Make pyloxi inheritance and list handling generic (https://github.com/floodlight/loxigen/pull/147)
diff --git a/loxi_front_end/frontend.py b/loxi_front_end/frontend.py
index a84508a..545b5bc 100644
--- a/loxi_front_end/frontend.py
+++ b/loxi_front_end/frontend.py
@@ -54,6 +54,8 @@
elif m_ast[2] == 'actions_len':
# HACK only usage so far
return ir.OFFieldLengthMember(name=m_ast[2], oftype=get_type(m_ast[1], ctx), field_name='actions')
+ if m_ast[2] == 'version': # Should be moved to parser
+ return ir.OFVersionMember(name=m_ast[2], oftype=get_type(m_ast[1], ctx))
else:
return ir.OFDataMember(name=m_ast[2], oftype=get_type(m_ast[1], ctx))
elif m_ast[0] == 'discriminator':
diff --git a/loxi_front_end/frontend_ir.py b/loxi_front_end/frontend_ir.py
index a927f94..af3f223 100644
--- a/loxi_front_end/frontend_ir.py
+++ b/loxi_front_end/frontend_ir.py
@@ -38,6 +38,7 @@
'OFLengthMember',
'OFFieldLengthMember',
'OFPadMember',
+ 'OFVersionMember',
'OFEnum',
'OFEnumEntry'
]
@@ -128,6 +129,16 @@
OFPadMember = namedtuple('OFPadMember', ['length'])
"""
+Field with the version of an OpenFlow object
+
+@param name
+@param oftype C-like type string
+
+Example: hello.version
+"""
+OFVersionMember = namedtuple('OFVersionMember', ['name', 'oftype'])
+
+"""
An OpenFlow enumeration
All values are Python ints.
diff --git a/loxi_ir/ir.py b/loxi_ir/ir.py
index 8553f62..df1c77d 100644
--- a/loxi_ir/ir.py
+++ b/loxi_ir/ir.py
@@ -33,6 +33,7 @@
from collections import namedtuple, OrderedDict
from generic_utils import find, memoize, OrderedSet
from loxi_ir import ir_offset
+import loxi_front_end.frontend_ir as frontend_ir
logger = logging.getLogger(__name__)
@@ -174,6 +175,14 @@
else:
raise Exception("Not a fixed length class: {}".format(self.name))
+ @property
+ def has_internal_alignment(self):
+ return self.params.get('length_includes_align') == 'True'
+
+ @property
+ def has_external_alignment(self):
+ return self.params.get('length_includes_align') == 'False'
+
""" one class unified across openflow versions. Keeps around a map version->versioned_class """
class OFUnifiedClass(OFClass):
def __new__(cls, version_classes, *a, **kw):
@@ -356,11 +365,18 @@
return { name if name != "length" else "pad_length" : value for name, value in props.items() }
def build_member(of_class, fe_member, length_info):
- ir_class = globals()[type(fe_member).__name__]
- member = ir_class(offset = length_info.offset,
- base_length = length_info.base_length,
- is_fixed_length=length_info.is_fixed_length,
- **convert_member_properties(fe_member._asdict()))
+ if isinstance(fe_member, frontend_ir.OFVersionMember):
+ member = OFTypeMember(offset = length_info.offset,
+ base_length = length_info.base_length,
+ is_fixed_length=length_info.is_fixed_length,
+ value = version.wire_version,
+ **convert_member_properties(fe_member._asdict()))
+ else:
+ ir_class = globals()[type(fe_member).__name__]
+ member = ir_class(offset = length_info.offset,
+ base_length = length_info.base_length,
+ is_fixed_length=length_info.is_fixed_length,
+ **convert_member_properties(fe_member._asdict()))
member.of_class = of_class
return member
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 7cd068f..2c4a135 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -25,7 +25,7 @@
# EPL for the specific language governing permissions and limitations
# under the EPL.
-from collections import namedtuple
+from collections import defaultdict
import loxi_globals
import struct
import template_utils
@@ -34,113 +34,76 @@
import oftype
from loxi_ir import *
-ofclasses_by_version = {}
+modules_by_version = {}
-PyOFClass = namedtuple('PyOFClass', ['name', 'pyname', 'members', 'type_members',
- 'min_length', 'is_fixed_length',
- 'has_internal_alignment', 'has_external_alignment'])
+# Map from inheritance root to module name
+roots = {
+ 'of_header': 'message',
+ 'of_action': 'action',
+ 'of_oxm': 'oxm',
+ 'of_instruction': 'instruction',
+ 'of_meter_band': 'meter_band',
+}
-# 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):
- return cls[7:]
- elif utils.class_is_meter_band(cls):
- return cls[14:]
- elif utils.class_is_instruction(cls):
- return cls[15:]
- else:
- return cls[3:]
+# Return the module and class names for the generated Python class
+def generate_pyname(ofclass):
+ for root, module_name in roots.items():
+ if ofclass.name == root:
+ return module_name, module_name
+ elif ofclass.is_instanceof(root):
+ if root == 'of_header':
+ # The input files don't prefix message names
+ return module_name, ofclass.name[3:]
+ else:
+ return module_name, ofclass.name[len(root)+1:]
+ return 'common', ofclass.name[3:]
# Create intermediate representation, extended from the LOXI IR
-# HACK the oftype member attribute is replaced with an OFType instance
def build_ofclasses(version):
- ofclasses = []
+ modules = defaultdict(list)
for ofclass in loxi_globals.ir[version].classes:
- cls = ofclass.name
- if ofclass.virtual:
- continue
-
- members = []
- type_members = []
-
- for m in ofclass.members:
- if type(m) == OFTypeMember:
- members.append(m)
- type_members.append(members[-1])
- 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.wire_version,
- base_length=m.base_length,
- is_fixed_length=m.is_fixed_length,
- offset=m.offset))
- type_members.append(members[-1])
- else:
- members.append(m)
-
- ofclasses.append(
- PyOFClass(name=cls,
- pyname=generate_pyname(cls),
- members=members,
- type_members=type_members,
- min_length=ofclass.base_length,
- is_fixed_length=ofclass.is_fixed_length,
- has_internal_alignment=cls == 'of_action_set_field',
- has_external_alignment=cls == 'of_match_v3'))
- return ofclasses
+ module_name, ofclass.pyname = generate_pyname(ofclass)
+ modules[module_name].append(ofclass)
+ return modules
def generate_init(out, name, version):
util.render_template(out, 'init.py', version=version)
def generate_action(out, name, version):
- ofclasses = [x for x in ofclasses_by_version[version]
- if utils.class_is_action(x.name)]
- util.render_template(out, 'action.py', ofclasses=ofclasses, version=version)
+ util.render_template(out, 'module.py',
+ ofclasses=modules_by_version[version]['action'],
+ version=version)
def generate_oxm(out, name, version):
- ofclasses = [x for x in ofclasses_by_version[version]
- if utils.class_is_oxm(x.name)]
- util.render_template(out, 'oxm.py', ofclasses=ofclasses, version=version)
+ util.render_template(out, 'module.py',
+ ofclasses=modules_by_version[version]['oxm'],
+ version=version)
def generate_common(out, name, version):
- ofclasses = [x for x in ofclasses_by_version[version]
- if not utils.class_is_message(x.name)
- and not utils.class_is_action(x.name)
- and not utils.class_is_instruction(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)
+ util.render_template(out, 'module.py',
+ ofclasses=modules_by_version[version]['common'],
+ version=version,
+ extra_template='_common_extra.py')
def generate_const(out, name, version):
util.render_template(out, 'const.py', version=version,
enums=loxi_globals.ir[version].enums)
def generate_instruction(out, name, version):
- ofclasses = [x for x in ofclasses_by_version[version]
- if utils.class_is_instruction(x.name)]
- util.render_template(out, 'instruction.py', ofclasses=ofclasses, version=version)
+ util.render_template(out, 'module.py',
+ ofclasses=modules_by_version[version]['instruction'],
+ version=version)
def generate_message(out, name, version):
- ofclasses = [x for x in ofclasses_by_version[version]
- if utils.class_is_message(x.name)]
- util.render_template(out, 'message.py', ofclasses=ofclasses, version=version)
+ util.render_template(out, 'module.py',
+ ofclasses=modules_by_version[version]['message'],
+ version=version,
+ extra_template='_message_extra.py')
def generate_meter_band(out, name, version):
- ofclasses = [x for x in ofclasses_by_version[version]
- if utils.class_is_meter_band(x.name)]
- util.render_template(out, 'meter_band.py', ofclasses=ofclasses, version=version)
+ util.render_template(out, 'module.py',
+ ofclasses=modules_by_version[version]['meter_band'],
+ version=version)
def generate_pp(out, name, version):
util.render_template(out, 'pp.py')
@@ -150,4 +113,4 @@
def init():
for version in loxi_globals.OFVersions.target_versions:
- ofclasses_by_version[version] = build_ofclasses(version)
+ modules_by_version[version] = build_ofclasses(version)
diff --git a/py_gen/oftype.py b/py_gen/oftype.py
index 605d5be..40bcb40 100644
--- a/py_gen/oftype.py
+++ b/py_gen/oftype.py
@@ -28,6 +28,8 @@
from collections import namedtuple
import loxi_utils.loxi_utils as loxi_utils
+import py_gen.codegen
+import loxi_globals
OFTypeData = namedtuple("OFTypeData", ["init", "pack", "unpack"])
@@ -105,34 +107,10 @@
pack='util.pack_bitmap_128(%s)',
unpack="util.unpack_bitmap_128(%s)"),
- # 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))'),
-
'of_oxm_t': OFTypeData(
init='None',
pack='%s.pack()',
- unpack='oxm.unpack(%s)'),
-
- # 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),
+ unpack='oxm.oxm.unpack(%s)'),
}
## Fixed length strings
@@ -167,50 +145,11 @@
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',
- 'list(of_bsn_lacp_stats_entry_t)': 'common.bsn_lacp_stats_entry.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)
+# Special case for lists of hello_elem, which must ignore unknown types
+type_data_map['list(of_hello_elem_t)'] = OFTypeData(
+ init='[]',
+ pack='loxi.generic_util.pack_list(%s)',
+ unpack='util.unpack_list_hello_elem(%s)')
## Public interface
@@ -222,6 +161,8 @@
type_data = lookup_type_data(oftype, version)
if type_data and type_data.init:
return type_data.init
+ elif oftype_is_list(oftype):
+ return "[]"
else:
return "loxi.unimplemented('init %s')" % oftype
@@ -233,6 +174,8 @@
type_data = lookup_type_data(oftype, version)
if type_data and type_data.pack:
return type_data.pack % value_expr
+ elif oftype_is_list(oftype):
+ return "loxi.generic_util.pack_list(%s)" % value_expr
else:
return "loxi.unimplemented('pack %s')" % oftype
@@ -244,5 +187,19 @@
type_data = lookup_type_data(oftype, version)
if type_data and type_data.unpack:
return type_data.unpack % reader_expr
+ elif oftype_is_list(oftype):
+ ofproto = loxi_globals.ir[version]
+ ofclass = ofproto.class_by_name(oftype_list_elem(oftype))
+ module_name, class_name = py_gen.codegen.generate_pyname(ofclass)
+ return 'loxi.generic_util.unpack_list(%s, %s.%s.unpack)' % \
+ (reader_expr, module_name, class_name)
else:
return "loxi.unimplemented('unpack %s')" % oftype
+
+def oftype_is_list(oftype):
+ return (oftype.find("list(") == 0)
+
+# Converts "list(of_flow_stats_entry_t)" to "of_flow_stats_entry"
+def oftype_list_elem(oftype):
+ assert oftype.find("list(") == 0
+ return oftype[5:-3]
diff --git a/py_gen/templates/_common_extra.py b/py_gen/templates/_common_extra.py
new file mode 100644
index 0000000..8639dc4
--- /dev/null
+++ b/py_gen/templates/_common_extra.py
@@ -0,0 +1,38 @@
+:: # 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.
+::
+:: from loxi_globals import OFVersions
+:: if version == OFVersions.VERSION_1_0:
+match = match_v1
+:: elif version == OFVersions.VERSION_1_1:
+match = match_v2
+:: elif version == OFVersions.VERSION_1_2:
+match = match_v3
+:: elif version == OFVersions.VERSION_1_3:
+:: # HACK
+match = match_v3
+:: #endif
diff --git a/py_gen/templates/_message_extra.py b/py_gen/templates/_message_extra.py
new file mode 100644
index 0000000..9fc2f5e
--- /dev/null
+++ b/py_gen/templates/_message_extra.py
@@ -0,0 +1,39 @@
+:: # 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.
+::
+def parse_header(buf):
+ if len(buf) < 8:
+ raise loxi.ProtocolError("too short to be an OpenFlow message")
+ return struct.unpack_from("!BBHL", buf)
+
+def parse_message(buf):
+ msg_ver, msg_type, msg_len, msg_xid = parse_header(buf)
+ if msg_ver != const.OFP_VERSION and msg_type != const.OFPT_HELLO:
+ 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")
+ return message.unpack(loxi.generic_util.OFReader(buf))
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
index 695cce6..f902ae8 100644
--- a/py_gen/templates/_ofclass.py
+++ b/py_gen/templates/_ofclass.py
@@ -1,8 +1,10 @@
+:: superclass_pyname = ofclass.superclass.pyname if ofclass.superclass else "loxi.OFObject"
:: from loxi_ir import *
:: import py_gen.oftype
+:: type_members = [m for m in ofclass.members if type(m) == OFTypeMember]
:: normal_members = [m for m in ofclass.members if type(m) == OFDataMember]
-class ${ofclass.pyname}(${superclass}):
-:: for m in ofclass.type_members:
+class ${ofclass.pyname}(${superclass_pyname}):
+:: for m in type_members:
${m.name} = ${m.value}
:: #endfor
@@ -11,7 +13,12 @@
if ${m.name} != None:
self.${m.name} = ${m.name}
else:
+:: if m.name == 'xid':
+:: # HACK for message xid
+ self.${m.name} = None
+:: else:
self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype, version=version)}
+:: #endif
:: #endfor
return
@@ -21,7 +28,7 @@
return ''.join(packed)
@staticmethod
- def unpack(buf):
+ def unpack(reader):
obj = ${ofclass.pyname}()
:: include("_unpack.py", ofclass=ofclass)
return obj
@@ -33,12 +40,12 @@
:: #endfor
return True
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def show(self):
- import loxi.pp
- return loxi.pp.pp(self)
-
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/_unpack.py b/py_gen/templates/_unpack.py
index 81efb21..81f74c7 100644
--- a/py_gen/templates/_unpack.py
+++ b/py_gen/templates/_unpack.py
@@ -28,16 +28,14 @@
:: # TODO coalesce format strings
:: 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) == OFPadMember:
reader.skip(${m.length})
:: elif type(m) == OFLengthMember:
_${m.name} = ${gen_unpack_expr(m.oftype, 'reader', version=version)}
+ orig_reader = reader
+ reader = orig_reader.slice(_${m.name} - (${m.offset} + ${m.length}))
:: elif type(m) == OFFieldLengthMember:
:: field_length_members[m.field_name] = m
_${m.name} = ${gen_unpack_expr(m.oftype, 'reader', version=version)}
@@ -53,6 +51,6 @@
obj.${m.name} = ${gen_unpack_expr(m.oftype, reader_expr, version=version)}
:: #endif
:: #endfor
-:: if ofclass.has_external_alignment or ofclass.has_internal_alignment:
- reader.skip_align()
+:: if ofclass.has_external_alignment:
+ orig_reader.skip_align()
:: #endif
diff --git a/py_gen/templates/_virtual_ofclass.py b/py_gen/templates/_virtual_ofclass.py
new file mode 100644
index 0000000..1e5dcc2
--- /dev/null
+++ b/py_gen/templates/_virtual_ofclass.py
@@ -0,0 +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}):
+ 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
deleted file mode 100644
index 92c4e26..0000000
--- a/py_gen/templates/action.py
+++ /dev/null
@@ -1,100 +0,0 @@
-:: # 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
-:: from loxi_globals import OFVersions
-:: import py_gen.util as util
-:: include('_copyright.py')
-
-:: include('_autogen.py')
-
-import struct
-import const
-import util
-import loxi.generic_util
-import loxi
-:: if version >= OFVersions.VERSION_1_2:
-import oxm # for unpack
-:: #endif
-
-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 loxi.generic_util.unpack_list_tlv16(reader, deserializer)
-
-class Action(object):
- type = None # override in subclass
- pass
-
-:: for ofclass in ofclasses:
-:: include('_ofclass.py', ofclass=ofclass, superclass="Action")
-
-:: #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 = {
-:: 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,
-:: else:
- ${k} : parse_${k[12:].lower()},
-:: #endif
-:: #endfor
-}
-
-:: 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)
-experimenter_parsers = {
-:: for (experimenter, v) in grouped:
- ${experimenter} : {
-:: for ofclass in v:
- ${ofclass.type_members[2].value}: ${ofclass.pyname}.unpack,
-:: #endfor
- },
-:: #endfor
-}
diff --git a/py_gen/templates/common.py b/py_gen/templates/common.py
deleted file mode 100644
index 76ae631..0000000
--- a/py_gen/templates/common.py
+++ /dev/null
@@ -1,106 +0,0 @@
-:: # 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.
-::
-:: include('_copyright.py')
-:: from loxi_globals import OFVersions
-:: include('_autogen.py')
-
-import sys
-import struct
-import action
-:: if version >= OFVersions.VERSION_1_1:
-import instruction # for unpack_list
-:: #endif
-:: if version >= OFVersions.VERSION_1_3:
-import meter_band # for unpack_list
-:: #endif
-import const
-import util
-import loxi.generic_util
-
-:: if version >= OFVersions.VERSION_1_2:
-import oxm
-:: #endif
-
-# HACK make this module visible as 'common' to simplify code generation
-common = sys.modules[__name__]
-
-def unpack_list_flow_stats_entry(reader):
- return loxi.generic_util.unpack_list_lv16(reader, flow_stats_entry.unpack)
-
-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 loxi.generic_util.unpack_list_tlv16(reader, deserializer)
-
-def unpack_list_packet_queue(reader):
- def wrapper(reader):
- length, = reader.peek('!4xH')
- return packet_queue.unpack(reader.slice(length))
- return loxi.generic_util.unpack_list(reader, wrapper)
-
-def unpack_list_hello_elem(reader):
- def deserializer(reader, typ):
- if typ == const.OFPHET_VERSIONBITMAP:
- return hello_elem_versionbitmap.unpack(reader)
- else:
- return None
- return [x for x in loxi.generic_util.unpack_list_tlv16(reader, deserializer) if x != None]
-
-def unpack_list_bucket(reader):
- return loxi.generic_util.unpack_list_lv16(reader, bucket.unpack)
-
-def unpack_list_group_desc_stats_entry(reader):
- return loxi.generic_util.unpack_list_lv16(reader, group_desc_stats_entry.unpack)
-
-def unpack_list_group_stats_entry(reader):
- return loxi.generic_util.unpack_list_lv16(reader, group_stats_entry.unpack)
-
-def unpack_list_meter_stats(reader):
- def wrapper(reader):
- length, = reader.peek('!4xH')
- return meter_stats.unpack(reader.slice(length))
- return loxi.generic_util.unpack_list(reader, wrapper)
-
-:: for ofclass in ofclasses:
-:: include('_ofclass.py', ofclass=ofclass, superclass="object")
-
-:: #endfor
-
-:: if version == OFVersions.VERSION_1_0:
-match = match_v1
-:: elif version == OFVersions.VERSION_1_1:
-match = match_v2
-:: elif version == OFVersions.VERSION_1_2:
-match = match_v3
-:: elif version == OFVersions.VERSION_1_3:
-:: # HACK
-match = match_v3
-:: #endif
diff --git a/py_gen/templates/generic_util.py b/py_gen/templates/generic_util.py
index 28d3b5f..039d82a 100644
--- a/py_gen/templates/generic_util.py
+++ b/py_gen/templates/generic_util.py
@@ -35,6 +35,9 @@
import loxi
import struct
+def pack_list(values):
+ return "".join([x.pack() for x in values])
+
def unpack_list(reader, deserializer):
"""
The deserializer function should take an OFReader and return the new object.
@@ -44,25 +47,6 @@
entries.append(deserializer(reader))
return entries
-def unpack_list_lv16(reader, deserializer):
- """
- The deserializer function should take an OFReader and return the new object.
- """
- def wrapper(reader):
- length, = reader.peek('!H')
- return deserializer(reader.slice(length))
- return unpack_list(reader, wrapper)
-
-def unpack_list_tlv16(reader, deserializer):
- """
- The deserializer function should take an OFReader and an integer type
- and return the new object.
- """
- def wrapper(reader):
- typ, length, = reader.peek('!HH')
- return deserializer(reader.slice(length), typ)
- return unpack_list(reader, wrapper)
-
def pad_to(alignment, length):
"""
Return a string of zero bytes that will pad a string of length 'length' to
@@ -79,50 +63,60 @@
known field lengths. This class supports efficiently reading
fields sequentially and is intended to be used recursively by the
parsers of child objects which will implicitly update the offset.
+
+ buf: buffer object
+ start: initial position in the buffer
+ length: number of bytes after start
+ offset: distance from start
"""
- def __init__(self, buf):
+ def __init__(self, buf, start=0, length=None):
self.buf = buf
+ self.start = start
+ if length is None:
+ self.length = len(buf) - start
+ else:
+ self.length = length
self.offset = 0
def read(self, fmt):
st = struct.Struct(fmt)
- if self.offset + st.size > len(self.buf):
+ if self.offset + st.size > self.length:
raise loxi.ProtocolError("Buffer too short")
- result = st.unpack_from(self.buf, self.offset)
+ result = st.unpack_from(self.buf, self.start+self.offset)
self.offset += st.size
return result
def read_all(self):
- buf = buffer(self.buf, self.offset)
- self.offset += len(buf)
- return str(buf)
+ s = self.buf[(self.start+self.offset):(self.start+self.length)]
+ assert(len(s) == self.length - self.offset)
+ self.offset = self.length
+ return s
- 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 > self.length:
raise loxi.ProtocolError("Buffer too short")
- result = st.unpack_from(self.buf, self.offset)
+ result = st.unpack_from(self.buf, self.start + self.offset + offset)
return result
def skip(self, length):
- if self.offset + length > len(self.buf):
+ if self.offset + length > self.length:
raise loxi.ProtocolError("Buffer too short")
self.offset += length
def skip_align(self):
- new_offset = (self.offset + 7) / 8 * 8
- if new_offset > len(self.buf):
+ new_offset = ((self.start + self.offset + 7) / 8 * 8) - self.start
+ if new_offset > self.length:
raise loxi.ProtocolError("Buffer too short")
self.offset = new_offset
def is_empty(self):
- return self.offset == len(self.buf)
+ return self.offset == self.length
- # Used when parsing variable length objects which have external length
- # fields (e.g. the actions list in an OF 1.0 packet-out message).
+ # Used when parsing objects that have their own length fields
def slice(self, length):
- if self.offset + length > len(self.buf):
+ if self.offset + length > self.length:
raise loxi.ProtocolError("Buffer too short")
- buf = OFReader(buffer(self.buf, self.offset, length))
+ reader = OFReader(self.buf, self.start + self.offset, length)
self.offset += length
- return buf
+ return reader
diff --git a/py_gen/templates/instruction.py b/py_gen/templates/instruction.py
deleted file mode 100644
index 2817625..0000000
--- a/py_gen/templates/instruction.py
+++ /dev/null
@@ -1,95 +0,0 @@
-:: # 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 py_gen.util as util
-:: include('_copyright.py')
-
-:: include('_autogen.py')
-
-import struct
-import action
-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 instruction type %d" % typ)
- return parser(reader)
- return loxi.generic_util.unpack_list_tlv16(reader, deserializer)
-
-class Instruction(object):
- type = None # override in subclass
- pass
-
-:: for ofclass in ofclasses:
-:: include('_ofclass.py', ofclass=ofclass, superclass="Instruction")
-
-:: #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 = {
-:: 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 and k != 'const.OFPIT_EXPERIMENTER':
- ${k} : ${v[0].pyname}.unpack,
-:: else:
- ${k} : parse_${k[12:].lower()},
-:: #endif
-:: #endfor
-}
-
-:: 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)
-experimenter_parsers = {
-:: for (experimenter, v) in grouped:
- ${experimenter} : {
-:: for ofclass in v:
- ${ofclass.type_members[2].value}: ${ofclass.pyname}.unpack,
-:: #endfor
- },
-:: #endfor
-}
diff --git a/py_gen/templates/message.py b/py_gen/templates/message.py
deleted file mode 100644
index 39e9722..0000000
--- a/py_gen/templates/message.py
+++ /dev/null
@@ -1,351 +0,0 @@
-:: # 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
-:: from loxi_globals import OFVersions
-:: import loxi_globals
-:: import py_gen.util as util
-:: import py_gen.oftype
-:: include('_copyright.py')
-
-:: include('_autogen.py')
-
-import struct
-import loxi
-import const
-import common
-import action # for unpack_list
-:: if version >= OFVersions.VERSION_1_1:
-import instruction # for unpack_list
-:: #endif
-:: if version >= OFVersions.VERSION_1_3:
-import meter_band # for unpack_list
-:: #endif
-import util
-import loxi.generic_util
-
-class Message(object):
- version = const.OFP_VERSION
- type = None # override in subclass
- xid = None
-
-:: for ofclass in ofclasses:
-:: 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}
-:: #endfor
-
- def __init__(self, ${', '.join(["%s=None" % m.name for m in normal_members])}):
- self.xid = xid
-:: for m in [x for x in normal_members if x.name != 'xid']:
- if ${m.name} != None:
- self.${m.name} = ${m.name}
- else:
- self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype, version=version)}
-:: #endfor
-
- def pack(self):
- packed = []
-:: include('_pack.py', ofclass=ofclass, version=version)
- return ''.join(packed)
-
- @staticmethod
- def unpack(buf):
- if len(buf) < 8: raise loxi.ProtocolError("buffer too short to contain an OpenFlow message")
- obj = ${ofclass.pyname}()
-:: include('_unpack.py', ofclass=ofclass, version=version)
- return obj
-
- def __eq__(self, other):
- if type(self) != type(other): return False
- if self.version != other.version: return False
- if self.type != other.type: return False
-:: for m in normal_members:
- if self.${m.name} != other.${m.name}: return False
-:: #endfor
- return True
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def __str__(self):
- return self.show()
-
- def show(self):
- import loxi.pp
- return loxi.pp.pp(self)
-
- def pretty_print(self, q):
-:: include('_pretty_print.py', ofclass=ofclass)
-
-:: #endfor
-
-def parse_header(buf):
- if len(buf) < 8:
- raise loxi.ProtocolError("too short to be an OpenFlow message")
- return struct.unpack_from("!BBHL", buf)
-
-def parse_message(buf):
- msg_ver, msg_type, msg_len, msg_xid = parse_header(buf)
- if msg_ver != const.OFP_VERSION and msg_type != const.OFPT_HELLO:
- 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 = {
-:: 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,
-:: 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 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)
-experimenter_parsers = {
-:: for (experimenter, v) in grouped:
- ${experimenter} : {
-:: for ofclass in v:
- ${ofclass.type_members[3].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
- },
-}
diff --git a/py_gen/templates/meter_band.py b/py_gen/templates/module.py
similarity index 64%
rename from py_gen/templates/meter_band.py
rename to py_gen/templates/module.py
index 37cc6d9..252bf0d 100644
--- a/py_gen/templates/meter_band.py
+++ b/py_gen/templates/module.py
@@ -25,44 +25,38 @@
:: # EPL for the specific language governing permissions and limitations
:: # under the EPL.
::
-:: import itertools
-:: import py_gen.util as util
+:: from loxi_globals import OFVersions
+:: import py_gen.oftype
:: include('_copyright.py')
:: include('_autogen.py')
import struct
+import loxi
import const
+import common
+import action
+:: if version >= OFVersions.VERSION_1_1:
+import instruction
+:: #endif
+:: if version >= OFVersions.VERSION_1_2:
+import oxm
+:: #endif
+:: if version >= OFVersions.VERSION_1_3:
+import meter_band
+:: #endif
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:
-:: k = util.constant_for_value(version, "ofp_meter_band_type", k)
-:: v = list(v)
-:: if len(v) == 1:
- ${k} : ${v[0].pyname}.unpack,
+:: if ofclass.virtual:
+:: include('_virtual_ofclass.py', ofclass=ofclass)
:: else:
- ${k} : parse_${k[12:].lower()},
+:: include('_ofclass.py', ofclass=ofclass)
:: #endif
+
:: #endfor
-}
+
+:: if 'extra_template' in locals():
+:: include(extra_template)
+:: #endif
diff --git a/py_gen/templates/oxm.py b/py_gen/templates/oxm.py
deleted file mode 100644
index ecb52ac..0000000
--- a/py_gen/templates/oxm.py
+++ /dev/null
@@ -1,107 +0,0 @@
-:: # 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 py_gen.oftype
-:: include('_copyright.py')
-
-:: include('_autogen.py')
-
-import struct
-import const
-import util
-import loxi.generic_util
-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))
-
-def unpack_list(reader):
- return loxi.generic_util.unpack_list(reader, unpack)
-
-class OXM(object):
- type_len = None # override in subclass
- pass
-
-:: for ofclass in ofclasses:
-:: 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}
-:: #endfor
-
- def __init__(self, ${', '.join(["%s=None" % m.name for m in normal_members])}):
-:: for m in normal_members:
- if ${m.name} != None:
- self.${m.name} = ${m.name}
- else:
- self.${m.name} = ${py_gen.oftype.gen_init_expr(m.oftype, version=version)}
-:: #endfor
-
- def pack(self):
- packed = []
-:: include("_pack.py", ofclass=ofclass, version=version)
- return ''.join(packed)
-
- @staticmethod
- def unpack(buf):
- obj = ${ofclass.pyname}()
-:: include("_unpack.py", ofclass=ofclass, version=version)
- return obj
-
- def __eq__(self, other):
- if type(self) != type(other): return False
-:: for m in normal_members:
- if self.${m.name} != other.${m.name}: return False
-:: #endfor
- return True
-
- def __ne__(self, other):
- return not self.__eq__(other)
-
- def show(self):
- import loxi.pp
- return loxi.pp.pp(self)
-
- def pretty_print(self, q):
-:: include('_pretty_print.py', ofclass=ofclass)
-
-:: #endfor
-
-parsers = {
-:: 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/toplevel_init.py b/py_gen/templates/toplevel_init.py
index b170bd8..e5493a5 100644
--- a/py_gen/templates/toplevel_init.py
+++ b/py_gen/templates/toplevel_init.py
@@ -68,3 +68,17 @@
def unimplemented(msg):
raise Unimplemented(msg)
+
+class OFObject(object):
+ """
+ Superclass of all OpenFlow classes
+ """
+ def __init__(self, *args):
+ raise NotImplementedError("cannot instantiate abstract class")
+
+ def __ne__(self, other):
+ return not self.__eq__(other)
+
+ def show(self):
+ import loxi.pp
+ return loxi.pp.pp(self)
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index 50a64db..8ad246a 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -29,9 +29,20 @@
:: from loxi_globals import OFVersions
:: include('_autogen.py')
+import struct
import loxi
import const
-import struct
+import common
+import action
+:: if version >= OFVersions.VERSION_1_1:
+import instruction
+:: #endif
+:: if version >= OFVersions.VERSION_1_2:
+import oxm
+:: #endif
+:: if version >= OFVersions.VERSION_1_3:
+import meter_band
+:: #endif
def pretty_mac(mac):
return ':'.join(["%02x" % x for x in mac])
@@ -140,9 +151,6 @@
return reader.read("!Q")[0]
:: #endif
-def pack_list(values):
- return "".join([x.pack() for x in values])
-
MASK64 = (1 << 64) - 1
def pack_bitmap_128(value):
@@ -162,3 +170,13 @@
i += 1
x >>= 1
return value
+
+def unpack_list_hello_elem(reader):
+ def deserializer(reader):
+ typ, length, = reader.peek('!HH')
+ reader = reader.slice(length)
+ try:
+ return common.hello_elem.unpack(reader)
+ except loxi.ProtocolError:
+ return None
+ return [x for x in loxi.generic_util.unpack_list(reader, deserializer) if x != None]
diff --git a/py_gen/tests/generic_util.py b/py_gen/tests/generic_util.py
index da045b9..8b2f59f 100644
--- a/py_gen/tests/generic_util.py
+++ b/py_gen/tests/generic_util.py
@@ -43,15 +43,6 @@
a = loxi.generic_util.unpack_list(reader, deserializer)
self.assertEquals(['\x04abc', '\x03de', '\x02f', '\x01'], a)
-class TestUnpackListLV16(unittest.TestCase):
- def test_simple(self):
- def deserializer(reader):
- reader.skip(2)
- return reader.read_all()
- reader = loxi.generic_util.OFReader("\x00\x05abc\x00\x04de\x00\x03f\x00\x02")
- a = loxi.generic_util.unpack_list_lv16(reader, deserializer)
- self.assertEquals(['abc', 'de', 'f', ''], a)
-
class TestOFReader(unittest.TestCase):
def test_empty(self):
reader = OFReader("")
@@ -116,5 +107,18 @@
self.assertEquals(reader.slice(2).read_all(), "fg")
self.assertEquals(reader.is_empty(), True)
+ def test_skip_align(self):
+ reader = OFReader("abcd" + "efgh" + "ijkl" + "mnop" + "qr")
+ reader.skip_align()
+ self.assertEquals(reader.peek('2s')[0], 'ab')
+ self.assertEquals(reader.read('2s')[0], "ab")
+ reader.skip_align()
+ self.assertEquals(reader.peek('2s')[0], 'ij')
+ self.assertEquals(reader.read('2s')[0], 'ij')
+ child = reader.slice(8)
+ self.assertEquals(child.peek('2s')[0], 'kl')
+ child.skip_align()
+ self.assertEquals(child.peek('2s')[0], 'qr')
+
if __name__ == '__main__':
unittest.main()
diff --git a/py_gen/tests/of10.py b/py_gen/tests/of10.py
index be93239..250000f 100644
--- a/py_gen/tests/of10.py
+++ b/py_gen/tests/of10.py
@@ -30,6 +30,7 @@
from testutil import add_datafiles_tests
try:
+ import loxi
import loxi.of10 as ofp
from loxi.generic_util import OFReader
except ImportError:
@@ -87,34 +88,34 @@
add(ofp.action.bsn_set_tunnel_dst(dst=0x12345678))
add(ofp.action.nicira_dec_ttl())
- actions = ofp.action.unpack_list(OFReader(''.join(bufs)))
+ actions = loxi.generic_util.unpack_list(OFReader(''.join(bufs)), ofp.action.action.unpack)
self.assertEquals(actions, expected)
def test_empty_list(self):
- self.assertEquals(ofp.action.unpack_list(OFReader('')), [])
+ self.assertEquals(loxi.generic_util.unpack_list(OFReader(''), ofp.action.action.unpack), [])
def test_invalid_list_length(self):
buf = '\x00' * 9
with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
- ofp.action.unpack_list(OFReader(buf))
+ loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
def test_invalid_action_length(self):
buf = '\x00' * 8
with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
- ofp.action.unpack_list(OFReader(buf))
+ loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
buf = '\x00\x00\x00\x04'
with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
- ofp.action.unpack_list(OFReader(buf))
+ loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
buf = '\x00\x00\x00\x10\x00\x00\x00\x00'
with self.assertRaisesRegexp(ofp.ProtocolError, 'Buffer too short'):
- ofp.action.unpack_list(OFReader(buf))
+ loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
def test_invalid_action_type(self):
buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
- with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action type'):
- ofp.action.unpack_list(OFReader(buf))
+ with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action subtype'):
+ loxi.generic_util.unpack_list(OFReader(buf), ofp.action.action.unpack)
class TestConstants(unittest.TestCase):
def test_ports(self):
@@ -129,7 +130,7 @@
self.assertEquals(match.wildcards, ofp.OFPFW_ALL)
self.assertEquals(match.tcp_src, 0)
buf = match.pack()
- match2 = ofp.match.unpack(buf)
+ match2 = ofp.match.unpack(OFReader(buf))
self.assertEquals(match, match2)
class TestMessages(unittest.TestCase):
@@ -152,8 +153,8 @@
def test_echo_request_invalid_length(self):
buf = "\x01\x02\x00\x07\x12\x34\x56"
- with self.assertRaisesRegexp(ofp.ProtocolError, "buffer too short"):
- ofp.message.echo_request.unpack(buf)
+ with self.assertRaisesRegexp(ofp.ProtocolError, "Buffer too short"):
+ ofp.message.echo_request.unpack(OFReader(buf))
def test_echo_request_equality(self):
msg = ofp.message.echo_request(xid=0x12345678, data="abc")
@@ -195,11 +196,11 @@
msg = ofp.message.parse_message(buf)
assert(msg.xid == 0x12345678)
- # Get a list of all message classes
+ # Get a list of all concrete message classes
test_klasses = [x for x in ofp.message.__dict__.values()
if type(x) == type
- and issubclass(x, ofp.message.Message)
- and x != ofp.message.Message]
+ and issubclass(x, ofp.message.message)
+ and hasattr(x, 'pack')]
for klass in test_klasses:
self.assertIsInstance(ofp.message.parse_message(klass(xid=1).pack()), klass)
@@ -228,7 +229,9 @@
mods = [ofp.action,ofp.message,ofp.common]
self.klasses = [klass for mod in mods
for klass in mod.__dict__.values()
- if hasattr(klass, 'show')]
+ if isinstance(klass, type) and
+ issubclass(klass, loxi.OFObject) and
+ hasattr(klass, 'pack')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
@@ -238,7 +241,7 @@
obj = klass()
if hasattr(obj, "xid"): obj.xid = 42
buf = obj.pack()
- obj2 = klass.unpack(buf)
+ obj2 = klass.unpack(OFReader(buf))
self.assertEquals(obj, obj2)
if klass in expected_failures:
self.assertRaises(Exception, fn)
@@ -248,7 +251,7 @@
def test_parse_message(self):
expected_failures = []
for klass in self.klasses:
- if not issubclass(klass, ofp.message.Message):
+ if not issubclass(klass, ofp.message.message):
continue
def fn():
obj = klass(xid=42)
diff --git a/py_gen/tests/of11.py b/py_gen/tests/of11.py
index 07a5437..d620509 100644
--- a/py_gen/tests/of11.py
+++ b/py_gen/tests/of11.py
@@ -28,7 +28,9 @@
import unittest
try:
+ import loxi
import loxi.of11 as ofp
+ from loxi.generic_util import OFReader
except ImportError:
exit("loxi package not found. Try setting PYTHONPATH.")
@@ -65,7 +67,9 @@
mods = [ofp.action,ofp.message,ofp.common]
self.klasses = [klass for mod in mods
for klass in mod.__dict__.values()
- if hasattr(klass, 'show')]
+ if isinstance(klass, type) and
+ issubclass(klass, loxi.OFObject) and
+ hasattr(klass, 'pack')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
@@ -75,7 +79,7 @@
obj = klass()
if hasattr(obj, "xid"): obj.xid = 42
buf = obj.pack()
- obj2 = klass.unpack(buf)
+ obj2 = klass.unpack(OFReader(buf))
self.assertEquals(obj, obj2)
if klass in expected_failures:
self.assertRaises(Exception, fn)
@@ -85,7 +89,7 @@
def test_parse_message(self):
expected_failures = []
for klass in self.klasses:
- if not issubclass(klass, ofp.message.Message):
+ if not issubclass(klass, ofp.message.message):
continue
def fn():
obj = klass(xid=42)
diff --git a/py_gen/tests/of12.py b/py_gen/tests/of12.py
index 98d999b..4774672 100644
--- a/py_gen/tests/of12.py
+++ b/py_gen/tests/of12.py
@@ -29,7 +29,9 @@
from testutil import add_datafiles_tests
try:
+ import loxi
import loxi.of12 as ofp
+ from loxi.generic_util import OFReader
except ImportError:
exit("loxi package not found. Try setting PYTHONPATH.")
@@ -74,7 +76,9 @@
mods = [ofp.action,ofp.message,ofp.common,ofp.oxm]
self.klasses = [klass for mod in mods
for klass in mod.__dict__.values()
- if hasattr(klass, 'show')]
+ if isinstance(klass, type) and
+ issubclass(klass, loxi.OFObject) and
+ hasattr(klass, 'pack')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
@@ -86,7 +90,7 @@
obj = klass()
if hasattr(obj, "xid"): obj.xid = 42
buf = obj.pack()
- obj2 = klass.unpack(buf)
+ obj2 = klass.unpack(OFReader(buf))
self.assertEquals(obj, obj2)
if klass in expected_failures:
self.assertRaises(Exception, fn)
@@ -96,7 +100,7 @@
def test_parse_message(self):
expected_failures = []
for klass in self.klasses:
- if not issubclass(klass, ofp.message.Message):
+ if not issubclass(klass, ofp.message.message):
continue
def fn():
obj = klass(xid=42)
diff --git a/py_gen/tests/of13.py b/py_gen/tests/of13.py
index 8c18f41..8e258df 100644
--- a/py_gen/tests/of13.py
+++ b/py_gen/tests/of13.py
@@ -30,6 +30,7 @@
from testutil import add_datafiles_tests
try:
+ import loxi
import loxi.of13 as ofp
from loxi.generic_util import OFReader
except ImportError:
@@ -66,7 +67,7 @@
'\x00\x00\x00\x04', # unknown type
'\x00\x01\x00\x04', # versionbitmap
])
- l = ofp.unpack_list_hello_elem(OFReader(buf))
+ 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))
@@ -88,26 +89,21 @@
mods = [ofp.action,ofp.message,ofp.common,ofp.oxm]
self.klasses = [klass for mod in mods
for klass in mod.__dict__.values()
- if hasattr(klass, 'show')]
+ if isinstance(klass, type) and
+ issubclass(klass, loxi.OFObject) and
+ hasattr(klass, 'pack')]
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
expected_failures = [
ofp.action.set_field, # field defaults to None
- ofp.common.table_feature_prop_apply_actions,
- ofp.common.table_feature_prop_apply_actions_miss,
- ofp.common.table_feature_prop_write_actions,
- ofp.common.table_feature_prop_write_actions_miss,
- ofp.common.table_features,
- ofp.message.table_features_stats_reply,
- ofp.message.table_features_stats_request,
]
for klass in self.klasses:
def fn():
obj = klass()
if hasattr(obj, "xid"): obj.xid = 42
buf = obj.pack()
- obj2 = klass.unpack(buf)
+ obj2 = klass.unpack(OFReader(buf))
self.assertEquals(obj, obj2)
if klass in expected_failures:
self.assertRaises(Exception, fn)
@@ -116,11 +112,9 @@
def test_parse_message(self):
expected_failures = [
- ofp.message.table_features_stats_reply,
- ofp.message.table_features_stats_request,
]
for klass in self.klasses:
- if not issubclass(klass, ofp.message.Message):
+ if not issubclass(klass, ofp.message.message):
continue
def fn():
obj = klass(xid=42)
diff --git a/py_gen/tests/testutil.py b/py_gen/tests/testutil.py
index ea30902..f8d5543 100644
--- a/py_gen/tests/testutil.py
+++ b/py_gen/tests/testutil.py
@@ -32,6 +32,7 @@
import os
import unittest
import test_data
+from loxi.generic_util import OFReader
# Human-friendly format for binary strings. 8 bytes per line.
def format_binary(buf):
@@ -55,7 +56,7 @@
b = format_binary(packed)
raise AssertionError("Serialization of %s failed\nExpected:\n%s\nActual:\n%s\nDiff:\n%s" % \
(type(obj).__name__, a, b, diff(a, b)))
- unpacked = type(obj).unpack(buf)
+ unpacked = type(obj).unpack(OFReader(buf))
if obj != unpacked:
a = obj.show()
b = unpacked.show()
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