Merge into master from pull request #43:
A few improvements to java_gen from the plane... (https://github.com/floodlight/loxigen/pull/43)
diff --git a/Makefile b/Makefile
index 146bdc5..c99ae0b 100644
--- a/Makefile
+++ b/Makefile
@@ -79,7 +79,7 @@
clean:
rm -rf loxi_output # only delete generated files in the default directory
- rm -f loxigen.log loxigen-test.log .loxi_ts.c .loxi_ts.python
+ rm -f loxigen.log loxigen-test.log .loxi_ts.c .loxi_ts.python .loxi_ts.java
debug:
@echo "LOXI_OUTPUT_DIR=\"${LOXI_OUTPUT_DIR}\""
@@ -107,6 +107,12 @@
make -C ${LOXI_OUTPUT_DIR}/locitest
${LOXI_OUTPUT_DIR}/locitest/locitest
+check-java: java
+ cd ${LOXI_OUTPUT_DIR}/openflowj/ && mvn compile test-compile test
+
+package-java: java
+ cd ${LOXI_OUTPUT_DIR}/openflowj/ && mvn package
+
pylint:
pylint -E ${LOXI_PY_FILES}
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index 4ac497f..0eb8f11 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -48,6 +48,8 @@
class JavaModel(object):
enum_blacklist = set(("OFDefinitions",))
enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
+ # OFUint structs are there for god-knows what in loci. We certainly don't need them.
+ interface_blacklist = set( ("OFUint8", "OFUint32",))
write_blacklist = defaultdict(lambda: set(), OFOxm=set(('typeLen',)), OFAction=set(('type',)), OFInstruction=set(('type',)), OFFlowMod=set(('command', )))
virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
@@ -74,6 +76,8 @@
for class_name, version_map in version_map_per_class.items():
interfaces.append(JavaOFInterface(class_name, version_map))
+ interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
+
return interfaces
@property
@@ -114,6 +118,10 @@
members=self.interfaces)
def generate_class(self, clazz):
+ """ return wether or not to generate implementation class clazz.
+ Now true for everything except OFTableModVer10.
+ @param clazz JavaOFClass instance
+ """
if clazz.interface.name.startswith("OFMatchV"):
return True
elif clazz.name == "OFTableModVer10":
@@ -188,10 +196,17 @@
Version agnostic, in contrast to the loxi_ir python model.
"""
def __init__(self, c_name, version_map):
+ """"
+ @param c_name: loxi style name (e.g., of_flow_add)
+ @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
+ """
self.c_name = c_name
self.version_map = version_map
+ # name: the Java Type name, e.g., OFFlowAdd
self.name = java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
+ # variable_name name to use for variables of this type. i.e., flowAdd
self.variable_name = self.name[2].lower() + self.name[3:]
+ # name for use in constants: FLOW_ADD
self.constant_name = c_name.upper().replace("OF_", "")
pck_suffix, parent_interface = self.class_info()
@@ -202,6 +217,10 @@
self.parent_interface = None
def class_info(self):
+ """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
+ # FIXME: This duplicates inheritance information that is now available in the loxi_ir
+ # model (note, that the loxi model is on versioned classes). Should check/infer the
+ # inheritance information from the versioned lox_ir classes.
if re.match(r'OF.+StatsRequest$', self.name):
return ("", "OFStatsRequest")
elif re.match(r'OF.+StatsReply$', self.name):
@@ -241,6 +260,9 @@
@property
@memoize
def members(self):
+ """return a list of all members to be exposed by this interface. Corresponds to
+ the union of the members of the vesioned classes without length, fieldlength
+ and pads (those are handled automatically during (de)serialization and not exposed"""
all_versions = []
member_map = collections.OrderedDict()
@@ -258,15 +280,18 @@
@property
@memoize
def is_virtual(self):
+ """ Is this interface virtual. If so, do not generate a builder interface """
return self.name in model.virtual_interfaces or all(ir_class.virtual for ir_class in self.version_map.values())
@property
def is_universal(self):
+ """ Is this interface universal, i.e., does it exist in all OF versions? """
return len(self.all_versions) == len(model.versions)
@property
@memoize
def all_versions(self):
+ """ return list of all versions that this interface exists in """
return self.version_map.keys()
def has_version(self, version):
@@ -289,6 +314,11 @@
Version specific child of a JavaOFInterface
"""
def __init__(self, interface, version, ir_class):
+ """
+ @param interface JavaOFInterface instance of the parent interface
+ @param version JavaOFVersion
+ @param ir_class OFClass from loxi_ir
+ """
self.interface = interface
self.ir_class = ir_class
self.c_name = self.ir_class.name
@@ -319,11 +349,13 @@
@property
def min_length(self):
+ """ @return the minimum wire length of an instance of this class in bytes """
id_tuple = (self.ir_class.name, self.version.int_version)
return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
@property
def is_fixed_length(self):
+ """ true iff this class serializes to a fixed length on the wire """
return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length
def all_properties(self):
@@ -593,7 +625,11 @@
class JavaEnum(object):
def __init__(self, c_name, version_enum_map):
self.c_name = c_name
- self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
+
+ if c_name == "of_stats_types":
+ self.name = "OFStatsType"
+ else:
+ self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
# Port_features has constants that start with digits
self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 38ce1ff..cc34a57 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -21,8 +21,10 @@
else:
return camel
-
java_primitive_types = set("byte char short int long".split(" "))
+
+### info table about java primitive types, for casting literals in the source code
+# { name : (signed?, length_in_bits) }
java_primitives_info = {
'byte' : (True, 8),
'char' : (False, 16),
@@ -31,7 +33,12 @@
'long' : (True, 64),
}
-def format_primitive_value(t, value):
+def format_primitive_literal(t, value):
+ """ Format a primitive numeric literal for inclusion in the
+ java source code. Takes care of casting the literal
+ apropriately for correct representation despite Java's
+ signed-craziness
+ """
signed, bits = java_primitives_info[t]
max = (1 << bits)-1
if value > max:
@@ -46,7 +53,7 @@
else:
return "(%s) 0x%x" % (t, value)
else:
- return "0x%x" % value
+ return "0x%x%s" % (value, "L" if t=="long" else "")
ANY = 0xFFFFFFFFFFFFFFFF
@@ -59,32 +66,46 @@
def __str__(self):
return "[Version: %d, Read: '%s', Write: '%s']" % (self.version, self.read, self.write)
+### FIXME: This class should really be cleaned up
class JType(object):
- """ Wrapper class to hold C to Java type conversion information """
- def __init__(self, pub_type, priv_type=None, size=None, read_op=None, write_op=None):
+ """ Wrapper class to hold C to Java type conversion information. JTypes can have a 'public'
+ and or 'private' java type associated with them and can define how those types can be
+ read from and written to ChannelBuffers.
+
+ """
+ def __init__(self, pub_type, priv_type=None, read_op=None, write_op=None):
self.pub_type = pub_type # the type we expose externally, e.g. 'U8'
if priv_type is None:
priv_type = pub_type
self.priv_type = priv_type # the internal storage type
- self.size = size # bytes on the wire; None == variable length or hard to calc
self.ops = {}
-# if read_op is None:
-# read_op = 'ChannelUtilsVer$version.read%s(bb)' % self.pub_type
-# if write_op is None:
-# write_op = 'ChannelUtilsVer$version.write%s(bb, $name)' % self.pub_type
-# self._read_op = read_op
-# self._write_op = write_op
def op(self, version=ANY, read=None, write=None, pub_type=ANY):
+ """
+ define operations to be performed for reading and writing this type
+ (when read_op, write_op is called). The operations 'read' and 'write'
+ can either be strings ($name, and $version and $length will be replaced),
+ or callables (name, version and length) will be passed.
+
+ @param version int OF version to define operation for, or ANY for all
+ @param pub_type boolean whether to define operations for the public type (True), the
+ private type(False) or both (ALL)
+ @param read read expression (either string or callable)s
+ @param write write expression (either string or callable)
+ """
+
pub_types = [ pub_type ] if pub_type is not ANY else [ False, True ]
for pub_type in pub_types:
- self.ops[(version,pub_type)] = VersionOp(version, read, write)
+ self.ops[(version, pub_type)] = VersionOp(version, read, write)
return self
def format_value(self, value, pub_type=True):
+ # Format a constant value of this type, for inclusion in the java source code
+ # For primitive types, takes care of casting the value appropriately, to
+ # cope with java's signedness limitation
t = self.pub_type if pub_type else self.priv_type
if t in java_primitive_types:
- return format_primitive_value(t, value)
+ return format_primitive_literal(t, value)
else:
return value
@@ -102,7 +123,18 @@
return self.pub_type != self.priv_type
def read_op(self, version=None, length=None, pub_type=True):
+ """ return a Java stanza that reads a value of this JType from ChannelBuffer bb.
+ @param version int - OF wire version to generate expression for
+ @param pub_type boolean use this JTypes 'public' (True), or private (False) representation
+ @param length string, for operations that need it (e.g., read a list of unknown length)
+ Java expression evaluating to the byte length to be read. Defaults to the remainig
+ length of the message.
+ @return string containing generated Java expression.
+ """
if length is None:
+ # assumes that
+ # (1) length of the message has been read to 'length'
+ # (2) readerIndex at the start of the message has been stored in 'start'
length = "length - (bb.readerIndex() - start)";
ver = ANY if version is None else version.int_version
@@ -119,6 +151,13 @@
return _read_op.replace("$length", str(length)).replace("$version", version.of_version)
def write_op(self, version=None, name=None, pub_type=True):
+ """ return a Java stanza that writes a value of this JType contained in Java expression
+ 'name' to ChannelBuffer bb.
+ @param name string containing Java expression that evaluations to the value to be written
+ @param version int - OF wire version to generate expression for
+ @param pub_type boolean use this JTypes 'public' (True), or private (False) representation
+ @return string containing generated Java expression.
+ """
ver = ANY if version is None else version.int_version
_write_op = None
if (ver, pub_type) in self.ops:
@@ -126,6 +165,7 @@
elif (ANY, pub_type) in self.ops:
_write_op = self.ops[(ANY, pub_type)].write
if _write_op is None:
+
_write_op = 'ChannelUtilsVer$version.write%s(bb, $name)' % self.pub_type
if callable(_write_op):
return _write_op(version, name)
@@ -133,82 +173,81 @@
return _write_op.replace("$name", str(name)).replace("$version", version.of_version)
def skip_op(self, version=None, length=None):
+ """ return a java stanza that skips an instance of JType in the input ChannelBuffer 'bb'.
+ This is used in the Reader implementations for virtual classes (because after the
+ discriminator field, the concrete Reader instance will re-read all the fields)
+ Currently just delegates to read_op + throws away the result."""
return self.read_op(version, length)
@property
def is_primitive(self):
+ """ return true if the pub_type is a java primitive type (and thus needs
+ special treatment, because it doesn't have methods)"""
return self.pub_type in java_primitive_types
@property
def is_array(self):
+ """ return true iff the pub_type is a Java array (and thus requires special
+ treament for equals / toString etc.) """
return self.pub_type.endswith("[]")
-u8 = JType('byte', size=1) \
+##### Predefined JType mappings
+# FIXME: This list needs to be pruned / cleaned up. Most of these are schematic.
+
+u8 = JType('short', 'byte') \
.op(read='bb.readByte()', write='bb.writeByte($name)')
-u8_list = JType('List<U8>', size=1) \
+u8_list = JType('List<U8>') \
.op(read='ChannelUtils.readList(bb, $length, U8.READER)', write='ChannelUtils.writeList(bb, $name)')
-u16 = JType('int', 'int', size=2) \
- .op(read='U16.f(bb.readShort())', write='bb.writeShort(U16.t($name))')
-u32 = JType('int', 'int', size=4) \
- .op(read='bb.readInt()', write='bb.writeInt($name)')
-u32_list = JType('List<U32>', 'int[]', size=4) \
+u16 = JType('int', 'short') \
+ .op(read='U16.f(bb.readShort())', write='bb.writeShort(U16.t($name))', pub_type=True) \
+ .op(read='bb.readShort()', write='bb.writeShort($name)', pub_type=False)
+u32 = JType('long', 'int') \
+ .op(read='U32.f(bb.readInt())', write='bb.writeInt(U32.t($name))', pub_type=True) \
+ .op(read='bb.readInt()', write='bb.writeInt($name)', pub_type=False)
+u32_list = JType('List<U32>', 'int[]') \
.op(read='ChannelUtils.readList(bb, $length, U32.READER)', write='ChannelUtils.writeList(bb, $name)')
-u64 = JType('U64', 'U64', size=8) \
+u64 = JType('U64', 'U64') \
.op(read='U64.of(bb.readLong())', write='bb.writeLong($name.getValue())')
of_port = JType("OFPort") \
.op(version=1, read="OFPort.read2Bytes(bb)", write="$name.write2Bytes(bb)") \
.op(version=ANY, read="OFPort.read4Bytes(bb)", write="$name.write4Bytes(bb)")
-one_byte_array = JType('byte[]', size=1) \
- .op(read='ChannelUtils.readBytes(bb, 1)', write='bb.writeBytes($name)')
-two_byte_array = JType('byte[]', size=2) \
- .op(read='ChannelUtils.readBytes(bb, 2)', write='bb.writeBytes($name)')
-three_byte_array = JType('byte[]', size=3) \
- .op(read='ChannelUtils.readBytes(bb, 3)', write='bb.writeBytes($name)')
-four_byte_array = JType('byte[]', size=4) \
- .op(read='ChannelUtils.readBytes(bb, 4)', write='bb.writeBytes($name)')
-five_byte_array = JType('byte[]', size=5) \
- .op(read='ChannelUtils.readBytes(bb, 5)', write='bb.writeBytes($name)')
-six_byte_array = JType('byte[]', size=6) \
- .op(read='ChannelUtils.readBytes(bb, 6)', write='bb.writeBytes($name)')
-seven_byte_array = JType('byte[]', size=7) \
- .op(read='ChannelUtils.readBytes(bb, 7)', write='bb.writeBytes($name)')
actions_list = JType('List<OFAction>') \
.op(read='ChannelUtils.readList(bb, $length, OFActionVer$version.READER)', write='ChannelUtils.writeList(bb, $name);')
instructions_list = JType('List<OFInstruction>') \
.op(read='ChannelUtils.readList(bb, $length, OFInstructionVer$version.READER)', \
write='ChannelUtils.writeList(bb, $name)')
-buckets_list = JType('List<OFBucket>', size='ChannelUtilsVer$version.calcListSize($name)') \
+buckets_list = JType('List<OFBucket>') \
.op(read='ChannelUtils.readList(bb, $length, OFBucketVer$version.READER)', write='ChannelUtils.writeList(bb, $name)')
-port_desc_list = JType('List<OFPhysicalPort>', size='ChannelUtilsVer$version.calcListSize($name)') \
+port_desc_list = JType('List<OFPhysicalPort>') \
.op(read='ChannelUtils.readList(bb, $length, OFPhysicalPort.READER)', write='ChannelUtils.writeList(bb, $name)')
-port_desc = JType('OFPortDesc', size='$name.getLength()') \
+port_desc = JType('OFPortDesc') \
.op(read='OFPortDescVer$version.READER.readFrom(bb)', \
write='$name.writeTo(bb)')
-packet_queue_list = JType('List<OFPacketQueue>', size='ChannelUtilsVer$version.calcListSize($name)') \
+packet_queue_list = JType('List<OFPacketQueue>') \
.op(read='ChannelUtils.readList(bb, $length, OFPacketQueueVer$version.READER)', write='ChannelUtils.writeList(bb, $name);')
-octets = JType('byte[]', size="$length") \
+octets = JType('byte[]') \
.op(read='ChannelUtils.readBytes(bb, $length)', \
write='bb.writeBytes($name)')
-of_match = JType('Match', size="$name.getLength()") \
+of_match = JType('Match') \
.op(read='ChannelUtilsVer$version.readOFMatch(bb)', \
write='$name.writeTo(bb)');
-flow_mod_cmd = JType('OFFlowModCommand', 'short', size="$name.getLength()") \
+flow_mod_cmd = JType('OFFlowModCommand', 'short') \
.op(version=1, read="bb.readShort()", write="bb.writeShort($name)") \
.op(version=ANY, read="bb.readByte()", write="bb.writeByte($name)")
-mac_addr = JType('MacAddress', 'byte[]', size=6) \
+mac_addr = JType('MacAddress') \
.op(read="MacAddress.read6Bytes(bb)", \
write="$name.write6Bytes(bb)")
-port_name = JType('String', size=16) \
+port_name = JType('String') \
.op(read='ChannelUtils.readFixedLengthString(bb, 16)', \
write='ChannelUtils.writeFixedLengthString(bb, $name, 16)')
-desc_str = JType('String', size=256) \
+desc_str = JType('String') \
.op(read='ChannelUtils.readFixedLengthString(bb, 256)', \
write='ChannelUtils.writeFixedLengthString(bb, $name, 256)')
-serial_num = JType('String', size=32) \
+serial_num = JType('String') \
.op(read='ChannelUtils.readFixedLengthString(bb, 32)', \
write='ChannelUtils.writeFixedLengthString(bb, $name, 32)')
-table_name = JType('String', size=32) \
+table_name = JType('String') \
.op(read='ChannelUtils.readFixedLengthString(bb, 32)', \
write='ChannelUtils.writeFixedLengthString(bb, $name, 32)')
ipv4 = JType("IPv4") \
@@ -234,13 +273,6 @@
'uint16_t' : u16,
'uint32_t' : u32,
'uint64_t' : u64,
- 'uint8_t[1]' : one_byte_array,
- 'uint8_t[2]' : two_byte_array,
- 'uint8_t[3]' : three_byte_array,
- 'uint8_t[4]' : four_byte_array,
- 'uint8_t[5]' : five_byte_array,
- 'uint8_t[6]' : six_byte_array,
- 'uint8_t[7]' : seven_byte_array,
'of_port_no_t' : of_port,
'list(of_action_t)' : actions_list,
'list(of_instruction_t)' : instructions_list,
@@ -265,7 +297,8 @@
'of_meter_features_t': meter_features,
}
-## This is where we drop in special case handling for certain types
+## Map that defines exceptions from the standard loxi->java mapping scheme
+# map of {<loxi_class_name> : { <loxi_member_name> : <JType instance> } }
exceptions = {
'of_packet_in': {
'data' : octets,
@@ -277,40 +310,40 @@
}
-enum_wire_types = {
- "uint8_t": JType("byte").op(read="bb.readByte()", write="bb.writeByte($name)"),
- "uint16_t": JType("short").op(read="bb.readShort()", write="bb.writeShort($name)"),
- "uint32_t": JType("int").op(read="bb.readInt()", write="bb.writeInt($name)"),
- "uint64_t": JType("long").op(read="bb.readLong()", write="bb.writeLong($name)"),
-}
-
-def convert_enum_wire_type_to_jtype(wire_type):
- return enum_wire_types[wire_type]
-
+# Create a default mapping for a list type. Type defauls to List<${java_mapping_of_name}>
def make_standard_list_jtype(c_type):
m = re.match(r'list\(of_([a-zA-Z_]+)_t\)', c_type)
if not m:
raise Exception("Not a recgonized standard list type declaration: %s" % c_type)
base_name = m.group(1)
java_base_name = name_c_to_caps_camel(base_name)
+
+ # read op assumes the class has a public final static field READER that implements
+ # OFMessageReader<$class> i.e., can deserialize an instance of class from a ChannelBuffer
+ # write op assumes class implements Writeable
return JType("List<OF%s>" % java_base_name) \
- .op(read= 'ChannelUtils.readList(bb, $length, OF%sVer$version.READER)' % java_base_name, \
+ .op(
+ read= 'ChannelUtils.readList(bb, $length, OF%sVer$version.READER)' % java_base_name, \
write='ChannelUtils.writeList(bb, $name)')
+
+#### main entry point for conversion of LOXI types (c_types) Java types.
+# FIXME: This badly needs a refactoring
+
def convert_to_jtype(obj_name, field_name, c_type):
""" Convert from a C type ("uint_32") to a java type ("U32")
and return a JType object with the size, internal type, and marshalling functions"""
if obj_name in exceptions and field_name in exceptions[obj_name]:
return exceptions[obj_name][field_name]
elif ( obj_name == "of_header" or loxi_utils.class_is_message(obj_name)) and field_name == "type" and c_type == "uint8_t":
- return JType("OFType", 'byte', size=1) \
+ return JType("OFType", 'byte') \
.op(read='bb.readByte()', write='bb.writeByte($name)')
elif field_name == "type" and re.match(r'of_action.*', obj_name):
- return JType("OFActionType", 'short', size=2) \
+ return JType("OFActionType", 'short') \
.op(read='bb.readShort()', write='bb.writeShort($name)', pub_type=False)\
.op(read="OFActionTypeSerializerVer$version.readFrom(bb)", write="OFActionTypeSerializerVer$version.writeTo(bb, $name)", pub_type=True)
elif field_name == "version" and c_type == "uint8_t":
- return JType("OFVersion", 'byte', size=1) \
+ return JType("OFVersion", 'byte') \
.op(read='bb.readByte()', write='bb.writeByte($name)')
elif c_type in default_mtype_to_jtype_convert_map:
return default_mtype_to_jtype_convert_map[c_type]
@@ -320,3 +353,15 @@
print "WARN: Couldn't find java type conversion for '%s' in %s:%s" % (c_type, obj_name, field_name)
jtype = name_c_to_caps_camel(re.sub(r'_t$', "", c_type))
return JType(jtype)
+
+
+#### Enum specific wiretype definitions
+enum_wire_types = {
+ "uint8_t": JType("byte").op(read="bb.readByte()", write="bb.writeByte($name)"),
+ "uint16_t": JType("short").op(read="bb.readShort()", write="bb.writeShort($name)"),
+ "uint32_t": JType("int").op(read="bb.readInt()", write="bb.writeInt($name)"),
+ "uint64_t": JType("long").op(read="bb.readLong()", write="bb.writeLong($name)"),
+}
+
+def convert_enum_wire_type_to_jtype(wire_type):
+ return enum_wire_types[wire_type]
diff --git a/java_gen/templates/of_class.java b/java_gen/templates/of_class.java
index 0781dcf..43d9893 100644
--- a/java_gen/templates/of_class.java
+++ b/java_gen/templates/of_class.java
@@ -187,7 +187,7 @@
bb.writeZero(${prop.length});
//:: elif prop.is_fixed_value:
// fixed value property ${prop.name} = ${prop.value}
- ${prop.java_type.write_op(version, prop.value, pub_type=False)};
+ ${prop.java_type.write_op(version, prop.priv_value, pub_type=False)};
//:: elif prop.is_length_value:
// ${prop.name} is length of variable message, will be updated at the end
//:: if not msg.is_fixed_length:
@@ -273,7 +273,9 @@
int result = 1;
//:: for prop in msg.data_members:
- //:: if prop.java_type.is_primitive:
+ //:: if prop.java_type.pub_type == 'long':
+ result = prime * (int) (${prop.name} ^ (${prop.name} >>> 32));
+ //:: elif prop.java_type.is_primitive:
result = prime * result + ${prop.name};
//:: elif prop.java_type.is_array:
result = prime * result + Arrays.hashCode(${prop.name});
diff --git a/test_data/of10/packet_in.data b/test_data/of10/packet_in.data
index 2cd98b0..bd89cf8 100644
--- a/test_data/of10/packet_in.data
+++ b/test_data/of10/packet_in.data
@@ -30,7 +30,7 @@
-- java
builder
.setXid(0x12345678)
- .setBufferId(0xabcdef01)
+ .setBufferId(0xabcdef01L)
.setTotalLen(9)
.setInPort(OFPort.LOCAL)
.setReason(OFPacketInReason.ACTION)
diff --git a/utest/test_frontend.py b/utest/test_frontend.py
index 9249273..8799000 100755
--- a/utest/test_frontend.py
+++ b/utest/test_frontend.py
@@ -56,7 +56,7 @@
#version 2
-struct of_echo_reply {
+struct of_echo_reply(align=8) {
uint8_t version;
uint8_t type == 3;
uint16_t length;
@@ -64,7 +64,7 @@
of_octets_t data;
};
-enum ofp_queue_op_failed_code {
+enum ofp_queue_op_failed_code(wire_type=uint32, bitmask=False, complete=True) {
OFPQOFC_BAD_PORT = 0,
OFPQOFC_BAD_QUEUE = 1,
OFPQOFC_EPERM = 2,
@@ -90,13 +90,14 @@
['OFPPC_NO_FWD', [], 32],
['OFPPC_NO_PACKET_IN', [], 64]]],
['metadata', 'version', '2'],
- ['struct', 'of_echo_reply', [], None, [
+ ['struct', 'of_echo_reply', [['align', '8']], None, [
['data', 'uint8_t', 'version'],
['type', 'uint8_t', 'type', 3],
['data', 'uint16_t', 'length'],
['data', 'uint32_t', 'xid'],
['data', 'of_octets_t', 'data']]],
- ['enum', 'ofp_queue_op_failed_code', [], [
+ ['enum', 'ofp_queue_op_failed_code',
+ [['wire_type', 'uint32'], ['bitmask','False'], ['complete', 'True']], [
['OFPQOFC_BAD_PORT', [], 0],
['OFPQOFC_BAD_QUEUE', [], 1],
['OFPQOFC_EPERM', [], 2]]],
@@ -116,7 +117,8 @@
OFTypeMember('type', 'uint8_t', 3),
OFLengthMember('length', 'uint16_t'),
OFDataMember('xid', 'uint32_t'),
- OFDataMember('data', 'of_octets_t')], virtual=False, params={}),
+ OFDataMember('data', 'of_octets_t')], virtual=False,
+ params={'align': '8'}),
OFClass(name='of_packet_queue', superclass=None, members=[
OFDataMember('queue_id', 'uint32_t'),
OFLengthMember('len', 'uint16_t'),
@@ -136,7 +138,8 @@
OFEnum(name='ofp_queue_op_failed_code', entries=[
OFEnumEntry('OFPQOFC_BAD_PORT', 0, {}),
OFEnumEntry('OFPQOFC_BAD_QUEUE', 1, {}),
- OFEnumEntry('OFPQOFC_EPERM', 2, {})], params={}),
+ OFEnumEntry('OFPQOFC_EPERM', 2, {})],
+ params={'wire_type': 'uint32', 'bitmask': 'False', 'complete': 'True'}),
]
self.assertEquals(expected_enums, ofinput.enums)
diff --git a/utest/test_parser.py b/utest/test_parser.py
index 8199096..9eb3764 100755
--- a/utest/test_parser.py
+++ b/utest/test_parser.py
@@ -54,6 +54,16 @@
self.assertEquals(ast,
[['struct', 'foo', [], None, [['data', 'uint32_t', 'bar']]]])
+ def test_struct_align_arg(self):
+ src = """\
+struct foo(align=8) {
+ uint32_t bar;
+};
+"""
+ ast = parser.parse(src)
+ self.assertEquals(ast,
+ [['struct', 'foo', [['align', '8']], None, [['data', 'uint32_t', 'bar']]]])
+
def test_multiple_fields(self):
src = """\
struct foo {
@@ -119,6 +129,16 @@
self.assertEquals(ast,
[['struct', 'foo', [], 'bar', [['type', 'uint16_t', 'foo', 0x10]]]])
+ def test_discriminator(self):
+ src = """\
+struct foo {
+ uint16_t foo == ?;
+};
+"""
+ ast = parser.parse(src)
+ self.assertEquals(ast,
+ [['struct', 'foo', [], None, [['discriminator', 'uint16_t', 'foo']]]])
+
class EnumTests(unittest.TestCase):
def test_empty(self):
src = """\
@@ -137,6 +157,17 @@
ast = parser.parse(src)
self.assertEquals(ast, [['enum', 'foo', [], [['BAR', [], 1]]]])
+ def test_params(self):
+ src = """\
+enum foo(wire_type=uint32, bitmask=False, complete=False) {
+ BAR = 1
+};
+"""
+ ast = parser.parse(src)
+ self.assertEquals(ast, [['enum', 'foo',
+ [ ['wire_type', 'uint32'], ['bitmask','False'], ['complete', 'False']],
+ [['BAR', [], 1]]]])
+
def test_multiple(self):
src = """\
enum foo {