Merge branch '13-support' of git://github.com/jonstout/loxigen
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index 0a10715..09c4f2f 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -756,6 +756,19 @@
*
****************************************************************/
+#ifdef __GNUC__
+#include <features.h>
+
+#if __GNUC_PREREQ(4,4)
+#pragma GCC optimize ("s")
+#endif
+
+#if __GNUC_PREREQ(4,6)
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+
+#endif
+
#include <loci/loci.h>
#include <loci/of_object.h>
#include "loci_log.h"
@@ -892,13 +905,17 @@
"init", \\
"unknown"
-extern const char *of_error_strings[];
+extern const char *const of_error_strings[];
+#ifndef NDEBUG
/* #define ASSERT(val) assert(val) */
#define FORCE_FAULT *(volatile int *)0 = 1
#define ASSERT(val) if (!(val)) \\
fprintf(stderr, "\\nASSERT %s. %s:%d\\n", #val, __FILE__, __LINE__), \\
FORCE_FAULT
+#else
+#define ASSERT(val)
+#endif
/*
* Some LOCI object accessors can fail, and it's easy to forget to check.
@@ -1101,8 +1118,10 @@
static inline void
of_object_parent_length_update(of_object_t *obj, int delta)
{
+#ifndef NDEBUG
int count = 0;
of_wire_buffer_t *wbuf; /* For debug asserts only */
+#endif
while (obj != NULL) {
ASSERT(count++ < _MAX_PARENT_ITERATIONS);
@@ -1110,7 +1129,9 @@
if (obj->wire_length_set != NULL) {
obj->wire_length_set(obj, obj->length);
}
+#ifndef NDEBUG
wbuf = obj->wire_object.wbuf;
+#endif
/* Asserts for wire length checking */
ASSERT(obj->length + obj->wire_object.obj_offset <=
@@ -1221,7 +1242,7 @@
OF_OBJECT_COUNT = %d
} of_object_id_t;
-extern const char *of_object_id_str[];
+extern const char *const of_object_id_str[];
#define OF_MESSAGE_OBJECT_COUNT %d
""" % ((last + 1), msg_count))
@@ -1282,7 +1303,7 @@
""")
def gen_object_enum_str(out):
- out.write("\nconst char *of_object_id_str[] = {\n")
+ out.write("\nconst char *const of_object_id_str[] = {\n")
out.write(" \"of_object\",\n")
for cls in of_g.ordered_messages:
out.write(" \"%s\",\n" % cls)
@@ -1299,7 +1320,7 @@
# We'll do version strings while we're at it
out.write("""
- const char *of_version_str[] = {
+ const char *const of_version_str[] = {
"Unknown OpenFlow Version",
"OpenFlow-1.0",
"OpenFlow-1.1",
@@ -1335,7 +1356,7 @@
/** @var of_error_strings
* The error string map; use abs value to index
*/
-const char *of_error_strings[] = { OF_ERROR_STRINGS };
+const char *const of_error_strings[] = { OF_ERROR_STRINGS };
""")
################################################################
@@ -2984,7 +3005,7 @@
out.write("""
typedef void (*of_object_init_f)(of_object_t *obj, of_version_t version,
int bytes, int clean_wire);
-extern of_object_init_f of_object_init_map[];
+extern const of_object_init_f of_object_init_map[];
""")
out.write("""
@@ -3112,7 +3133,7 @@
/**
* Map from object ID to type coerce function
*/
-of_object_init_f of_object_init_map[] = {
+const of_object_init_f of_object_init_map[] = {
(of_object_init_f)NULL,
""")
count = 1
diff --git a/c_gen/c_dump_gen.py b/c_gen/c_dump_gen.py
index dbf1e7a..08abae0 100644
--- a/c_gen/c_dump_gen.py
+++ b/c_gen/c_dump_gen.py
@@ -228,7 +228,7 @@
# Generate big table indexed by version and object
for version in of_g.of_version_range:
out.write("""
-static loci_obj_dump_f dump_funs_v%(version)s[OF_OBJECT_COUNT] = {
+static const loci_obj_dump_f dump_funs_v%(version)s[OF_OBJECT_COUNT] = {
""" % dict(version=version))
out.write(" unknown_dump, /* of_object, not a valid specific type */\n")
for j, cls in enumerate(of_g.all_class_order):
@@ -245,7 +245,7 @@
out.write("};\n\n")
out.write("""
-static loci_obj_dump_f *dump_funs[5] = {
+static const loci_obj_dump_f *const dump_funs[5] = {
NULL,
dump_funs_v1,
dump_funs_v2,
diff --git a/c_gen/c_match.py b/c_gen/c_match.py
index 1b5be1e..8c27bb5 100644
--- a/c_gen/c_match.py
+++ b/c_gen/c_match.py
@@ -338,7 +338,7 @@
out.write(""")
/* Indexed by version number */
-extern uint64_t of_match_incompat[4];
+extern const uint64_t of_match_incompat[4];
""")
@@ -387,7 +387,7 @@
}
/* Indexed by version number */
-uint64_t of_match_incompat[4] = {
+const uint64_t of_match_incompat[4] = {
-1,
OF_MATCH_V1_INCOMPAT,
OF_MATCH_V2_INCOMPAT,
diff --git a/c_gen/c_show_gen.py b/c_gen/c_show_gen.py
index 5dab038..14709ab 100644
--- a/c_gen/c_show_gen.py
+++ b/c_gen/c_show_gen.py
@@ -226,7 +226,7 @@
# Generate big table indexed by version and object
for version in of_g.of_version_range:
out.write("""
-static loci_obj_show_f show_funs_v%(version)s[OF_OBJECT_COUNT] = {
+static const loci_obj_show_f show_funs_v%(version)s[OF_OBJECT_COUNT] = {
""" % dict(version=version))
out.write(" unknown_show, /* of_object, not a valid specific type */\n")
for j, cls in enumerate(of_g.all_class_order):
@@ -243,7 +243,7 @@
out.write("};\n\n")
out.write("""
-static loci_obj_show_f *show_funs[5] = {
+static const loci_obj_show_f *const show_funs[5] = {
NULL,
show_funs_v1,
show_funs_v2,
diff --git a/c_gen/c_type_maps.py b/c_gen/c_type_maps.py
index 924243a..d790c85 100644
--- a/c_gen/c_type_maps.py
+++ b/c_gen/c_type_maps.py
@@ -52,7 +52,7 @@
*/
""")
for version in of_g.of_version_range:
- out.write("static int\nof_object_to_type_map_v%d[OF_OBJECT_COUNT] = {\n"
+ out.write("static const int\nof_object_to_type_map_v%d[OF_OBJECT_COUNT] = {\n"
%version)
out.write(" -1, /* of_object, not a valid specific type */\n")
for j, cls in enumerate(of_g.all_class_order):
@@ -112,7 +112,7 @@
/**
* Unified map, indexed by wire version which is 1-based.
*/
-int *of_object_to_type_map[OF_VERSION_ARRAY_MAX] = {
+const int *const of_object_to_type_map[OF_VERSION_ARRAY_MAX] = {
NULL,
""")
for version in of_g.of_version_range:
@@ -130,7 +130,7 @@
""")
for version in of_g.of_version_range:
out.write("""
-static of_experimenter_data_t
+static const of_experimenter_data_t
of_object_to_extension_data_v%d[OF_OBJECT_COUNT] = {
""" % version)
out.write(" {0, 0, 0}, /* of_object, not a valid specific type */\n")
@@ -153,7 +153,7 @@
/**
* Unified map, indexed by wire version which is 1-based.
*/
-of_experimenter_data_t *of_object_to_extension_data[OF_VERSION_ARRAY_MAX] = {
+const of_experimenter_data_t *const of_object_to_extension_data[OF_VERSION_ARRAY_MAX] = {
NULL,
""")
for version in of_g.of_version_range:
@@ -182,7 +182,7 @@
for i, ar in enumerate(all_ars):
version = i + 1
- out.write("static of_object_id_t\nof_%s_v%d[%s] = {\n" %
+ out.write("static const of_object_id_t\nof_%s_v%d[%s] = {\n" %
(type_str, version, len_name))
for i in range(arr_len):
comma = ""
@@ -210,7 +210,7 @@
* Indexed by wire version which is 1-based.
*/
-of_object_id_t *of_%(name)s[OF_VERSION_ARRAY_MAX] = {
+const of_object_id_t *const of_%(name)s[OF_VERSION_ARRAY_MAX] = {
NULL,
""" % dict(name=type_str, c_name=prefix.lower()))
for version in of_g.of_version_range:
@@ -291,7 +291,7 @@
* Treat as private; use function accessor below
*/
-extern of_object_id_t *of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
+extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
@@ -322,7 +322,7 @@
* Treat as private; use function accessor below
*/
-extern of_object_id_t *of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
+extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
@@ -357,7 +357,7 @@
* Treat as private; use function accessor below
*/
-extern of_object_id_t *of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
+extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
@@ -441,7 +441,7 @@
* Treat as private; use function accessor below
*/
-extern of_object_id_t *of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
+extern const of_object_id_t *const of_%(name)s_type_to_id[OF_VERSION_ARRAY_MAX];
#define OF_%(u_name)s_ITEM_COUNT %(ar_len)d\n
@@ -586,7 +586,7 @@
################################################################
out.write("""
-extern int *of_object_to_type_map[OF_VERSION_ARRAY_MAX];
+extern const int *const of_object_to_type_map[OF_VERSION_ARRAY_MAX];
/**
* Map an object ID to its primary wire type value
@@ -632,7 +632,7 @@
""")
out.write("""
-extern of_experimenter_data_t *of_object_to_extension_data[OF_VERSION_ARRAY_MAX];
+extern const of_experimenter_data_t *const of_object_to_extension_data[OF_VERSION_ARRAY_MAX];
/**
* Map from the object ID of an extension to the experimenter ID
@@ -792,7 +792,7 @@
gen_type_to_obj_map_functions(out)
gen_obj_to_type_map_functions(out)
- out.write("extern int *of_object_fixed_len[OF_VERSION_ARRAY_MAX];\n")
+ out.write("extern const int *const of_object_fixed_len[OF_VERSION_ARRAY_MAX];\n")
out.write("""
/**
@@ -1038,7 +1038,7 @@
for version in of_g.of_version_range:
out.write("""
-static int\nof_object_fixed_len_v%d[OF_OBJECT_COUNT] = {
+static const int\nof_object_fixed_len_v%d[OF_OBJECT_COUNT] = {
-1, /* of_object is not instantiable */
""" % version)
for i, cls in enumerate(of_g.all_class_order):
@@ -1055,7 +1055,7 @@
/**
* Unified map of fixed length part of each object
*/
-int *of_object_fixed_len[OF_VERSION_ARRAY_MAX] = {
+const int *const of_object_fixed_len[OF_VERSION_ARRAY_MAX] = {
NULL,
""")
for version in of_g.of_version_range:
@@ -1078,7 +1078,7 @@
*/
""")
for version in of_g.of_version_range:
- out.write("int *of_object_to_stats_type_map_v%d = {\n" % (i+1))
+ out.write("const int *of_object_to_stats_type_map_v%d = {\n" % (i+1))
out.write(" -1, /* of_object (invalid) */\n");
for cls in of_g.ordered_messages:
name = cls[3:]
@@ -1096,7 +1096,7 @@
/**
* Unified map, indexed by wire version which is 1-based.
*/
-int *of_object_to_stats_type_map[OF_VERSION_ARRAY_MAX] = {
+const int *of_object_to_stats_type_map[OF_VERSION_ARRAY_MAX] = {
NULL,
""")
for version in of_g.of_version_range:
diff --git a/c_gen/templates/loci_show.h b/c_gen/templates/loci_show.h
index 86efab1..ed7a7d5 100644
--- a/c_gen/templates/loci_show.h
+++ b/c_gen/templates/loci_show.h
@@ -108,6 +108,7 @@
#define LOCI_SHOW_u16_tcp_dst(writer, cookie, val) LOCI_SHOW_u16(writer, cookie, val)
#define LOCI_SHOW_u8_ip_proto(writer, cookie, val) LOCI_SHOW_u8(writer, cookie, val)
#define LOCI_SHOW_u64_metadata(writer, cookie, val) LOCI_SHOW_x64(writer, cookie, val)
+#define LOCI_SHOW_u8_enabled(writer, cookie, val) LOCI_SHOW_u8(writer, cookie, val)
diff --git a/c_gen/templates/of_type_maps.c b/c_gen/templates/of_type_maps.c
index ef1df37..f1fab57 100644
--- a/c_gen/templates/of_type_maps.c
+++ b/c_gen/templates/of_type_maps.c
@@ -50,8 +50,7 @@
void
of_object_message_wire_length_get(of_object_t *obj, int *bytes)
{
- of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
- ASSERT(wbuf != NULL);
+ ASSERT(OF_OBJECT_TO_WBUF(obj) != NULL);
// ASSERT(obj is message)
*bytes = of_message_length_get(OF_OBJECT_TO_MESSAGE(obj));
}
@@ -65,9 +64,7 @@
void
of_object_message_wire_length_set(of_object_t *obj, int bytes)
{
- of_wire_buffer_t *wbuf = OF_OBJECT_TO_WBUF(obj);
- ASSERT(wbuf != NULL);
-
+ ASSERT(OF_OBJECT_TO_WBUF(obj) != NULL);
// ASSERT(obj is message)
of_message_length_set(OF_OBJECT_TO_MESSAGE(obj), bytes);
}
diff --git a/loxi_front_end/of_h_utils.py b/loxi_front_end/of_h_utils.py
index 1259a90..26dbac1 100644
--- a/loxi_front_end/of_h_utils.py
+++ b/loxi_front_end/of_h_utils.py
@@ -65,6 +65,9 @@
if ident.find("OFPP_") == 0 and version == of_g.VERSION_1_0:
value_string = "0x%x" % (int(value, 0) + 0xffff0000)
+ if ident == 'OFP_VLAN_NONE' and version > of_g.VERSION_1_0:
+ value_string = '0'
+
# Otherwise, if no reference to a wildcard value, all done
if value_string.find("OFPFW") < 0:
return value_string
diff --git a/loxi_front_end/type_maps.py b/loxi_front_end/type_maps.py
index ecfd850..df002c4 100644
--- a/loxi_front_end/type_maps.py
+++ b/loxi_front_end/type_maps.py
@@ -795,6 +795,7 @@
"of_bsn_shell_status" : 8,
"of_bsn_get_interfaces_request" : 9,
"of_bsn_get_interfaces_reply" : 10,
+ "of_bsn_set_pktin_suppression" : 11,
},
nicira = { # Nicira extensions, value is subtype
"of_nicira_controller_role_request" : 10,
@@ -808,6 +809,7 @@
"of_bsn_get_mirroring_reply" : 5,
"of_bsn_get_interfaces_request" : 9,
"of_bsn_get_interfaces_reply" : 10,
+ "of_bsn_set_pktin_suppression" : 11,
},
),
of_g.VERSION_1_2:dict( # Version 1.0 extensions
@@ -817,6 +819,7 @@
"of_bsn_get_mirroring_reply" : 5,
"of_bsn_get_interfaces_request" : 9,
"of_bsn_get_interfaces_reply" : 10,
+ "of_bsn_set_pktin_suppression" : 11,
},
),
of_g.VERSION_1_3:dict( # Version 1.0 extensions
@@ -826,6 +829,7 @@
"of_bsn_get_mirroring_reply" : 5,
"of_bsn_get_interfaces_request" : 9,
"of_bsn_get_interfaces_reply" : 10,
+ "of_bsn_set_pktin_suppression" : 11,
},
),
}
diff --git a/loxi_utils/loxi_utils.py b/loxi_utils/loxi_utils.py
index cc3e635..5508c94 100644
--- a/loxi_utils/loxi_utils.py
+++ b/loxi_utils/loxi_utils.py
@@ -33,6 +33,7 @@
These may need to be sorted out into language specific functions
"""
+import sys
import of_g
import tenjin
@@ -502,7 +503,7 @@
"""
pp = [ tenjin.PrefixedLinePreprocessor() ] # support "::" syntax
template_globals = { "to_str": str, "escape": str } # disable HTML escaping
- engine = tenjin.Engine(path=path, pp=pp)
+ engine = TemplateEngine(path=path, pp=pp)
out.write(engine.render(name, context, template_globals))
def render_static(out, name, path):
@@ -518,3 +519,18 @@
raise ValueError("template %s not found" % name)
with open(template_filename) as infile:
out.write(infile.read())
+
+class TemplateEngine(tenjin.Engine):
+ def include(self, template_name, **kwargs):
+ """
+ Tenjin has an issue with nested includes that use the same local variable
+ names, because it uses the same context dict for each level of nesting.
+ The fix is to copy the context.
+ """
+ frame = sys._getframe(1)
+ locals = frame.f_locals
+ globals = frame.f_globals
+ context = locals["_context"].copy()
+ context.update(kwargs)
+ template = self.get_template(template_name, context, globals)
+ return template.render(context, globals, _buf=locals["_buf"])
diff --git a/openflow_input/bsn_pktin_suppression b/openflow_input/bsn_pktin_suppression
new file mode 100644
index 0000000..fcfa4aa
--- /dev/null
+++ b/openflow_input/bsn_pktin_suppression
@@ -0,0 +1,71 @@
+// 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.
+
+#version any
+
+// This extension enables the switch to send only one packet-in for a given flow.
+// The goal is to reduce the load on the controller, particularly in the case of
+// large UDP streams.
+//
+// When this extension is enabled (by sending the bsn_set_pktin_suppression message
+// described below with enabled=1) the switch will install an exact-match drop
+// flow for each packet-in. This flow has the idle_timeout, hard_timeout,
+// priority, and cookie fields set to the values given in the
+// bsn_set_pktin_suppression message.
+//
+// This extension does not affect packet-ins caused by an output to
+// OFPP_CONTROLLER.
+//
+// When this extension is enabled the switch may still send multiple packet-ins
+// for the same flow.
+//
+// This extension keeps its configuration across controller disconnects. When a
+// controller that is aware of this extension connects to a switch it should send
+// a DELETE flow-mod with the cookie field set to ensure that it receives
+// packet-ins for ongoing flows.
+//
+// The flows installed by this extension are not automatically deleted when the
+// extension is disabled. Similarly other configuration changes (cookie, etc) do
+// not affect already-installed flows.
+//
+// The switch should reject the message if both 'hard_timeout' and 'idle_timeout'
+// are zero, since the suppression flows would never expire.
+
+struct ofp_bsn_set_pktin_suppression {
+ uint8_t version;
+ uint8_t type;
+ uint16_t length;
+ uint32_t xid;
+ uint32_t experimenter; // OF_EXPERIMENTER_ID_BSN
+ uint32_t subtype; // 11
+ uint8_t enabled;
+ uint8_t pad;
+ uint16_t idle_timeout;
+ uint16_t hard_timeout;
+ uint16_t priority;
+ uint64_t cookie;
+};
diff --git a/py_gen/codegen.py b/py_gen/codegen.py
index 211c71e..67529e0 100644
--- a/py_gen/codegen.py
+++ b/py_gen/codegen.py
@@ -92,6 +92,7 @@
length_member = None
type_members = []
+ pad_count = 0
for member in unified_class['members']:
if member['name'] in ['length', 'len']:
@@ -105,9 +106,12 @@
value=type_values[member['name']]))
else:
# HACK ensure member names are unique
- if member['name'] == "pad" and \
- [x for x in members if x.name == 'pad']:
- m_name = "pad2"
+ if member['name'].startswith("pad"):
+ if pad_count == 0:
+ m_name = 'pad'
+ else:
+ m_name = "pad%d" % pad_count
+ pad_count += 1
else:
m_name = member['name']
members.append(Member(name=m_name,
diff --git a/py_gen/templates/_ofclass.py b/py_gen/templates/_ofclass.py
new file mode 100644
index 0000000..ff8b21b
--- /dev/null
+++ b/py_gen/templates/_ofclass.py
@@ -0,0 +1,42 @@
+:: nonskip_members = [m for m in ofclass.members if not m.skip]
+class ${ofclass.pyname}(${superclass}):
+:: for m in ofclass.type_members:
+ ${m.name} = ${m.value}
+:: #endfor
+
+ def __init__(${', '.join(['self'] + ["%s=None" % m.name for m in nonskip_members])}):
+:: for m in nonskip_members:
+ if ${m.name} != None:
+ self.${m.name} = ${m.name}
+ else:
+ self.${m.name} = ${m.oftype.gen_init_expr()}
+:: #endfor
+ return
+
+ def pack(self):
+ packed = []
+:: include("_pack.py", ofclass=ofclass)
+ return ''.join(packed)
+
+ @staticmethod
+ def unpack(buf):
+ obj = ${ofclass.pyname}()
+:: include("_unpack.py", ofclass=ofclass)
+ return obj
+
+ def __eq__(self, other):
+ if type(self) != type(other): return False
+:: for m in nonskip_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)
diff --git a/py_gen/templates/action.py b/py_gen/templates/action.py
index 439e56f..089cc53 100644
--- a/py_gen/templates/action.py
+++ b/py_gen/templates/action.py
@@ -37,66 +37,20 @@
def unpack_list(buf):
if len(buf) % 8 != 0: raise loxi.ProtocolError("action list length not a multiple of 8")
- actions = []
- offset = 0
- while offset < len(buf):
- type, length = struct.unpack_from("!HH", buf, offset)
- if length == 0: raise loxi.ProtocolError("action length is 0")
+ def deserializer(buf):
+ type, length = struct.unpack_from("!HH", buf)
if length % 8 != 0: raise loxi.ProtocolError("action length not a multiple of 8")
- if offset + length > len(buf): raise loxi.ProtocolError("action length overruns list length")
parser = parsers.get(type)
if not parser: raise loxi.ProtocolError("unknown action type %d" % type)
- actions.append(parser(buffer(buf, offset, length)))
- offset += length
- return actions
+ return parser(buf)
+ return util.unpack_list(deserializer, "!2xH", buf)
class Action(object):
type = None # override in subclass
pass
:: for ofclass in ofclasses:
-:: nonskip_members = [m for m in ofclass.members if not m.skip]
-class ${ofclass.pyname}(Action):
-:: for m in ofclass.type_members:
- ${m.name} = ${m.value}
-:: #endfor
-
- def __init__(self, ${', '.join(["%s=None" % m.name for m in nonskip_members])}):
-:: for m in nonskip_members:
- if ${m.name} != None:
- self.${m.name} = ${m.name}
- else:
- self.${m.name} = ${m.oftype.gen_init_expr()}
-:: #endfor
-
- def pack(self):
- packed = []
-:: include("_pack.py", ofclass=ofclass)
- return ''.join(packed)
-
- @staticmethod
- def unpack(buf):
- obj = ${ofclass.pyname}()
-:: include("_unpack.py", ofclass=ofclass)
- return obj
-
- def __eq__(self, other):
- if type(self) != type(other): return False
- if self.type != other.type: return False
-:: for m in nonskip_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)
+:: include('_ofclass.py', ofclass=ofclass, superclass="Action")
:: #endfor
diff --git a/py_gen/templates/common.py b/py_gen/templates/common.py
index 08fb65d..c9af309 100644
--- a/py_gen/templates/common.py
+++ b/py_gen/templates/common.py
@@ -39,87 +39,23 @@
common = sys.modules[__name__]
def unpack_list_flow_stats_entry(buf):
- entries = []
- offset = 0
- while offset < len(buf):
- length, = struct.unpack_from("!H", buf, offset)
- if length == 0: raise loxi.ProtocolError("entry length is 0")
- if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
- entries.append(flow_stats_entry.unpack(buffer(buf, offset, length)))
- offset += length
- return entries
+ return util.unpack_list(flow_stats_entry.unpack, "!H", buf)
def unpack_list_queue_prop(buf):
- entries = []
- offset = 0
- while offset < len(buf):
- type, length, = struct.unpack_from("!HH", buf, offset)
- if length == 0: raise loxi.ProtocolError("entry length is 0")
- if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
+ def deserializer(buf):
+ type, = struct.unpack_from("!H", buf)
if type == const.OFPQT_MIN_RATE:
- entry = queue_prop_min_rate.unpack(buffer(buf, offset, length))
+ return queue_prop_min_rate.unpack(buf)
else:
raise loxi.ProtocolError("unknown queue prop %d" % type)
- entries.append(entry)
- offset += length
- return entries
+ return util.unpack_list(deserializer, "!2xH", buf)
def unpack_list_packet_queue(buf):
- entries = []
- offset = 0
- while offset < len(buf):
- _, length, = struct.unpack_from("!LH", buf, offset)
- if length == 0: raise loxi.ProtocolError("entry length is 0")
- if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
- entries.append(packet_queue.unpack(buffer(buf, offset, length)))
- offset += length
- return entries
+ return util.unpack_list(packet_queue.unpack, "!4xH", buf)
:: for ofclass in ofclasses:
-class ${ofclass.pyname}(object):
-:: for m in ofclass.type_members:
- ${m.name} = ${m.value}
+:: include('_ofclass.py', ofclass=ofclass, superclass="object")
+
:: #endfor
- def __init__(self, ${', '.join(["%s=None" % m.name for m in ofclass.members])}):
-:: for m in ofclass.members:
- if ${m.name} != None:
- self.${m.name} = ${m.name}
- else:
- self.${m.name} = ${m.oftype.gen_init_expr()}
-:: #endfor
-
- def pack(self):
- packed = []
-:: include("_pack.py", ofclass=ofclass)
- return ''.join(packed)
-
- @staticmethod
- def unpack(buf):
- assert(len(buf) >= ${ofclass.min_length}) # Should be verified by caller
- obj = ${ofclass.pyname}()
-:: include("_unpack.py", ofclass=ofclass)
- return obj
-
- def __eq__(self, other):
- if type(self) != type(other): return False
-:: for m in ofclass.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)
-
-:: if ofclass.name.startswith("of_match_v"):
-match = ${ofclass.pyname}
-
-:: #endif
-:: #endfor
+match = match_v1
diff --git a/py_gen/templates/util.py b/py_gen/templates/util.py
index 5933e14..fead78f 100644
--- a/py_gen/templates/util.py
+++ b/py_gen/templates/util.py
@@ -31,6 +31,7 @@
import loxi
import const
+import struct
def unpack_array(deserializer, element_size, buf):
"""
@@ -41,6 +42,26 @@
n = len(buf) / element_size
return [deserializer(buffer(buf, i*element_size, element_size)) for i in range(n)]
+def unpack_list(deserializer, length_fmt, buf):
+ """
+ Deserialize a list of variable-length entries.
+ 'length_fmt' is a struct format string with exactly one non-padding format
+ character that returns the length of the given element.
+ The deserializer function should take a buffer and return the new object.
+ """
+ entries = []
+ offset = 0
+ length_struct = struct.Struct(length_fmt)
+ n = len(buf)
+ while offset < n:
+ if offset + length_struct.size > len(buf): raise loxi.ProtocolError("entry header overruns list length")
+ length, = length_struct.unpack_from(buf, offset)
+ if length < length_struct.size: raise loxi.ProtocolError("entry length is less than the header length")
+ if offset + length > len(buf): raise loxi.ProtocolError("entry length overruns list length")
+ entries.append(deserializer(buffer(buf, offset, length)))
+ offset += length
+ return entries
+
def pretty_mac(mac):
return ':'.join(["%02x" % x for x in mac])
diff --git a/py_gen/tests.py b/py_gen/tests/of10.py
similarity index 95%
rename from py_gen/tests.py
rename to py_gen/tests/of10.py
index 23d40b8..86f9b2b 100644
--- a/py_gen/tests.py
+++ b/py_gen/tests/of10.py
@@ -28,8 +28,7 @@
import unittest
try:
- import loxi
- del loxi
+ import loxi.of10 as ofp
except ImportError:
exit("loxi package not found. Try setting PYTHONPATH.")
@@ -45,7 +44,7 @@
self.assertTrue(hasattr(ofp, "message"))
def test_version(self):
- import loxi.of10
+ import loxi
self.assertTrue(hasattr(loxi.of10, "ProtocolError"))
self.assertTrue(hasattr(loxi.of10, "OFP_VERSION"))
self.assertEquals(loxi.of10.OFP_VERSION, 1)
@@ -56,14 +55,11 @@
class TestActions(unittest.TestCase):
def test_output_pack(self):
- import loxi.of10 as ofp
expected = "\x00\x00\x00\x08\xff\xf8\xff\xff"
action = ofp.action.output(port=ofp.OFPP_IN_PORT, max_len=0xffff)
self.assertEquals(expected, action.pack())
def test_output_unpack(self):
- import loxi.of10 as ofp
-
# Normal case
buf = "\x00\x00\x00\x08\xff\xf8\xff\xff"
action = ofp.action.output.unpack(buf)
@@ -76,7 +72,6 @@
ofp.action.output.unpack(buf)
def test_output_equality(self):
- import loxi.of10 as ofp
action = ofp.action.output(port=1, max_len=0x1234)
action2 = ofp.action.output(port=1, max_len=0x1234)
self.assertEquals(action, action2)
@@ -90,13 +85,11 @@
action2.max_len = 0x1234
def test_output_show(self):
- import loxi.of10 as ofp
action = ofp.action.output(port=1, max_len=0x1234)
expected = "output { port = 1, max_len = 0x1234 }"
self.assertEquals(expected, action.show())
def test_bsn_set_tunnel_dst_pack(self):
- import loxi.of10 as ofp
expected = ''.join([
"\xff\xff", "\x00\x10", # type/length
"\x00\x5c\x16\xc7", # experimenter
@@ -107,7 +100,6 @@
self.assertEquals(expected, action.pack())
def test_bsn_set_tunnel_dst_unpack(self):
- import loxi.of10 as ofp
buf = ''.join([
"\xff\xff", "\x00\x10", # type/length
"\x00\x5c\x16\xc7", # experimenter
@@ -121,8 +113,6 @@
# Assumes action serialization/deserialization works
class TestActionList(unittest.TestCase):
def test_normal(self):
- import loxi.of10 as ofp
-
expected = []
bufs = []
@@ -140,20 +130,16 @@
self.assertEquals(actions, expected)
def test_empty_list(self):
- import loxi.of10 as ofp
self.assertEquals(ofp.action.unpack_list(''), [])
def test_invalid_list_length(self):
- import loxi.of10 as ofp
buf = '\x00' * 9
with self.assertRaisesRegexp(ofp.ProtocolError, 'not a multiple of 8'):
ofp.action.unpack_list(buf)
def test_invalid_action_length(self):
- import loxi.of10 as ofp
-
buf = '\x00' * 8
- with self.assertRaisesRegexp(ofp.ProtocolError, 'is 0'):
+ with self.assertRaisesRegexp(ofp.ProtocolError, 'is less than the header length'):
ofp.action.unpack_list(buf)
buf = '\x00\x00\x00\x04'
@@ -165,23 +151,19 @@
ofp.action.unpack_list(buf)
def test_invalid_action_type(self):
- import loxi.of10 as ofp
buf = '\xff\xfe\x00\x08\x00\x00\x00\x00'
with self.assertRaisesRegexp(ofp.ProtocolError, 'unknown action type'):
ofp.action.unpack_list(buf)
class TestConstants(unittest.TestCase):
def test_ports(self):
- import loxi.of10 as ofp
self.assertEquals(0xffff, ofp.OFPP_NONE)
def test_wildcards(self):
- import loxi.of10 as ofp
self.assertEquals(0xfc000, ofp.OFPFW_NW_DST_MASK)
class TestCommon(unittest.TestCase):
def test_port_desc_pack(self):
- import loxi.of10 as ofp
obj = ofp.port_desc(port_no=ofp.OFPP_CONTROLLER,
hw_addr=[1,2,3,4,5,6],
name="foo",
@@ -205,7 +187,6 @@
self.assertEquals(expected, obj.pack())
def test_port_desc_unpack(self):
- import loxi.of10 as ofp
buf = ''.join([
'\xff\xfd', # port_no
'\x01\x02\x03\x04\x05\x06', # hw_addr
@@ -223,7 +204,6 @@
self.assertEquals(ofp.OFPPF_PAUSE_ASYM, obj.peer)
def test_table_stats_entry_pack(self):
- import loxi.of10 as ofp
obj = ofp.table_stats_entry(table_id=3,
name="foo",
wildcards=ofp.OFPFW_ALL,
@@ -244,7 +224,6 @@
self.assertEquals(expected, obj.pack())
def test_table_stats_entry_unpack(self):
- import loxi.of10 as ofp
buf = ''.join([
'\x03', # table_id
'\x00\x00\x00', # pad
@@ -261,7 +240,6 @@
self.assertEquals(9300233470495232273L, obj.matched_count)
def test_flow_stats_entry_pack(self):
- import loxi.of10 as ofp
obj = ofp.flow_stats_entry(table_id=3,
match=ofp.match(),
duration_sec=1,
@@ -301,7 +279,6 @@
self.assertEquals(expected, obj.pack())
def test_flow_stats_entry_unpack(self):
- import loxi.of10 as ofp
buf = ''.join([
'\x00\x68', # length
'\x03', # table_id
@@ -334,7 +311,6 @@
self.assertEquals(2, obj.actions[1].port)
def test_match(self):
- import loxi.of10 as ofp
match = ofp.match()
self.assertEquals(match.wildcards, ofp.OFPFW_ALL)
self.assertEquals(match.tcp_src, 0)
@@ -344,8 +320,6 @@
class TestMessages(unittest.TestCase):
def test_hello_construction(self):
- import loxi.of10 as ofp
-
msg = ofp.message.hello()
self.assertEquals(msg.version, ofp.OFP_VERSION)
self.assertEquals(msg.type, ofp.OFPT_HELLO)
@@ -359,8 +333,6 @@
self.assertEquals(msg.xid, 0)
def test_hello_unpack(self):
- import loxi.of10 as ofp
-
# Normal case
buf = "\x01\x00\x00\x08\x12\x34\x56\x78"
msg = ofp.message.hello(xid=0x12345678)
@@ -372,13 +344,10 @@
ofp.message.hello.unpack(buf)
def test_echo_request_construction(self):
- import loxi.of10 as ofp
msg = ofp.message.echo_request(data="abc")
self.assertEquals(msg.data, "abc")
def test_echo_request_pack(self):
- import loxi.of10 as ofp
-
msg = ofp.message.echo_request(xid=0x12345678, data="abc")
buf = msg.pack()
self.assertEquals(buf, "\x01\x02\x00\x0b\x12\x34\x56\x78\x61\x62\x63")
@@ -387,8 +356,6 @@
self.assertEquals(msg, msg2)
def test_echo_request_unpack(self):
- import loxi.of10 as ofp
-
# Normal case
buf = "\x01\x02\x00\x0b\x12\x34\x56\x78\x61\x62\x63"
msg = ofp.message.echo_request(xid=0x12345678, data="abc")
@@ -400,8 +367,6 @@
ofp.message.echo_request.unpack(buf)
def test_echo_request_equality(self):
- import loxi.of10 as ofp
-
msg = ofp.message.echo_request(xid=0x12345678, data="abc")
msg2 = ofp.message.echo_request(xid=0x12345678, data="abc")
#msg2 = ofp.message.echo_request.unpack(msg.pack())
@@ -416,13 +381,11 @@
msg2.data = msg.data
def test_echo_request_show(self):
- import loxi.of10 as ofp
expected = "echo_request { xid = 0x12345678, data = 'ab\\x01' }"
msg = ofp.message.echo_request(xid=0x12345678, data="ab\x01")
self.assertEquals(msg.show(), expected)
def test_flow_add(self):
- import loxi.of10 as ofp
match = ofp.match()
msg = ofp.message.flow_add(xid=1,
match=match,
@@ -439,7 +402,6 @@
self.assertEquals(msg, msg2)
def test_port_mod_pack(self):
- import loxi.of10 as ofp
msg = ofp.message.port_mod(xid=2,
port_no=ofp.OFPP_CONTROLLER,
hw_addr=[1,2,3,4,5,6],
@@ -450,7 +412,6 @@
self.assertEquals(expected, msg.pack())
def test_desc_stats_reply_pack(self):
- import loxi.of10 as ofp
msg = ofp.message.desc_stats_reply(xid=3,
flags=ofp.OFPSF_REPLY_MORE,
mfr_desc="The Indigo-2 Community",
@@ -473,7 +434,6 @@
self.assertEquals(expected, msg.pack())
def test_desc_stats_reply_unpack(self):
- import loxi.of10 as ofp
buf = ''.join([
'\x01', '\x11', # version/type
'\x04\x2c', # length
@@ -493,8 +453,6 @@
self.assertEquals(ofp.OFPSF_REPLY_MORE, msg.flags)
def test_port_status_pack(self):
- import loxi.of10 as ofp
-
desc = ofp.port_desc(port_no=ofp.OFPP_CONTROLLER,
hw_addr=[1,2,3,4,5,6],
name="foo",
@@ -527,7 +485,6 @@
self.assertEquals(expected, msg.pack())
def test_port_status_unpack(self):
- import loxi.of10 as ofp
buf = ''.join([
'\x01', '\x0c', # version/type
'\x00\x40', # length
@@ -549,7 +506,6 @@
self.assertEquals(ofp.OFPPF_PAUSE_ASYM, msg.desc.peer)
def test_port_stats_reply_pack(self):
- import loxi.of10 as ofp
msg = ofp.message.port_stats_reply(xid=5, flags=0, entries=[
ofp.port_stats_entry(port_no=1, rx_packets=56, collisions=5),
ofp.port_stats_entry(port_no=ofp.OFPP_LOCAL, rx_packets=1, collisions=1)])
@@ -591,7 +547,6 @@
self.assertEquals(expected, msg.pack())
def test_port_stats_reply_unpack(self):
- import loxi.of10 as ofp
buf = ''.join([
'\x01', '\x11', # version/type
'\x00\xdc', # length
@@ -688,7 +643,6 @@
])
def test_flow_stats_reply_pack(self):
- import loxi.of10 as ofp
msg = ofp.message.flow_stats_reply(xid=6, flags=0, entries=[
ofp.flow_stats_entry(table_id=3,
match=ofp.match(),
@@ -718,7 +672,6 @@
self.assertEquals(self.sample_flow_stats_reply_buf, msg.pack())
def test_flow_stats_reply_unpack(self):
- import loxi.of10 as ofp
msg = ofp.message.flow_stats_reply.unpack(self.sample_flow_stats_reply_buf)
self.assertEquals(ofp.OFPST_FLOW, msg.stats_type)
self.assertEquals(2, len(msg.entries))
@@ -726,7 +679,6 @@
self.assertEquals(3, len(msg.entries[1].actions))
def test_flow_add_show(self):
- import loxi.of10 as ofp
expected = """\
flow_add {
xid = None,
@@ -737,11 +689,11 @@
eth_dst = cd:ef:01:23:45:67,
vlan_vid = 0x0,
vlan_pcp = 0x0,
- pad1 = 0x0,
+ pad = 0x0,
eth_type = 0x0,
ip_dscp = 0x0,
ip_proto = 0x0,
- pad2 = [ 0, 0 ],
+ pad1 = [ 0, 0 ],
ipv4_src = 192.168.3.127,
ipv4_dst = 255.255.255.255,
tcp_src = 0x0,
@@ -756,7 +708,7 @@
flags = 0x0,
actions = [
output { port = OFPP_FLOOD, max_len = 0x0 },
- nicira_dec_ttl { pad = 0x0, pad2 = 0x0 },
+ nicira_dec_ttl { pad = 0x0, pad1 = 0x0 },
bsn_set_tunnel_dst { dst = 0x0 }
]
}"""
@@ -793,7 +745,6 @@
])
def test_packet_out_pack(self):
- import loxi.of10 as ofp
msg = ofp.message.packet_out(
xid=0x12345678,
buffer_id=0xabcdef01,
@@ -805,7 +756,6 @@
self.assertEquals(self.sample_packet_out_buf, msg.pack())
def test_packet_out_unpack(self):
- import loxi.of10 as ofp
msg = ofp.message.packet_out.unpack(self.sample_packet_out_buf)
self.assertEquals(0x12345678, msg.xid)
self.assertEquals(0xabcdef01, msg.buffer_id)
@@ -828,7 +778,6 @@
])
def test_packet_in_pack(self):
- import loxi.of10 as ofp
msg = ofp.message.packet_in(
xid=0x12345678,
buffer_id=0xabcdef01,
@@ -839,7 +788,6 @@
self.assertEquals(self.sample_packet_in_buf, msg.pack())
def test_packet_in_unpack(self):
- import loxi.of10 as ofp
msg = ofp.message.packet_in.unpack(self.sample_packet_in_buf)
self.assertEquals(0x12345678, msg.xid)
self.assertEquals(0xabcdef01, msg.buffer_id)
@@ -878,7 +826,6 @@
])
def test_queue_get_config_reply_pack(self):
- import loxi.of10 as ofp
msg = ofp.message.queue_get_config_reply(
xid=0x12345678,
port=ofp.OFPP_LOCAL,
@@ -891,7 +838,6 @@
self.assertEquals(self.sample_queue_get_config_reply_buf, msg.pack())
def test_queue_get_config_reply_unpack(self):
- import loxi.of10 as ofp
msg = ofp.message.queue_get_config_reply.unpack(self.sample_queue_get_config_reply_buf)
self.assertEquals(ofp.OFPP_LOCAL, msg.port)
self.assertEquals(msg.queues[0].queue_id, 1)
@@ -903,7 +849,6 @@
class TestParse(unittest.TestCase):
def test_parse_header(self):
import loxi
- import loxi.of10 as ofp
msg_ver, msg_type, msg_len, msg_xid = ofp.message.parse_header("\x01\x04\xAF\xE8\x12\x34\x56\x78")
self.assertEquals(1, msg_ver)
@@ -943,7 +888,6 @@
util.unpack_array(str, 3, "abcdefgh")
def test_pretty_wildcards(self):
- import loxi.of10 as ofp
self.assertEquals("OFPFW_ALL", ofp.util.pretty_wildcards(ofp.OFPFW_ALL))
self.assertEquals("0", ofp.util.pretty_wildcards(0))
self.assertEquals("OFPFW_DL_SRC|OFPFW_DL_DST",
@@ -963,7 +907,6 @@
"""
def setUp(self):
- import loxi.of10 as ofp
mods = [ofp.action,ofp.message,ofp.common]
self.klasses = [klass for mod in mods
for klass in mod.__dict__.values()
@@ -971,7 +914,6 @@
self.klasses.sort(key=lambda x: str(x))
def test_serialization(self):
- import loxi.of10 as ofp
expected_failures = []
for klass in self.klasses:
def fn():
@@ -986,7 +928,6 @@
fn()
def test_show(self):
- import loxi.of10 as ofp
expected_failures = []
for klass in self.klasses:
def fn():