loxi_frontend: move classes only used by c to c_gen
- flags
- identifiers
- match
- translation
- type_maps
were only used by the c backend. I'm moving them to c_gen to
unclutter the loxi_front_end space (and updating references
in the c backend)
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index 798f5a4..e35ee8d 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -34,13 +34,11 @@
import of_g
import c_match
from generic_utils import *
-import c_gen.c_type_maps as c_type_maps
-import loxi_front_end.type_maps as type_maps
-import loxi_front_end.flags as flags
-import loxi_utils.loxi_utils as loxi_utils
-import loxi_front_end.identifiers as identifiers
+from c_gen import flags, type_maps, c_type_maps
+import c_gen.loxi_utils_legacy as loxi_utils
+from c_gen.loxi_utils_legacy import config_check
-# 'property' is for queues. Could be trouble
+import c_gen.identifiers as identifiers
################################################################
diff --git a/c_gen/c_dump_gen.py b/c_gen/c_dump_gen.py
index 837ff78..9d487e2 100644
--- a/c_gen/c_dump_gen.py
+++ b/c_gen/c_dump_gen.py
@@ -34,12 +34,12 @@
import sys
import of_g
-import loxi_front_end.match as match
-import loxi_front_end.flags as flags
+import c_gen.match as match
+import c_gen.flags as flags
from generic_utils import *
-import loxi_front_end.type_maps as type_maps
-import loxi_utils.loxi_utils as loxi_utils
-import loxi_front_end.identifiers as identifiers
+import c_gen.type_maps as type_maps
+import c_gen.loxi_utils_legacy as loxi_utils
+import c_gen.identifiers as identifiers
from c_test_gen import var_name_map
def gen_obj_dump_h(out, name):
diff --git a/c_gen/c_match.py b/c_gen/c_match.py
index 4001612..a45090a 100644
--- a/c_gen/c_match.py
+++ b/c_gen/c_match.py
@@ -40,8 +40,8 @@
# takes mask
import sys
-import of_g
-import loxi_front_end.match as match
+import c_gen.of_g_legacy as of_g
+import c_gen.match as match
import c_code_gen
def match_c_top_matter(out, name):
diff --git a/c_gen/c_show_gen.py b/c_gen/c_show_gen.py
index 0ec81b4..fe3e3dc 100644
--- a/c_gen/c_show_gen.py
+++ b/c_gen/c_show_gen.py
@@ -34,12 +34,13 @@
import sys
import of_g
-import loxi_front_end.match as match
-import loxi_front_end.flags as flags
+import c_gen.match as match
+import c_gen.flags as flags
from generic_utils import *
-import loxi_front_end.type_maps as type_maps
+import c_gen.type_maps as type_maps
import loxi_utils.loxi_utils as loxi_utils
-import loxi_front_end.identifiers as identifiers
+import c_gen.loxi_utils_legacy as loxi_utils
+import c_gen.identifiers as identifiers
from c_test_gen import var_name_map
def gen_obj_show_h(out, name):
diff --git a/c_gen/c_test_gen.py b/c_gen/c_test_gen.py
index d237cfe..c9c8245 100644
--- a/c_gen/c_test_gen.py
+++ b/c_gen/c_test_gen.py
@@ -60,12 +60,12 @@
import sys
import of_g
-import loxi_front_end.match as match
-import loxi_front_end.flags as flags
+import c_gen.match as match
+import c_gen.flags as flags
from generic_utils import *
-import loxi_front_end.type_maps as type_maps
-import loxi_utils.loxi_utils as loxi_utils
-import loxi_front_end.identifiers as identifiers
+import c_gen.type_maps as type_maps
+import c_gen.loxi_utils_legacy as loxi_utils
+import c_gen.identifiers as identifiers
import util
import test_data
diff --git a/c_gen/c_type_maps.py b/c_gen/c_type_maps.py
index 8883193..69dc804 100644
--- a/c_gen/c_type_maps.py
+++ b/c_gen/c_type_maps.py
@@ -32,7 +32,7 @@
import of_g
import sys
from generic_utils import *
-import loxi_front_end.type_maps as type_maps
+import c_gen.type_maps as type_maps
# Some number larger than small type values, but less then
diff --git a/c_gen/c_validator_gen.py b/c_gen/c_validator_gen.py
index 2930724..e799918 100644
--- a/c_gen/c_validator_gen.py
+++ b/c_gen/c_validator_gen.py
@@ -34,12 +34,13 @@
import sys
import of_g
-import loxi_front_end.match as match
-import loxi_front_end.flags as flags
+import c_gen.match as match
+import c_gen.flags as flags
from generic_utils import *
-import loxi_front_end.type_maps as type_maps
+import c_gen.type_maps as type_maps
import loxi_utils.loxi_utils as loxi_utils
-import loxi_front_end.identifiers as identifiers
+import c_gen.loxi_utils_legacy as loxi_utils
+import c_gen.identifiers as identifiers
from c_test_gen import var_name_map
from c_code_gen import v3_match_offset_get
diff --git a/c_gen/flags.py b/c_gen/flags.py
new file mode 100644
index 0000000..1fa4ae5
--- /dev/null
+++ b/c_gen/flags.py
@@ -0,0 +1,75 @@
+# 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.
+
+"""
+This file needs significant work and normalization. We need a better
+representation for flags, associating them to variables, and associating
+them to OF versions.
+
+@fixme Most of this will be going away soon
+"""
+
+import sys
+import copy
+import type_maps
+import c_gen.of_g_legacy as of_g
+import re
+
+# These mark idents as _not_ flags and have precedence
+non_flag_rules = [
+ "OF_CONFIG_FRAG_NORMAL",
+ "OF_FLOW_MOD_FAILED_BAD_FLAGS",
+ "OF_SWITCH_CONFIG_FAILED_BAD_FLAGS",
+ "OF_PORT_STATE_FLAG_STP_LISTEN",
+ "OF_TABLE_CONFIG_TABLE_MISS_CONTROLLER",
+ ]
+
+# These mark idents as flags
+flag_rules = [
+ "OF_CONFIG_",
+ "OF_TABLE_CONFIG_",
+ ]
+
+def ident_is_flag(ident):
+ """
+ Return True if ident should be treated as a flag
+ """
+
+ # Do negative matches first
+ for entry in non_flag_rules:
+ if re.match(entry, ident):
+ return False
+
+ # General rule, if it says flag it is (unless indicated above)
+ if ident.find("FLAG") >= 0:
+ return True
+
+ for entry in flag_rules:
+ if re.match(entry, ident):
+ return True
+
+ return False
diff --git a/c_gen/identifiers.py b/c_gen/identifiers.py
new file mode 100644
index 0000000..5862967
--- /dev/null
+++ b/c_gen/identifiers.py
@@ -0,0 +1,83 @@
+# 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.
+
+##
+# @brief Process identifiers for updating of_g.identifiers
+#
+
+import sys
+from generic_utils import *
+
+##
+# The value to use when an identifier is not defined for a version
+UNDEFINED_IDENT_VALUE = 0
+
+def add_identifier(name, ofp_name, ofp_group, value, version, all_idents, idents_by_group):
+ assert(isinstance(value, (int,long)))
+ if name in all_idents:
+ all_idents[name]["values_by_version"][version] = value
+ if ((all_idents[name]["ofp_name"] != ofp_name or
+ all_idents[name]["ofp_group"] != ofp_group) and
+ ofp_name.find("OFPP_") != 0):
+ log("""
+NOTE: Identifier %s has different ofp name or group in version %s
+From ofp name %s, group %s to name %s, group %s.
+This could indicate a name collision in LOXI identifier translation.
+""" % (name, str(version), all_idents[name]["ofp_name"],
+ all_idents[name]["ofp_group"], ofp_name, ofp_group))
+ # Update stuff assuming newer versions processed later
+ all_idents[name]["ofp_name"] = ofp_name
+ all_idents[name]["ofp_group"] = ofp_group
+
+ else: # New name
+ all_idents[name] = dict(
+ values_by_version = {version:value},
+ common_value = value,
+ ofp_name = ofp_name,
+ ofp_group = ofp_group
+ )
+ if ofp_group not in idents_by_group:
+ idents_by_group[ofp_group] = []
+ if name not in idents_by_group[ofp_group]:
+ idents_by_group[ofp_group].append(name)
+
+def all_versions_agree(all_idents, version_list, name):
+ val_list = all_idents[name]["values_by_version"]
+ for version in version_list:
+ if not version in val_list:
+ return False
+ if str(val_list[version]) != str(all_idents[name]["common_value"]):
+ return False
+ return True
+
+def defined_versions_agree(all_idents, version_list, name):
+ val_list = all_idents[name]["values_by_version"]
+ for version in version_list:
+ if version in val_list:
+ if str(val_list[version]) != str(all_idents[name]["common_value"]):
+ return False
+ return True
diff --git a/c_gen/loci_utils.py b/c_gen/loci_utils.py
new file mode 100644
index 0000000..bc3092f
--- /dev/null
+++ b/c_gen/loci_utils.py
@@ -0,0 +1,267 @@
+import c_gen.of_g_legacy as of_g
+
+def class_signature(members):
+ """
+ Generate a signature string for a class in canonical form
+
+ @param cls The class whose signature is to be generated
+ """
+ return ";".join([",".join([x["m_type"], x["name"], str(x["offset"])])
+ for x in members])
+
+def type_dec_to_count_base(m_type):
+ """
+ Resolve a type declaration like uint8_t[4] to a count (4) and base_type
+ (uint8_t)
+
+ @param m_type The string type declaration to process
+ """
+ count = 1
+ chk_ar = m_type.split('[')
+ if len(chk_ar) > 1:
+ count_str = chk_ar[1].split(']')[0]
+ if count_str in of_g.ofp_constants:
+ count = of_g.ofp_constants[count_str]
+ else:
+ count = int(count_str)
+ base_type = chk_ar[0]
+ else:
+ base_type = m_type
+ return count, base_type
+
+def list_to_entry_type(cls):
+ """
+ Return the entry type for a list
+ """
+ slen = len("of_list_")
+ return "of_" + cls[slen:]
+
+def type_to_short_name(m_type):
+ if m_type in of_g.of_base_types:
+ tname = of_g.of_base_types[m_type]["short_name"]
+ elif m_type in of_g.of_mixed_types:
+ tname = of_g.of_mixed_types[m_type]["short_name"]
+ else:
+ tname = "unknown"
+ return tname
+
+def type_to_name_type(cls, member_name):
+ """
+ Generate the root name of a member for accessor functions, etc
+ @param cls The class name
+ @param member_name The member name
+ """
+ members = of_g.unified[cls]["union"]
+ if not member_name in members:
+ debug("Error: %s is not in class %s for acc_name defn" %
+ (member_name, cls))
+ os.exit()
+
+ mem = members[member_name]
+ m_type = mem["m_type"]
+ id = mem["memid"]
+ tname = type_to_short_name(m_type)
+
+ return "o%d_m%d_%s" % (of_g.unified[cls]["object_id"], id, tname)
+
+
+def member_to_index(m_name, members):
+ """
+ Given a member name, return the index in the members dict
+ @param m_name The name of the data member to search for
+ @param members The dict of members
+ @return Index if found, -1 not found
+
+ Note we could generate an index when processing the original input
+ """
+ count = 0
+ for d in members:
+ if d["name"] == m_name:
+ return count
+ count += 1
+ return -1
+
+def member_base_type(cls, m_name):
+ """
+ Map a member to its of_ type
+ @param cls The class name
+ @param m_name The name of the member being gotten
+ @return The of_ type of the member
+ """
+ rv = of_g.unified[cls]["union"][m_name]["m_type"]
+ if rv[-2:] == "_t":
+ return rv
+ return rv + "_t"
+
+def member_type_is_octets(cls, m_name):
+ return member_base_type(cls, m_name) == "of_octets_t"
+
+def member_returns_val(cls, m_name):
+ """
+ Should get accessor return a value rather than void
+ @param cls The class name
+ @param m_name The member name
+ @return True if of_g config and the specific member allow a
+ return value. Otherwise False
+ """
+ m_type = of_g.unified[cls]["union"][m_name]["m_type"]
+ return (config_check("get_returns") =="value" and
+ m_type in of_g.of_scalar_types)
+
+def config_check(str, dictionary = of_g.code_gen_config):
+ """
+ Return config value if in dictionary; else return False.
+ @param str The lookup index
+ @param dictionary The dict to check; use code_gen_config if None
+ """
+
+ if str in dictionary:
+ return dictionary[str]
+
+ return False
+
+def h_file_to_define(name):
+ """
+ Convert a .h file name to the define used for the header
+ """
+ h_name = name[:-2].upper()
+ h_name = "_" + h_name + "_H_"
+ return h_name
+
+def type_to_cof_type(m_type):
+ if m_type in of_g.of_base_types:
+ if "cof_type" in of_g.of_base_types[m_type]:
+ return of_g.of_base_types[m_type]["cof_type"]
+ return m_type
+
+
+def member_is_scalar(cls, m_name):
+ return of_g.unified[cls]["union"][m_name]["m_type"] in of_g.of_scalar_types
+
+def type_is_scalar(m_type):
+ return m_type in of_g.of_scalar_types
+
+def skip_member_name(name):
+ return name.find("pad") == 0 or name in of_g.skip_members
+
+def enum_name(cls):
+ """
+ Return the name used for an enum identifier for the given class
+ @param cls The class name
+ """
+ return cls.upper()
+
+def class_in_version(cls, ver):
+ """
+ Return boolean indicating if cls is defined for wire version ver
+ """
+
+ return (cls, ver) in of_g.base_length
+
+def instance_to_class(instance, parent):
+ """
+ Return the name of the class for an instance of inheritance type parent
+ """
+ return parent + "_" + instance
+
+def sub_class_to_var_name(cls):
+ """
+ Given a subclass name like of_action_output, generate the
+ name of a variable like 'output'
+ @param cls The class name
+ """
+ pass
+
+def class_is_var_len(cls, version):
+ # Match is special case. Only version 1.2 (wire version 3) is var
+ if cls == "of_match":
+ return version == 3
+
+ return not (cls, version) in of_g.is_fixed_length
+
+def base_type_to_length(base_type, version):
+ if base_type + "_t" in of_g.of_base_types:
+ inst_len = of_g.of_base_types[base_type + "_t"]["bytes"]
+ else:
+ inst_len = of_g.base_length[(base_type, version)]
+
+def version_to_name(version):
+ """
+ Convert an integer version to the C macro name
+ """
+ return "OF_" + of_g.version_names[version]
+
+##
+# Is class a flow modify of some sort?
+
+def cls_is_flow_mod(cls):
+ return cls in ["of_flow_mod", "of_flow_modify", "of_flow_add", "of_flow_delete",
+ "of_flow_modify_strict", "of_flow_delete_strict"]
+
+def all_member_types_get(cls, version):
+ """
+ Get the members and list of types for members of a given class
+ @param cls The class name to process
+ @param version The version for the class
+ """
+ member_types = []
+
+ if not version in of_g.unified[cls]:
+ return ([], [])
+
+ if "use_version" in of_g.unified[cls][version]:
+ v = of_g.unified[cls][version]["use_version"]
+ members = of_g.unified[cls][v]["members"]
+ else:
+ members = of_g.unified[cls][version]["members"]
+ # Accumulate variables that are supported
+ for member in members:
+ m_type = member["m_type"]
+ m_name = member["name"]
+ if skip_member_name(m_name):
+ continue
+ if not m_type in member_types:
+ member_types.append(m_type)
+
+ return (members, member_types)
+
+def list_name_extract(list_type):
+ """
+ Return the base name for a list object of the given type
+ @param list_type The type of the list as appears in the input,
+ for example list(of_port_desc_t).
+ @return A pair, (list-name, base-type) where list-name is the
+ base name for the list, for example of_list_port_desc, and base-type
+ is the type of list elements like of_port_desc_t
+ """
+ base_type = list_type[5:-1]
+ list_name = base_type
+ if list_name.find("of_") == 0:
+ list_name = list_name[3:]
+ if list_name[-2:] == "_t":
+ list_name = list_name[:-2]
+ list_name = "of_list_" + list_name
+ return (list_name, base_type)
+
+def version_to_name(version):
+ """
+ Convert an integer version to the C macro name
+ """
+ return "OF_" + of_g.version_names[version]
+
+def gen_c_copy_license(out):
+ """
+ Generate the top comments for copyright and license
+ """
+ import c_gen.util
+ c_gen.util.render_template(out, '_copyright.c')
+
+def accessor_returns_error(a_type, m_type):
+ is_var_len = (not type_is_scalar(m_type)) and \
+ [x for x in of_g.of_version_range if class_is_var_len(m_type[:-2], x)] != []
+ if a_type == "set" and is_var_len:
+ return True
+ elif m_type == "of_match_t":
+ return True
+ else:
+ return False
diff --git a/c_gen/loxi_utils_legacy.py b/c_gen/loxi_utils_legacy.py
new file mode 100644
index 0000000..47b1430
--- /dev/null
+++ b/c_gen/loxi_utils_legacy.py
@@ -0,0 +1,545 @@
+# 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.
+
+"""
+@brief Utilities involving LOXI naming conventions
+
+Utility functions for OpenFlow class generation
+
+These may need to be sorted out into language specific functions
+"""
+
+import sys
+import c_gen.of_g_legacy as of_g
+import tenjin
+from generic_utils import find, memoize
+
+def class_signature(members):
+ """
+ Generate a signature string for a class in canonical form
+
+ @param cls The class whose signature is to be generated
+ """
+ return ";".join([",".join([x["m_type"], x["name"], str(x["offset"])])
+ for x in members])
+
+def type_dec_to_count_base(m_type):
+ """
+ Resolve a type declaration like uint8_t[4] to a count (4) and base_type
+ (uint8_t)
+
+ @param m_type The string type declaration to process
+ """
+ count = 1
+ chk_ar = m_type.split('[')
+ if len(chk_ar) > 1:
+ count_str = chk_ar[1].split(']')[0]
+ if count_str in of_g.ofp_constants:
+ count = of_g.ofp_constants[count_str]
+ else:
+ count = int(count_str)
+ base_type = chk_ar[0]
+ else:
+ base_type = m_type
+ return count, base_type
+
+##
+# Class types:
+#
+# Virtual
+# A virtual class is one which does not have an explicit wire
+# representation. For example, an inheritance super class
+# or a list type.
+#
+# List
+# A list of objects of some other type
+#
+# TLV16
+# The wire represenation starts with 16-bit type and length fields
+#
+# OXM
+# An extensible match object
+#
+# Message
+# A top level OpenFlow message
+#
+#
+
+def class_is_message(cls):
+ """
+ Return True if cls is a message object based on info in unified
+ """
+ return "xid" in of_g.unified[cls]["union"] and cls != "of_header"
+
+def class_is_tlv16(cls):
+ """
+ Return True if cls_name is an object which uses uint16 for type and length
+ """
+ if cls.find("of_action") == 0: # Includes of_action_id classes
+ return True
+ if cls.find("of_instruction") == 0:
+ return True
+ if cls.find("of_queue_prop") == 0:
+ return True
+ if cls.find("of_table_feature_prop") == 0:
+ return True
+ # *sigh*
+ if cls.find("of_meter_band_stats") == 0: # NOT A TLV
+ return False
+ if cls.find("of_meter_band") == 0:
+ return True
+ if cls.find("of_hello_elem") == 0:
+ return True
+ if cls == "of_match_v3":
+ return True
+ if cls == "of_match_v4":
+ return True
+ return False
+
+def class_is_u16_len(cls):
+ """
+ Return True if cls_name is an object which uses initial uint16 length
+ """
+ return cls in ["of_group_desc_stats_entry", "of_group_stats_entry",
+ "of_flow_stats_entry", "of_bucket", "of_table_features"]
+
+def class_is_oxm(cls):
+ """
+ Return True if cls_name is an OXM object
+ """
+ if cls.find("of_oxm") == 0:
+ return True
+ return False
+
+def class_is_action(cls):
+ """
+ Return True if cls_name is an action object
+
+ Note that action_id is not an action object, though it has
+ the same header. It looks like an action header, but the type
+ is used to identify a kind of action, it does not indicate the
+ type of the object following.
+ """
+ if cls.find("of_action_id") == 0:
+ return False
+ if cls.find("of_action") == 0:
+ return True
+
+ # For each vendor, check for vendor specific action
+ for exp in of_g.experimenter_name_to_id:
+ if cls.find("of_action" + exp) == 0:
+ return True
+
+ return False
+
+def class_is_action_id(cls):
+ """
+ Return True if cls_name is an action object
+
+ Note that action_id is not an action object, though it has
+ the same header. It looks like an action header, but the type
+ is used to identify a kind of action, it does not indicate the
+ type of the object following.
+ """
+ if cls.find("of_action_id") == 0:
+ return True
+
+ # For each vendor, check for vendor specific action
+ for exp in of_g.experimenter_name_to_id:
+ if cls.find("of_action_id_" + exp) == 0:
+ return True
+
+ return False
+
+def class_is_instruction(cls):
+ """
+ Return True if cls_name is an instruction object
+ """
+ if cls.find("of_instruction") == 0:
+ return True
+
+ # For each vendor, check for vendor specific action
+ for exp in of_g.experimenter_name_to_id:
+ if cls.find("of_instruction_" + exp) == 0:
+ return True
+
+ return False
+
+def class_is_meter_band(cls):
+ """
+ Return True if cls_name is an instruction object
+ """
+ # meter_band_stats is not a member of meter_band class hierarchy
+ if cls.find("of_meter_band_stats") == 0:
+ return False
+ if cls.find("of_meter_band") == 0:
+ return True
+ return False
+
+def class_is_hello_elem(cls):
+ """
+ Return True if cls_name is an instruction object
+ """
+ if cls.find("of_hello_elem") == 0:
+ return True
+ return False
+
+def class_is_queue_prop(cls):
+ """
+ Return True if cls_name is a queue_prop object
+ """
+ if cls.find("of_queue_prop") == 0:
+ return True
+
+ # For each vendor, check for vendor specific action
+ for exp in of_g.experimenter_name_to_id:
+ if cls.find("of_queue_prop_" + exp) == 0:
+ return True
+
+ return False
+
+def class_is_table_feature_prop(cls):
+ """
+ Return True if cls_name is a queue_prop object
+ """
+ if cls.find("of_table_feature_prop") == 0:
+ return True
+ return False
+
+def class_is_stats_message(cls):
+ """
+ Return True if cls_name is a message object based on info in unified
+ """
+
+ return "stats_type" in of_g.unified[cls]["union"]
+
+def class_is_list(cls):
+ """
+ Return True if cls_name is a list object
+ """
+ return (cls.find("of_list_") == 0)
+
+def type_is_of_object(m_type):
+ """
+ Return True if m_type is an OF object type
+ """
+ # Remove _t from the type id and see if key for unified class
+ if m_type[-2:] == "_t":
+ m_type = m_type[:-2]
+ return m_type in of_g.unified
+
+def list_to_entry_type(cls):
+ """
+ Return the entry type for a list
+ """
+ slen = len("of_list_")
+ return "of_" + cls[slen:]
+
+def type_to_short_name(m_type):
+ if m_type in of_g.of_base_types:
+ tname = of_g.of_base_types[m_type]["short_name"]
+ elif m_type in of_g.of_mixed_types:
+ tname = of_g.of_mixed_types[m_type]["short_name"]
+ else:
+ tname = "unknown"
+ return tname
+
+def type_to_name_type(cls, member_name):
+ """
+ Generate the root name of a member for accessor functions, etc
+ @param cls The class name
+ @param member_name The member name
+ """
+ members = of_g.unified[cls]["union"]
+ if not member_name in members:
+ debug("Error: %s is not in class %s for acc_name defn" %
+ (member_name, cls))
+ os.exit()
+
+ mem = members[member_name]
+ m_type = mem["m_type"]
+ id = mem["memid"]
+ tname = type_to_short_name(m_type)
+
+ return "o%d_m%d_%s" % (of_g.unified[cls]["object_id"], id, tname)
+
+
+def member_to_index(m_name, members):
+ """
+ Given a member name, return the index in the members dict
+ @param m_name The name of the data member to search for
+ @param members The dict of members
+ @return Index if found, -1 not found
+
+ Note we could generate an index when processing the original input
+ """
+ count = 0
+ for d in members:
+ if d["name"] == m_name:
+ return count
+ count += 1
+ return -1
+
+def member_base_type(cls, m_name):
+ """
+ Map a member to its of_ type
+ @param cls The class name
+ @param m_name The name of the member being gotten
+ @return The of_ type of the member
+ """
+ rv = of_g.unified[cls]["union"][m_name]["m_type"]
+ if rv[-2:] == "_t":
+ return rv
+ return rv + "_t"
+
+def member_type_is_octets(cls, m_name):
+ return member_base_type(cls, m_name) == "of_octets_t"
+
+def member_returns_val(cls, m_name):
+ """
+ Should get accessor return a value rather than void
+ @param cls The class name
+ @param m_name The member name
+ @return True if of_g config and the specific member allow a
+ return value. Otherwise False
+ """
+ m_type = of_g.unified[cls]["union"][m_name]["m_type"]
+ return (config_check("get_returns") =="value" and
+ m_type in of_g.of_scalar_types)
+
+def config_check(str, dictionary = of_g.code_gen_config):
+ """
+ Return config value if in dictionary; else return False.
+ @param str The lookup index
+ @param dictionary The dict to check; use code_gen_config if None
+ """
+
+ if str in dictionary:
+ return dictionary[str]
+
+ return False
+
+def h_file_to_define(name):
+ """
+ Convert a .h file name to the define used for the header
+ """
+ h_name = name[:-2].upper()
+ h_name = "_" + h_name + "_H_"
+ return h_name
+
+def type_to_cof_type(m_type):
+ if m_type in of_g.of_base_types:
+ if "cof_type" in of_g.of_base_types[m_type]:
+ return of_g.of_base_types[m_type]["cof_type"]
+ return m_type
+
+
+def member_is_scalar(cls, m_name):
+ return of_g.unified[cls]["union"][m_name]["m_type"] in of_g.of_scalar_types
+
+def type_is_scalar(m_type):
+ return m_type in of_g.of_scalar_types
+
+def skip_member_name(name):
+ return name.find("pad") == 0 or name in of_g.skip_members
+
+def enum_name(cls):
+ """
+ Return the name used for an enum identifier for the given class
+ @param cls The class name
+ """
+ return cls.upper()
+
+def class_in_version(cls, ver):
+ """
+ Return boolean indicating if cls is defined for wire version ver
+ """
+
+ return (cls, ver) in of_g.base_length
+
+def instance_to_class(instance, parent):
+ """
+ Return the name of the class for an instance of inheritance type parent
+ """
+ return parent + "_" + instance
+
+def sub_class_to_var_name(cls):
+ """
+ Given a subclass name like of_action_output, generate the
+ name of a variable like 'output'
+ @param cls The class name
+ """
+ pass
+
+def class_is_var_len(cls, version):
+ # Match is special case. Only version 1.2 (wire version 3) is var
+ if cls == "of_match":
+ return version == 3
+
+ return not (cls, version) in of_g.is_fixed_length
+
+def base_type_to_length(base_type, version):
+ if base_type + "_t" in of_g.of_base_types:
+ inst_len = of_g.of_base_types[base_type + "_t"]["bytes"]
+ else:
+ inst_len = of_g.base_length[(base_type, version)]
+
+def version_to_name(version):
+ """
+ Convert an integer version to the C macro name
+ """
+ return "OF_" + of_g.version_names[version]
+
+##
+# Is class a flow modify of some sort?
+
+def cls_is_flow_mod(cls):
+ return cls in ["of_flow_mod", "of_flow_modify", "of_flow_add", "of_flow_delete",
+ "of_flow_modify_strict", "of_flow_delete_strict"]
+
+
+def all_member_types_get(cls, version):
+ """
+ Get the members and list of types for members of a given class
+ @param cls The class name to process
+ @param version The version for the class
+ """
+ member_types = []
+
+ if not version in of_g.unified[cls]:
+ return ([], [])
+
+ if "use_version" in of_g.unified[cls][version]:
+ v = of_g.unified[cls][version]["use_version"]
+ members = of_g.unified[cls][v]["members"]
+ else:
+ members = of_g.unified[cls][version]["members"]
+ # Accumulate variables that are supported
+ for member in members:
+ m_type = member["m_type"]
+ m_name = member["name"]
+ if skip_member_name(m_name):
+ continue
+ if not m_type in member_types:
+ member_types.append(m_type)
+
+ return (members, member_types)
+
+def list_name_extract(list_type):
+ """
+ Return the base name for a list object of the given type
+ @param list_type The type of the list as appears in the input,
+ for example list(of_port_desc_t).
+ @return A pair, (list-name, base-type) where list-name is the
+ base name for the list, for example of_list_port_desc, and base-type
+ is the type of list elements like of_port_desc_t
+ """
+ base_type = list_type[5:-1]
+ list_name = base_type
+ if list_name.find("of_") == 0:
+ list_name = list_name[3:]
+ if list_name[-2:] == "_t":
+ list_name = list_name[:-2]
+ list_name = "of_list_" + list_name
+ return (list_name, base_type)
+
+def version_to_name(version):
+ """
+ Convert an integer version to the C macro name
+ """
+ return "OF_" + of_g.version_names[version]
+
+def gen_c_copy_license(out):
+ """
+ Generate the top comments for copyright and license
+ """
+ import c_gen.util
+ c_gen.util.render_template(out, '_copyright.c')
+
+def accessor_returns_error(a_type, m_type):
+ is_var_len = (not type_is_scalar(m_type)) and \
+ [x for x in of_g.of_version_range if class_is_var_len(m_type[:-2], x)] != []
+ if a_type == "set" and is_var_len:
+ return True
+ elif m_type == "of_match_t":
+ return True
+ else:
+ return False
+
+def render_template(out, name, path, context, prefix = None):
+ """
+ Render a template using tenjin.
+ out: a file-like object
+ name: name of the template
+ path: array of directories to search for the template
+ context: dictionary of variables to pass to the template
+ prefix: optional prefix to use for embedding (for other languages than python)
+ """
+ pp = [ tenjin.PrefixedLinePreprocessor(prefix=prefix) if prefix else tenjin.PrefixedLinePreprocessor() ] # support "::" syntax
+ template_globals = { "to_str": str, "escape": str } # disable HTML escaping
+ engine = TemplateEngine(path=path, pp=pp)
+ out.write(engine.render(name, context, template_globals))
+
+def render_static(out, name, path):
+ """
+ Write out a static template.
+ out: a file-like object
+ name: name of the template
+ path: array of directories to search for the template
+ """
+ # Reuse the tenjin logic for finding the template
+ template_filename = tenjin.FileSystemLoader().find(name, path)
+ if not template_filename:
+ raise ValueError("template %s not found" % name)
+ with open(template_filename) as infile:
+ out.write(infile.read())
+
+@memoize
+def lookup_ir_wiretype(oftype, version):
+ """ if of is a reference to an enum in ir, resolve it to the wiretype
+ declared in that enum. Else return oftype """
+ enums = of_g.ir[version].enums
+ enum = find(lambda e: e.name == oftype, enums)
+ if enum and 'wire_type' in enum.params:
+ return enum.params['wire_type']
+ else:
+ return oftype
+
+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/c_gen/match.py b/c_gen/match.py
new file mode 100644
index 0000000..611a80d
--- /dev/null
+++ b/c_gen/match.py
@@ -0,0 +1,558 @@
+# 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.
+
+# @brief Match data representation
+#
+# @fixme This still has lots of C specific code that should be moved into c_gen
+
+import sys
+import c_gen.of_g_legacy as of_g
+from generic_utils import *
+import c_gen.loxi_utils_legacy as loxi_utils
+
+#
+# Use 1.2 match semantics for common case
+#
+# Generate maps between generic match and version specific matches
+# Generate dump functions for generic match
+# Generate dump functions for version specific matches
+
+## @var of_match_members
+# The dictionary from unified match members to type and indexing info
+#
+# Keys:
+# name The unified name used for the member
+# m_type The data type used for the object in unified structure
+# print_type The id to use when printing
+# conditions The condition underwhich the field could occur TBD
+# takes_mask_in_spec Shown as taking mask in OF 1.2 spec; IGNORED NOW
+# order Used to define an order for readability
+# v1_wc_shift The WC shift in OF 1.0
+# v2_wc_shift The WC shift in OF 1.1
+#
+# Unless noted otherwise, class is 0x8000, OFPXMC_OPENFLOW_BASIC
+# We use the 1.2 names and alias older names
+# Conditions:
+# is_ipv4(_m): ((_m)->eth_type == 0x0800)
+# is_ipv6(_m): ((_m)->eth_type == 0x86dd)
+# is_ip(_m): (is_ipv4(_m) || is_ipv6(_m))
+# is_arp(_m): ((_m)->eth_type == 0x0806)
+# is_tcp(_m): (is_ip(_m) && ((_m)->ip_proto == 6))
+# is_udp(_m): (is_ip(_m) && ((_m)->ip_proto == 17))
+# is_sctp(_m): (is_ip(_m) && ((_m)->ip_proto == 132))
+# is_icmpv4(_m): (is_ipv4(_m) && ((_m)->ip_proto == 1))
+# is_icmpv6(_m): (is_ipv6(_m) && ((_m)->ip_proto == 58))
+#
+
+of_match_members = dict(
+ in_port = dict(
+ name="in_port",
+ m_type="of_port_no_t",
+ print_type="PRIx32",
+ conditions="",
+ v1_wc_shift=0,
+ v2_wc_shift=0,
+ takes_mask_in_spec=False,
+ order=100,
+ ),
+ in_phy_port = dict(
+ name="in_phy_port",
+ m_type="of_port_no_t",
+ print_type="PRIx32",
+ conditions="", # OXM_OF_IN_PORT must be present
+ takes_mask_in_spec=False,
+ order=101,
+ ),
+ metadata = dict(
+ name="metadata",
+ m_type="uint64_t",
+ print_type="PRIx64",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=102,
+ ),
+
+ eth_dst = dict(
+ name="eth_dst",
+ m_type="of_mac_addr_t",
+ v1_wc_shift=3,
+ print_type="\"p\"",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=200,
+ ),
+ eth_src = dict(
+ name="eth_src",
+ m_type="of_mac_addr_t",
+ v1_wc_shift=2,
+ print_type="\"p\"",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=201,
+ ),
+ eth_type = dict(
+ name="eth_type",
+ m_type="uint16_t",
+ v1_wc_shift=4,
+ v2_wc_shift=3,
+ print_type="PRIx16",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=203,
+ ),
+ vlan_vid = dict( # FIXME: Semantics changed in 1.2
+ # Use CFI bit to indicate tag presence
+ name="vlan_vid",
+ m_type="uint16_t",
+ v1_wc_shift=1,
+ v2_wc_shift=1,
+ print_type="PRIx16",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=210,
+ ),
+ vlan_pcp = dict(
+ name="vlan_pcp",
+ m_type="uint8_t",
+ v1_wc_shift=20,
+ v2_wc_shift=2,
+ print_type="PRIx8",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=211,
+ ),
+
+ ip_dscp = dict(
+ name="ip_dscp",
+ m_type="uint8_t",
+ v1_wc_shift=21,
+ v2_wc_shift=4,
+ print_type="PRIx8",
+ conditions="is_ip(match)",
+ takes_mask_in_spec=False,
+ order=310,
+ ),
+ ip_ecn = dict(
+ name="ip_ecn",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_ip(match)",
+ takes_mask_in_spec=False,
+ order=311,
+ ),
+ ip_proto = dict(
+ name="ip_proto",
+ m_type="uint8_t",
+ v1_wc_shift=5,
+ v2_wc_shift=5,
+ print_type="PRIx8",
+ conditions="is_ip(match)",
+ takes_mask_in_spec=False,
+ order=320,
+ ),
+ ipv4_src = dict(
+ name="ipv4_src",
+ m_type="of_ipv4_t",
+ v1_wc_shift=8,
+ print_type="PRIx32",
+ conditions="is_ipv4(match)",
+ takes_mask_in_spec=True,
+ order=330,
+ ),
+ ipv4_dst = dict(
+ name="ipv4_dst",
+ m_type="of_ipv4_t",
+ v1_wc_shift=14,
+ print_type="PRIx32",
+ conditions="is_ipv4(match)",
+ takes_mask_in_spec=True,
+ order=331,
+ ),
+
+ tcp_dst = dict(
+ name="tcp_dst",
+ m_type="uint16_t",
+ v1_wc_shift=7,
+ v2_wc_shift=7,
+ print_type="PRIx16",
+ conditions="is_tcp(match)",
+ takes_mask_in_spec=False,
+ order=400,
+ ),
+ tcp_src = dict(
+ name="tcp_src",
+ m_type="uint16_t",
+ v1_wc_shift=6,
+ v2_wc_shift=6,
+ print_type="PRIx16",
+ conditions="is_tcp(match)",
+ takes_mask_in_spec=False,
+ order=401,
+ ),
+
+ udp_dst = dict(
+ name="udp_dst",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_udp(match)",
+ takes_mask_in_spec=False,
+ order=410,
+ ),
+ udp_src = dict(
+ name="udp_src",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_udp(match)",
+ takes_mask_in_spec=False,
+ order=411,
+ ),
+
+ sctp_dst = dict(
+ name="sctp_dst",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_sctp(match)",
+ takes_mask_in_spec=False,
+ order=420,
+ ),
+ sctp_src = dict(
+ name="sctp_src",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_sctp(match)",
+ takes_mask_in_spec=False,
+ order=421,
+ ),
+
+ icmpv4_type = dict(
+ name="icmpv4_type",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v4(match)",
+ takes_mask_in_spec=False,
+ order=430,
+ ),
+ icmpv4_code = dict(
+ name="icmpv4_code",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v4(match)",
+ takes_mask_in_spec=False,
+ order=431,
+ ),
+
+ arp_op = dict(
+ name="arp_op",
+ m_type="uint16_t",
+ print_type="PRIx16",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=False,
+ order=450,
+ ),
+
+ arp_spa = dict(
+ name="arp_spa",
+ m_type="uint32_t",
+ print_type="PRIx32",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=True,
+ order=451,
+ ),
+ arp_tpa = dict(
+ name="arp_tpa",
+ m_type="uint32_t",
+ print_type="PRIx32",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=True,
+ order=452,
+ ),
+
+ arp_sha = dict(
+ name="arp_sha",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=False,
+ order=453,
+ ),
+ arp_tha = dict(
+ name="arp_tha",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="is_arp(match)",
+ takes_mask_in_spec=False,
+ order=454,
+ ),
+
+ ipv6_src = dict(
+ name="ipv6_src",
+ m_type="of_ipv6_t",
+ print_type="\"p\"",
+ conditions="is_ipv6(match)",
+ takes_mask_in_spec=True,
+ order=500,
+ ),
+ ipv6_dst = dict(
+ name="ipv6_dst",
+ m_type="of_ipv6_t",
+ print_type="\"p\"",
+ conditions="is_ipv6(match)",
+ takes_mask_in_spec=True,
+ order=501,
+ ),
+
+ ipv6_flabel = dict(
+ name="ipv6_flabel",
+ m_type="uint32_t",
+ print_type="PRIx32",
+ conditions="is_ipv6(match)",
+ takes_mask_in_spec=False, # Comment in openflow.h says True
+ order=502,
+ ),
+
+ icmpv6_type = dict(
+ name="icmpv6_type",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v6(match)",
+ takes_mask_in_spec=False,
+ order=510,
+ ),
+ icmpv6_code = dict(
+ name="icmpv6_code",
+ m_type="uint8_t",
+ print_type="PRIx8",
+ conditions="is_icmp_v6(match)",
+ takes_mask_in_spec=False,
+ order=511,
+ ),
+
+ ipv6_nd_target = dict(
+ name="ipv6_nd_target",
+ m_type="of_ipv6_t",
+ print_type="\"p\"",
+ conditions="", # fixme
+ takes_mask_in_spec=False,
+ order=512,
+ ),
+
+ ipv6_nd_sll = dict(
+ name="ipv6_nd_sll",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="", # fixme
+ takes_mask_in_spec=False,
+ order=520,
+ ),
+ ipv6_nd_tll = dict(
+ name="ipv6_nd_tll",
+ m_type="of_mac_addr_t",
+ print_type="\"p\"",
+ conditions="", # fixme
+ takes_mask_in_spec=False,
+ order=521,
+ ),
+
+ mpls_label = dict(
+ name="mpls_label",
+ m_type="uint32_t",
+ v2_wc_shift=8,
+ print_type="PRIx32",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=600,
+ ),
+ mpls_tc = dict(
+ name="mpls_tc",
+ m_type="uint8_t",
+ v2_wc_shift=9,
+ print_type="PRIx8",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=601,
+ ),
+
+ bsn_in_ports_128 = dict(
+ name="bsn_in_ports_128",
+ m_type="of_bitmap_128_t",
+ v2_wc_shift=9,
+ print_type="p",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=1000,
+ ),
+
+ bsn_lag_id = dict(
+ name="bsn_lag_id",
+ m_type="uint32_t",
+ print_type="PRIu32",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=1001,
+ ),
+
+ bsn_vrf = dict(
+ name="bsn_vrf",
+ m_type="uint32_t",
+ print_type="PRIu32",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=1002,
+ ),
+
+ bsn_global_vrf_allowed = dict(
+ name="bsn_global_vrf_allowed",
+ m_type="uint8_t",
+ print_type="PRIu8",
+ conditions="",
+ takes_mask_in_spec=False,
+ order=1003,
+ ),
+
+ bsn_l3_interface_class_id = dict(
+ name="bsn_l3_interface_class_id",
+ m_type="uint32_t",
+ print_type="PRIu32",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=1003,
+ ),
+
+ bsn_l3_src_class_id = dict(
+ name="bsn_l3_src_class_id",
+ m_type="uint32_t",
+ print_type="PRIu32",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=1004,
+ ),
+
+ bsn_l3_dst_class_id = dict(
+ name="bsn_l3_dst_class_id",
+ m_type="uint32_t",
+ print_type="PRIu32",
+ conditions="",
+ takes_mask_in_spec=True,
+ order=1005,
+ ),
+)
+
+match_keys_sorted = of_match_members.keys()
+match_keys_sorted.sort(key=lambda entry:of_match_members[entry]["order"])
+
+of_v1_keys = [
+ "eth_dst",
+ "eth_src",
+ "eth_type",
+ "in_port",
+ "ipv4_dst",
+ "ip_proto",
+ "ipv4_src",
+ "ip_dscp",
+ "tcp_dst", # Means UDP too for 1.0 and 1.1
+ "tcp_src", # Means UDP too for 1.0 and 1.1
+ "vlan_pcp",
+ "vlan_vid"
+ ]
+
+of_v2_keys = [
+ "eth_dst",
+ "eth_src",
+ "eth_type",
+ "in_port",
+ "ipv4_dst",
+ "ip_proto",
+ "ipv4_src",
+ "ip_dscp",
+ "tcp_dst", # Means UDP too for 1.0 and 1.1
+ "tcp_src", # Means UDP too for 1.0 and 1.1
+ "vlan_pcp",
+ "vlan_vid",
+ "mpls_label",
+ "mpls_tc",
+ "metadata"
+ ]
+
+of_v2_full_mask = [
+ "eth_dst",
+ "eth_src",
+ "ipv4_dst",
+ "ipv4_src",
+ "metadata"
+ ]
+
+def oxm_index(key):
+ """
+ What's the index called for a match key
+ """
+ return "OF_OXM_INDEX_" + key.upper()
+
+##
+# Check that all members in the hash are recognized as match keys
+def match_sanity_check():
+ count = 0
+ for match_v in ["of_match_v1", "of_match_v2"]:
+ count += 1
+ for mm in of_g.unified[match_v][count]["members"]:
+ key = mm["name"]
+ if key.find("_mask") >= 0:
+ continue
+ if loxi_utils.skip_member_name(key):
+ continue
+ if key == "wildcards":
+ continue
+ if not key in of_match_members:
+ print "Key %s not found in match struct, v %s" % (key, match_v)
+ sys.exit(1)
+
+ # Generate list of OXM names from the unified classes
+ oxm_names = [x[7:] for x in of_g.unified.keys() if
+ x.startswith('of_oxm_') and
+ x.find('masked') < 0 and
+ x.find('header') < 0]
+
+ # Check that all OXMs are in the match members
+ for key in oxm_names:
+ if not key in of_match_members:
+ if not (key.find("_masked") > 0):
+ debug("Key %s in OXM, not of_match_members" % key)
+ sys.exit(1)
+ if not key[:-7] in of_match_members:
+ debug("Key %s in OXM, but %s not in of_match_members"
+ % (key, key[:-7]))
+ sys.exit(1)
+
+ # Check that all match members are in the OXMs
+ for key in of_match_members:
+ if not key in oxm_names:
+ debug("Key %s in of_match_members, not in OXM" % key)
+ sys.exit(1)
+ oxm_type = of_g.unified['of_oxm_%s' % key]['union']['value']['m_type']
+ if of_match_members[key]["m_type"] != oxm_type:
+ debug("Type mismatch for key %s in oxm data: %s vs %s" %
+ (key, of_match_members[key]["m_type"], oxm_type))
+ sys.exit(1)
diff --git a/c_gen/templates/locitest/test_validator.c b/c_gen/templates/locitest/test_validator.c
index 6b9dd87..eb3cbb6 100644
--- a/c_gen/templates/locitest/test_validator.c
+++ b/c_gen/templates/locitest/test_validator.c
@@ -27,8 +27,8 @@
::
:: include('_copyright.c')
:: import of_g
-:: from loxi_utils import loxi_utils
-:: from loxi_front_end import type_maps
+:: import c_gen.loxi_utils_legacy as loxi_utils
+:: from c_gen import type_maps
/**
* Test message validator
diff --git a/c_gen/translation.py b/c_gen/translation.py
new file mode 100644
index 0000000..ef6c11b
--- /dev/null
+++ b/c_gen/translation.py
@@ -0,0 +1,127 @@
+# 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.
+
+##
+# @brief Translation data between openflow.h and LOXI
+#
+
+import re
+import sys
+
+def loxi_name(ident):
+ """
+ Return the LOXI name of an openflow.h identifier
+ """
+
+ # Order at the outer level matters as super strings are listed first
+ rules = [
+# The following are for #define macros and have highest precedence
+ dict(OFP_MAX_TABLE_NAME_LEN = "OF_MAX_TABLE_NAME_LEN"),
+ dict(OFP_MAX_PORT_NAME_LEN = "OF_MAX_PORT_NAME_LEN"),
+ dict(OFP_TCP_PORT = "OF_TCP_PORT"),
+ dict(OFP_SSL_PORT = "OF_SSL_PORT"),
+ dict(OFP_ETH_ALEN = "OF_ETH_ALEN"),
+ dict(OFP_DEFAULT_MISS_SEND_LEN = "OF_DEFAULT_MISS_SEND_LEN"),
+ dict(OFP_VLAN_NONE = "OF_VLAN_UNTAGGED"),
+ dict(OFP_DL_TYPE_ETH2_CUTOFF = "OF_DL_TYPE_ETH2_CUTOFF"),
+ dict(OFP_DL_TYPE_NOT_ETH_TYPE = "OF_DL_TYPE_NOT_ETH_TYPE"),
+ dict(OFP_FLOW_PERMANENT = "OF_FLOW_PERMANENT"),
+ dict(OFP_DEFAULT_PRIORITY = "OF_DEFAULT_PRIORITY"),
+ dict(DESC_STR_LEN = "OF_DESC_STR_LEN"),
+ dict(SERIAL_NUM_LEN = "OF_SERIAL_NUM_LEN"),
+ dict(OFPQ_ALL = "OF_QUEUE_ALL"),
+ dict(OFPQ_MIN_RATE_UNCFG = "OF_QUEUE_MIN_RATE_UNCFG"),
+ dict(OFPQ_MAX_RATE_UNCFG = "OF_QUEUE_MAX_RATE_UNCFG"),
+ dict(OFP_NO_BUFFER = "OF_BUFFER_ID_NO_BUFFER"),
+
+# These are for enums; they map the prefixes
+ dict(OFPP_MAX = "OF_PORT_NUMBER_MAX"), # Special case
+ dict(OFPP_TABLE = "OF_PORT_DEST_USE_TABLE"), # Special case
+ dict(OFPP_ANY = "OF_PORT_DEST_WILDCARD"), # Special case
+ dict(OFPTC_ = "OF_TABLE_CONFIG_"),
+ dict(OFPIEH_ = "OF_IPV6_EXT_HDR_FLAG_"),
+ dict(OFPMBT_ = "OF_METER_BAND_TYPE_"),
+ dict(OFPMC_ = "OF_METER_MOD_COMMAND_"),
+ dict(OFPMF_ = "OF_METER_FLAG_"),
+ dict(OFPTFFC_ = "OF_TABLE_REQUEST_FAILED_"),
+ dict(OFPMMFC_ = "OF_METER_MOD_FAILED_"),
+ dict(OFPPR_ = "OF_PORT_CHANGE_REASON_"),
+ dict(OFPPMFC_ = "OF_PORT_MOD_FAILED_"),
+ dict(OFPP_ = "OF_PORT_DEST_"),
+ dict(OFPRRFC_ = "OF_ROLE_REQUEST_FAILED_"),
+ dict(OFPRR_ = "OF_FLOW_REMOVED_REASON_"),
+ dict(OFPR_ = "OF_PACKET_IN_REASON_"),
+ dict(OFPC_FRAG_ = "OF_CONFIG_FRAG_"),
+ dict(OFPC_INVALID_ = "OF_CONFIG_INVALID_"),
+ dict(OFPCML_ = "OF_CONTROLLER_PKT_"),
+ dict(OFPCR_ROLE_ = "OF_CONTROLLER_ROLE_"),
+ dict(OFPC_ = "OF_CAPABILITIES_FLAG_"),
+ dict(OFPPC_ = "OF_PORT_CONFIG_FLAG_"),
+ dict(OFPPS_ = "OF_PORT_STATE_FLAG_"),
+ dict(OFPPF_ = "OF_PORT_FEATURE_FLAG_"),
+ dict(OFPTT_ = "OF_TABLE_"),
+ dict(OFPT_ = "OF_OBJ_TYPE_"),
+ dict(OFPMT_ = "OF_MATCH_TYPE_"),
+ dict(OFPM_ = "OF_METER_"),
+ dict(OFPXMC_ = "OF_OXM_CLASS_"),
+ dict(OFPVID_ = "OF_VLAN_TAG_"),
+ dict(OFPGC_ = "OF_GROUP_"),
+ dict(OFPGT_ = "OF_GROUP_TYPE_"),
+ dict(OFPG_ = "OF_GROUP_"),
+ dict(OFPET_ = "OF_ERROR_TYPE_"),
+ dict(OFPFC_ = "OF_FLOW_MOD_COMMAND_"),
+ dict(OFPHFC_ = "OF_HELLO_FAILED_"),
+ dict(OFPBRC_ = "OF_REQUEST_FAILED_"),
+ dict(OFPBAC_ = "OF_ACTION_FAILED_"),
+ dict(OFPBIC_ = "OF_INSTRUCTION_FAILED_"),
+ dict(OFPBMC_ = "OF_MATCH_FAILED_"),
+ dict(OFPGMFC_ = "OF_GROUP_MOD_FAILED_"),
+ dict(OFPTMFC_ = "OF_TABLE_MOD_FAILED_"),
+ dict(OFPFMFC_ = "OF_FLOW_MOD_FAILED_"),
+ dict(OFPQOFC_ = "OF_QUEUE_OP_FAILED_"),
+ dict(OFPSCFC_ = "OF_SWITCH_CONFIG_FAILED_"),
+ dict(OFPQCFC_ = "OF_SWITCH_CONFIG_FAILED_"), # See EXT-208
+ dict(OFPAT_ = "OF_ACTION_TYPE_"),
+ dict(OFPFW_ = "OF_FLOW_WC_V1_"),
+ dict(OFPFF_ = "OF_FLOW_MOD_FLAG_"),
+ dict(OFPST_ = "OF_STATS_TYPE_"),
+ dict(OFPSF_ = "OF_STATS_REPLY_FLAG_"),
+ dict(OFPQT_ = "OF_QUEUE_PROPERTY_"),
+ dict(OFPIT_ = "OF_INSTRUCTION_TYPE_"),
+ dict(OFPGFC_ = "OF_GROUP_CAPABILITIES_"),
+ dict(OFPMP_ = "OF_MULTIPART_"),
+ dict(OFPMPF_ = "OF_MULTIPART_FLAG_"),
+ dict(OFPTFPT_ = "OF_TABLE_FEATURE_"),
+ dict(OFPHET = "OF_HELLO_ELEM_TYPE_"),
+ dict(NX_ROLE_ = "OF_NICIRA_CONTROLLER_ROLE_"),
+ ]
+
+ for entry in rules:
+ for id_from, id_to in entry.items():
+ if re.match(id_from, ident):
+ return re.sub(id_from, id_to, ident)
+ return ident
diff --git a/c_gen/type_maps.py b/c_gen/type_maps.py
new file mode 100644
index 0000000..909ee79
--- /dev/null
+++ b/c_gen/type_maps.py
@@ -0,0 +1,802 @@
+# 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.
+
+#
+# Miscellaneous type information
+#
+# Define the map between sub-class types and wire values. In each
+# case, an array indexed by wire version gives a hash from identifier
+# to wire value.
+#
+
+import c_gen.of_g_legacy as of_g
+import sys
+from generic_utils import *
+import loxi_utils.loxi_utils as loxi_utils
+import c_gen.loxi_utils_legacy as loxi_utils
+
+invalid_type = "invalid_type"
+invalid_value = "0xeeee" # Note, as a string
+
+################################################################
+#
+# Define type data for inheritance classes:
+# instructions, actions, queue properties and OXM
+#
+# Messages are not in this group; they're treated specially for now
+#
+# These are indexed by wire protocol number
+#
+################################################################
+
+instruction_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict()
+ }
+
+# HACK shared between actions and action_ids
+of_1_3_action_types = dict()
+
+action_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:of_1_3_action_types
+ }
+
+action_id_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:of_1_3_action_types
+ }
+
+queue_prop_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict()
+ }
+
+bsn_vport_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ q_in_q = 0,
+ ),
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ q_in_q = 0,
+ ),
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ q_in_q = 0,
+ ),
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ q_in_q = 0,
+ )
+ }
+
+oxm_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict(),
+ }
+
+hello_elem_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict(),
+ }
+
+table_feature_prop_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict(),
+ }
+
+meter_band_types = {
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict(),
+ }
+
+# All inheritance data for non-messages
+inheritance_data = dict(
+ of_instruction = instruction_types,
+ of_action = action_types,
+ of_action_id = action_id_types,
+ of_oxm = oxm_types,
+ of_queue_prop = queue_prop_types,
+ of_hello_elem = hello_elem_types,
+ of_table_feature_prop = table_feature_prop_types,
+ of_meter_band = meter_band_types,
+ # BSN specific inheritance extensions
+ of_bsn_vport = bsn_vport_types
+ )
+
+def class_is_virtual(cls):
+ """
+ Returns True if cls is a virtual class
+ """
+ if cls in inheritance_map:
+ return True
+ if cls.find("header") > 0:
+ return True
+ if loxi_utils.class_is_list(cls):
+ return True
+ # TODO get this from the input file when we have virtual class syntax
+ if cls in ["of_flow_mod", "of_stats_request", "of_stats_reply", "of_error_msg", "of_bsn_header", "of_nicira_header", "of_action_bsn", "of_action_nicira", "of_action_id_bsn", "of_action_id_nicira"]:
+ return True
+ return False
+
+################################################################
+#
+# These are message types
+#
+################################################################
+
+# The hardcoded message types are for inheritance parents
+message_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ error_msg = 1,
+ experimenter = 4,
+ flow_mod = 14,
+ stats_request = 16,
+ stats_reply = 17,
+ ),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ error_msg = 1,
+ experimenter = 4,
+ flow_mod = 14,
+ stats_request = 18,
+ stats_reply = 19,
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ error_msg = 1,
+ experimenter = 4,
+ flow_mod = 14,
+ stats_request = 18,
+ stats_reply = 19,
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ error_msg = 1,
+ experimenter = 4,
+ flow_mod = 14,
+ stats_request = 18, # FIXME Multipart
+ stats_reply = 19,
+ )
+ }
+
+################################################################
+#
+# These are other objects that have a notion of type but are
+# not (yet) promoted to objects with inheritance
+#
+################################################################
+
+stats_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ experimenter = 0xffff
+ ),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ group = 6,
+ group_desc = 7,
+ experimenter = 0xffff
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ group = 6,
+ group_desc = 7,
+ group_features = 8,
+ experimenter = 0xffff
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ desc = 0,
+ flow = 1,
+ aggregate = 2,
+ table = 3,
+ port = 4,
+ queue = 5,
+ group = 6,
+ group_desc = 7,
+ group_features = 8,
+ meter = 9,
+ meter_config = 10,
+ meter_features = 11,
+ table_features = 12,
+ port_desc = 13,
+ experimenter = 0xffff
+ )
+ }
+
+common_flow_mod_types = dict(
+ add = 0,
+ modify = 1,
+ modify_strict = 2,
+ delete = 3,
+ delete_strict = 4
+ )
+
+flow_mod_types = {
+ # version 1.0
+ of_g.VERSION_1_0:common_flow_mod_types,
+ of_g.VERSION_1_1:common_flow_mod_types,
+ of_g.VERSION_1_2:common_flow_mod_types,
+ of_g.VERSION_1_3:common_flow_mod_types
+ }
+
+# These do not translate to objects (yet)
+error_types = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ flow_mod_failed = 3,
+ port_mod_failed = 4,
+ queue_op_failed = 5
+ ),
+
+ # version 1.1
+ of_g.VERSION_1_1:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ bad_instruction = 3,
+ bad_match = 4,
+ flow_mod_failed = 5,
+ group_mod_failed = 6,
+ port_mod_failed = 7,
+ table_mod_failed = 8,
+ queue_op_failed = 9,
+ switch_config_failed = 10
+ ),
+
+ # version 1.2
+ of_g.VERSION_1_2:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ bad_instruction = 3,
+ bad_match = 4,
+ flow_mod_failed = 5,
+ group_mod_failed = 6,
+ port_mod_failed = 7,
+ table_mod_failed = 8,
+ queue_op_failed = 9,
+ switch_config_failed = 10,
+ role_request_failed = 11,
+ experimenter = 0xffff
+ ),
+
+ # version 1.3
+ of_g.VERSION_1_3:dict(
+ hello_failed = 0,
+ bad_request = 1,
+ bad_action = 2,
+ bad_instruction = 3,
+ bad_match = 4,
+ flow_mod_failed = 5,
+ group_mod_failed = 6,
+ port_mod_failed = 7,
+ table_mod_failed = 8,
+ queue_op_failed = 9,
+ switch_config_failed = 10,
+ role_request_failed = 11,
+ meter_mod_failed = 12,
+ table_features_failed= 13,
+ experimenter = 0xffff
+ )
+ }
+
+##
+# These are the objects whose length is specified by an external
+# reference, specifically another data member in the class.
+#
+#external_length_spec = {
+# ("of_packet_out", "actions", OF_VERSION_1_0) : "actions_len",
+# ("of_packet_out", "actions", OF_VERSION_1_1) : "actions_len",
+# ("of_packet_out", "actions", OF_VERSION_1_2) : "actions_len",
+# ("of_packet_out", "actions", OF_VERSION_1_3) : "actions_len"
+#}
+
+
+################################################################
+#
+# type_val is the primary data structure that maps an
+# (class_name, version) pair to the wire data type value
+#
+################################################################
+
+type_val = dict()
+inheritance_map = dict()
+
+def generate_maps():
+ for parent, versioned in inheritance_data.items():
+ inheritance_map[parent] = set()
+ for ver, subclasses in versioned.items():
+ for subcls in subclasses:
+ inheritance_map[parent].add(subcls)
+
+ for version, classes in message_types.items():
+ for cls in classes:
+ name = "of_" + cls
+ type_val[(name, version)] = classes[cls]
+
+ for parent, versioned in inheritance_data.items():
+ for version, subclasses in versioned.items():
+ for subcls, value in subclasses.items():
+ name = parent + "_" + subcls
+ type_val[(name, version)] = value
+
+ # Special case OF-1.2 match type
+ type_val[("of_match_v3", of_g.VERSION_1_2)] = 1
+ type_val[("of_match_v3", of_g.VERSION_1_3)] = 1
+
+# Utility function
+def dict_to_array(d, m_val, def_val=-1):
+ """
+ Given a dictionary, d, with each value a small integer,
+ produce an array indexed by the integer whose value is the key.
+ @param d The dictionary
+ @param m_val Ignore values greater than m_val
+ @param def_val The default value (for indices not in range of d)
+ """
+
+ # Get the max value in range for hash
+ max_val = 0
+ for key in d:
+ if (d[key] > max_val) and (d[key] < m_val):
+ max_val = d[key]
+ ar = []
+ for x in range(0, max_val + 1):
+ ar.append(def_val)
+ for key in d:
+ if (d[key] < m_val):
+ ar[d[key]] = key
+ return ar
+
+def type_array_len(version_indexed, max_val):
+ """
+ Given versioned information about a type, calculate how long
+ the unified array should be.
+
+ @param version_indexed A dict indexed by version. Each value is a
+ dict indexed by a name and whose value is an integer
+ @param max_val Ignore values greater than this for length calcs
+ """
+ # First, find the max length of all arrays
+ arr_len = 0
+ for version, val_dict in version_indexed.items():
+ ar = dict_to_array(val_dict, max_val, invalid_type)
+ if arr_len < len(ar):
+ arr_len = len(ar)
+ return arr_len
+
+# FIXME: Need to move update for multipart messages
+
+stats_reply_list = [
+ "of_aggregate_stats_reply",
+ "of_desc_stats_reply",
+ "of_experimenter_stats_reply",
+ "of_flow_stats_reply",
+ "of_group_stats_reply",
+ "of_group_desc_stats_reply",
+ "of_group_features_stats_reply",
+ "of_meter_stats_reply",
+ "of_meter_config_stats_reply",
+ "of_meter_features_stats_reply",
+ "of_port_stats_reply",
+ "of_port_desc_stats_reply",
+ "of_queue_stats_reply",
+ "of_table_stats_reply",
+ "of_table_features_stats_reply"
+]
+
+stats_request_list = [
+ "of_aggregate_stats_request",
+ "of_desc_stats_request",
+ "of_experimenter_stats_request",
+ "of_flow_stats_request",
+ "of_group_stats_request",
+ "of_group_desc_stats_request",
+ "of_group_features_stats_request",
+ "of_meter_stats_request",
+ "of_meter_config_stats_request",
+ "of_meter_features_stats_request",
+ "of_port_stats_request",
+ "of_port_desc_stats_request",
+ "of_queue_stats_request",
+ "of_table_stats_request",
+ "of_table_features_stats_request"
+]
+
+flow_mod_list = [
+ "of_flow_add",
+ "of_flow_modify",
+ "of_flow_modify_strict",
+ "of_flow_delete",
+ "of_flow_delete_strict"
+]
+
+error_msg_list = [
+ "of_hello_failed_error_msg",
+ "of_bad_request_error_msg",
+ "of_bad_action_error_msg",
+ "of_bad_instruction_error_msg",
+ "of_bad_match_error_msg",
+ "of_flow_mod_failed_error_msg",
+ "of_group_mod_failed_error_msg",
+ "of_port_mod_failed_error_msg",
+ "of_table_mod_failed_error_msg",
+ "of_queue_op_failed_error_msg",
+ "of_switch_config_failed_error_msg",
+ "of_role_request_failed_error_msg",
+ "of_meter_mod_failed_error_msg",
+ "of_table_features_failed_error_msg",
+ "of_experimenter_error_msg"
+]
+
+def sub_class_map(base_type, version):
+ """
+ Returns an iterable object giving the instance nameys and subclass types
+ for the base_type, version values
+ """
+ rv = []
+ if base_type not in inheritance_map:
+ return rv
+
+ for instance in inheritance_map[base_type]:
+ subcls = loxi_utils.instance_to_class(instance, base_type)
+ if not loxi_utils.class_in_version(subcls, version):
+ continue
+ rv.append((instance, subcls))
+
+ return rv
+
+################################################################
+#
+# Extension related data and functions
+#
+################################################################
+
+# Per OF Version, per experimenter, map exp msg type (subtype) to object IDs
+# @fixme Generate defines for OF_<exp>_SUBTYPE_<msg> for the values below?
+extension_message_subtype = {
+ # version 1.0
+ of_g.VERSION_1_0:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ },
+ nicira = { # Nicira extensions, value is subtype
+ },
+ ),
+ of_g.VERSION_1_1:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ },
+ ),
+ of_g.VERSION_1_2:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ },
+ ),
+ of_g.VERSION_1_3:dict( # Version 1.0 extensions
+ bsn = { # BSN extensions; indexed by class name, value is subtype
+ },
+ ),
+}
+
+# Set to empty dict if no extension actions defined
+# Per OF Version, per experimenter, map actions to subtype
+extension_action_subtype = {
+ # version 1.0
+ of_g.VERSION_1_0:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ },
+ nicira = { # of_action_nicira_
+ }
+ ),
+ of_g.VERSION_1_1:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ },
+ nicira = { # of_action_nicira_
+ }
+ ),
+ of_g.VERSION_1_2:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ },
+ nicira = { # of_action_nicira_
+ }
+ ),
+ of_g.VERSION_1_3:dict( # Version 1.0 extensions
+ bsn = { # of_action_bsn_
+ },
+ nicira = { # of_action_nicira_
+ }
+ ),
+}
+
+# Set to empty dict if no extension actions defined
+# Per OF Version, per experimenter, map actions to subtype
+extension_action_id_subtype = {
+ # version 1.0
+ of_g.VERSION_1_0:dict(),
+ of_g.VERSION_1_1:dict(),
+ of_g.VERSION_1_2:dict(),
+ of_g.VERSION_1_3:dict( # Version 1.3 extensions
+ bsn = { # of_action_bsn_
+ },
+ nicira = { # of_action_nicira_
+ }
+ ),
+}
+
+# Set to empty dict if no extension instructions defined
+extension_instruction_subtype = {}
+
+# Set to empty dict if no extension instructions defined
+extension_queue_prop_subtype = {}
+
+# Set to empty dict if no extension instructions defined
+extension_table_feature_prop_subtype = {}
+
+extension_objects = [
+ extension_message_subtype,
+ extension_action_subtype,
+ extension_action_id_subtype,
+ extension_instruction_subtype,
+ extension_queue_prop_subtype,
+ extension_table_feature_prop_subtype
+]
+
+################################################################
+# These are extension type generic (for messages, actions...)
+################################################################
+
+def extension_to_experimenter_name(cls):
+ """
+ Return the name of the experimenter if class is an
+ extension, else None
+
+ This is brute force; we search all extension data for a match
+ """
+
+ for ext_obj in extension_objects:
+ for version, exp_list in ext_obj.items():
+ for exp_name, classes in exp_list.items():
+ if cls in classes:
+ return exp_name
+ return None
+
+def extension_to_experimenter_id(cls):
+ """
+ Return the ID of the experimenter if class is an
+ extension, else None
+ """
+ exp_name = extension_to_experimenter_name(cls)
+ if exp_name:
+ return of_g.experimenter_name_to_id[exp_name]
+ return None
+
+def extension_to_experimenter_macro_name(cls):
+ """
+ Return the "macro name" of the ID of the experimenter if class is an
+ extension, else None
+ """
+ exp_name = extension_to_experimenter_name(cls)
+ if exp_name:
+ return "OF_EXPERIMENTER_ID_" + exp_name.upper()
+ return None
+
+def extension_to_subtype(cls, version):
+ """
+ Generic across all extension objects, return subtype identifier
+ """
+ for ext_obj in extension_objects:
+ for version, exp_list in ext_obj.items():
+ for exp_name, classes in exp_list.items():
+ if cls in classes:
+ return classes[cls]
+
+def class_is_extension(cls, version):
+ """
+ Return True if class, version is recognized as an extension
+ of any type (message, action....)
+
+ Accepts of_g.OF_VERSION_ANY
+ """
+
+ for ext_obj in extension_objects:
+ if cls_is_ext_obj(cls, version, ext_obj):
+ return True
+
+ return False
+
+# Internal
+def cls_is_ext_obj(cls, version, ext_obj):
+ """
+ @brief Return True if cls in an extension of type ext_obj
+ @param cls The class to check
+ @param version The version to check
+ @param ext_obj The extension object dictionary (messages, actions...)
+
+ Accepts of_g.VERSION_ANY
+ """
+
+ # Call with each version if "any" is passed
+ if version == of_g.VERSION_ANY:
+ for v in of_g.of_version_range:
+ if cls_is_ext_obj(cls, v, ext_obj):
+ return True
+ else: # Version specified
+ if version in ext_obj:
+ for exp, subtype_vals in ext_obj[version].items():
+ if cls in subtype_vals:
+ return True
+
+ return False
+
+################################################################
+# These are extension message specific
+################################################################
+
+def message_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_message_subtype)
+
+def extension_message_to_subtype(cls, version):
+ """
+ Return the subtype of the experimenter message if the class is an
+ extension, else None
+ """
+ if version in extension_message_subtype:
+ for exp, classes in extension_message_subtype[version].items():
+ for ext_class, subtype in classes.items():
+ if cls == ext_class:
+ return subtype
+ return None
+
+################################################################
+# These are extension action specific
+################################################################
+
+def action_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an action extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_action_subtype)
+
+def extension_action_to_subtype(cls, version):
+ """
+ Return the ID of the action subtype (for its experimenteer)
+ if class is an action extension, else None
+ """
+ if version in extension_action_subtype:
+ for exp, classes in extension_action_subtype[version].items():
+ if cls in classes:
+ return classes[cls]
+
+ return None
+
+################################################################
+# These are extension action specific
+################################################################
+
+def action_id_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an action ID extension
+ This is brute force, searching records for a match
+ """
+ if version not in [of_g.VERSION_1_3]: # Action IDs only 1.3
+ return False
+ return cls_is_ext_obj(cls, version, extension_action_id_subtype)
+
+def extension_action_id_to_subtype(cls, version):
+ """
+ Return the ID of the action ID subtype (for its experimenteer)
+ if class is an action ID extension, else None
+ """
+ if version in extension_action_id_subtype:
+ for exp, classes in extension_action_id_subtype[version].items():
+ if cls in classes:
+ return classes[cls]
+
+ return None
+
+################################################################
+# These are extension instruction specific
+################################################################
+
+def instruction_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an instruction extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_instruction_subtype)
+
+################################################################
+# These are extension queue_prop specific
+################################################################
+
+def queue_prop_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an instruction extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version, extension_queue_prop_subtype)
+
+################################################################
+# These are extension table_feature_prop specific
+################################################################
+
+def table_feature_prop_is_extension(cls, version):
+ """
+ Return True if cls, version is recognized as an instruction extension
+ This is brute force, searching records for a match
+ """
+ return cls_is_ext_obj(cls, version,
+ extension_table_feature_prop_subtype)