blob: d6d8b32c76fe8135dbc855d11c290d83e4c96c6c [file] [log] [blame]
# Copyright 2013, Big Switch Networks, Inc.
#
# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
# the following special exception:
#
# LOXI Exception
#
# As a special exception to the terms of the EPL, you may distribute libraries
# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
# that copyright and licensing notices generated by LoxiGen are not altered or removed
# from the LoxiGen Libraries and the notice provided below is (i) included in
# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
# documentation for the LoxiGen Libraries, if distributed in binary form.
#
# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
#
# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
# a copy of the EPL at:
#
# http://www.eclipse.org/legal/epl-v10.html
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# EPL for the specific language governing permissions and limitations
# under the EPL.
from collections import namedtuple
import struct
import of_g
import loxi_front_end.type_maps as type_maps
import loxi_utils.loxi_utils as utils
import util
import oftype
OFClass = namedtuple('OFClass', ['name', 'pyname', 'members', 'type_members',
'min_length', 'is_fixed_length'])
Member = namedtuple('Member', ['name', 'oftype'])
LengthMember = namedtuple('LengthMember', ['name', 'oftype'])
FieldLengthMember = namedtuple('FieldLengthMember', ['name', 'oftype', 'field_name'])
TypeMember = namedtuple('TypeMember', ['name', 'oftype', 'value'])
PadMember = namedtuple('PadMember', ['length'])
# XXX move to frontend
field_length_members = {
('of_packet_out', 1, 'actions_len') : 'actions',
('of_packet_out', 2, 'actions_len') : 'actions',
('of_packet_out', 3, 'actions_len') : 'actions',
('of_packet_out', 4, 'actions_len') : 'actions',
}
def get_type_values(cls, version):
"""
Returns a map from the name of the type member to its value.
"""
type_values = {}
# Primary wire type
if utils.class_is_message(cls):
type_values['version'] = 'const.OFP_VERSION'
type_values['type'] = util.constant_for_value(version, "ofp_type", util.primary_wire_type(cls, version))
if cls in type_maps.flow_mod_list:
type_values['_command'] = util.constant_for_value(version, "ofp_flow_mod_command",
type_maps.flow_mod_types[version][cls[8:]])
if cls in type_maps.stats_request_list:
type_values['stats_type'] = util.constant_for_value(version, "ofp_stats_types",
type_maps.stats_types[version][cls[3:-14]])
if cls in type_maps.stats_reply_list:
type_values['stats_type'] = util.constant_for_value(version, "ofp_stats_types",
type_maps.stats_types[version][cls[3:-12]])
if type_maps.message_is_extension(cls, version):
type_values['experimenter'] = '%#x' % type_maps.extension_to_experimenter_id(cls)
type_values['subtype'] = type_maps.extension_message_to_subtype(cls, version)
elif utils.class_is_action(cls):
type_values['type'] = util.constant_for_value(version, "ofp_action_type", util.primary_wire_type(cls, version))
if type_maps.action_is_extension(cls, version):
type_values['experimenter'] = '%#x' % type_maps.extension_to_experimenter_id(cls)
type_values['subtype'] = type_maps.extension_action_to_subtype(cls, version)
elif utils.class_is_queue_prop(cls):
type_values['type'] = util.constant_for_value(version, "ofp_queue_properties", util.primary_wire_type(cls, version))
elif utils.class_is_hello_elem(cls):
type_values['type'] = util.constant_for_value(version, "ofp_hello_elem_type", util.primary_wire_type(cls, version))
elif utils.class_is_oxm(cls):
oxm_class = 0x8000
oxm_type = util.primary_wire_type(cls, version)
oxm_masked = cls.find('masked') != -1 and 1 or 0
oxm_len = of_g.base_length[(cls, version)] - 4
type_values['type_len'] = '%#x' % (oxm_class << 16 | oxm_type << 8 | \
oxm_masked << 8 | oxm_len)
elif cls == "of_match_v2":
type_values['type'] = 0
elif cls == "of_match_v3":
type_values['type'] = 1
elif utils.class_is_meter_band(cls):
type_values['type'] = util.constant_for_value(version, "ofp_meter_band_type", util.primary_wire_type(cls, version))
elif utils.class_is_instruction(cls):
type_values['type'] = util.constant_for_value(version, "ofp_instruction_type", util.primary_wire_type(cls, version))
return type_values
# Create intermediate representation
def build_ofclasses(version):
blacklist = ["of_action", "of_action_header", "of_header", "of_queue_prop",
"of_queue_prop_header", "of_experimenter", "of_action_experimenter",
"of_oxm", "of_oxm_header", "of_oxm_experimenter_header",
"of_hello_elem", "of_hello_elem_header"]
ofclasses = []
for cls in of_g.standard_class_order:
if type_maps.class_is_virtual(cls):
continue
if version not in of_g.unified[cls] or cls in blacklist:
continue
unified_class = util.lookup_unified_class(cls, version)
# Name for the generated Python class
if utils.class_is_action(cls):
pyname = cls[10:]
elif utils.class_is_oxm(cls):
pyname = cls[7:]
elif utils.class_is_meter_band(cls):
pyname = cls[14:]
elif utils.class_is_instruction(cls):
pyname = cls[15:]
else:
pyname = cls[3:]
type_values = get_type_values(cls, version)
members = []
type_members = []
pad_count = 0
for member in unified_class['members']:
if member['name'] in ['length', 'len']:
members.append(LengthMember(name=member['name'],
oftype=oftype.OFType(member['m_type'], version)))
elif (cls, version, member['name']) in field_length_members:
field_name = field_length_members[(cls, version, member['name'])]
members.append(FieldLengthMember(name=member['name'],
oftype=oftype.OFType(member['m_type'], version),
field_name=field_name))
elif member['name'] in type_values:
members.append(TypeMember(name=member['name'],
oftype=oftype.OFType(member['m_type'], version),
value=type_values[member['name']]))
type_members.append(members[-1])
elif member['name'].startswith("pad"):
# HACK this should be moved to the frontend
pad_oftype = oftype.OFType(member['m_type'], version)
length = struct.calcsize("!" + pad_oftype._pack_fmt())
if pad_oftype.is_array: length *= pad_oftype.array_length
members.append(PadMember(length=length))
else:
members.append(Member(name=member['name'],
oftype=oftype.OFType(member['m_type'], version)))
ofclasses.append(
OFClass(name=cls,
pyname=pyname,
members=members,
type_members=type_members,
min_length=of_g.base_length[(cls, version)],
is_fixed_length=(cls, version) in of_g.is_fixed_length))
return ofclasses
def generate_init(out, name, version):
util.render_template(out, 'init.py', version=version)
def generate_action(out, name, version):
ofclasses = [x for x in build_ofclasses(version)
if utils.class_is_action(x.name)]
util.render_template(out, 'action.py', ofclasses=ofclasses, version=version)
def generate_oxm(out, name, version):
ofclasses = [x for x in build_ofclasses(version)
if utils.class_is_oxm(x.name)]
util.render_template(out, 'oxm.py', ofclasses=ofclasses, version=version)
def generate_common(out, name, version):
ofclasses = [x for x in build_ofclasses(version)
if not utils.class_is_message(x.name)
and not utils.class_is_action(x.name)
and not utils.class_is_instruction(x.name)
and not utils.class_is_meter_band(x.name)
and not utils.class_is_oxm(x.name)
and not utils.class_is_list(x.name)]
util.render_template(out, 'common.py', ofclasses=ofclasses, version=version)
def generate_const(out, name, version):
groups = {}
for (group, idents) in of_g.identifiers_by_group.items():
items = []
for ident in idents:
info = of_g.identifiers[ident]
if version in info["values_by_version"]:
items.append((info["ofp_name"], info["values_by_version"][version]))
if items:
groups[group] = items
util.render_template(out, 'const.py', version=version, groups=groups)
def generate_instruction(out, name, version):
ofclasses = [x for x in build_ofclasses(version)
if utils.class_is_instruction(x.name)]
util.render_template(out, 'instruction.py', ofclasses=ofclasses, version=version)
def generate_message(out, name, version):
ofclasses = [x for x in build_ofclasses(version)
if utils.class_is_message(x.name)]
util.render_template(out, 'message.py', ofclasses=ofclasses, version=version)
def generate_meter_band(out, name, version):
ofclasses = [x for x in build_ofclasses(version)
if utils.class_is_meter_band(x.name)]
util.render_template(out, 'meter_band.py', ofclasses=ofclasses, version=version)
def generate_pp(out, name, version):
util.render_template(out, 'pp.py')
def generate_util(out, name, version):
util.render_template(out, 'util.py', version=version)