blob: f40bc71e4db65d094ca2d8cac12007299c25c3bb [file] [log] [blame]
Andreas Wundsam27303462013-07-16 12:52:35 -07001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
Andreas Wundsam40e14f72013-05-06 14:49:08 -070028# Prototype of an Intermediate Object model for the java code generator
29# A lot of this stuff could/should probably be merged with the python utilities
30
Andreas Wundsam27303462013-07-16 12:52:35 -070031import collections
Andreas Wundsam5204de22013-07-30 11:34:45 -070032from collections import namedtuple, defaultdict, OrderedDict
Andreas Wundsam27303462013-07-16 12:52:35 -070033import logging
Andreas Wundsam40e14f72013-05-06 14:49:08 -070034import os
35import pdb
36import re
37
Andreas Wundsam5204de22013-07-30 11:34:45 -070038from generic_utils import find, memoize, OrderedSet, OrderedDefaultDict
Andreas Wundsam27303462013-07-16 12:52:35 -070039import of_g
40from loxi_ir import *
Andreas Wundsam40e14f72013-05-06 14:49:08 -070041import loxi_front_end.type_maps as type_maps
Andreas Wundsam5204de22013-07-30 11:34:45 -070042import loxi_utils.loxi_utils as loxi_utils
Andreas Wundsam40e14f72013-05-06 14:49:08 -070043import py_gen.util as py_utils
Andreas Wundsam5204de22013-07-30 11:34:45 -070044import test_data
Andreas Wundsam40e14f72013-05-06 14:49:08 -070045
Andreas Wundsam27303462013-07-16 12:52:35 -070046import java_gen.java_type as java_type
Andreas Wundsame0d52be2013-08-22 07:52:13 -070047from java_gen.java_type import erase_type_annotation
Andreas Wundsam40e14f72013-05-06 14:49:08 -070048
Rob Vaterlaus4d311942013-09-24 13:41:44 -070049# Key is modified name of error code; value is OFErrorType value string
50error_type_map = {}
51
52def adjust_ir():
53 """
54 For Java we change of_error_message to combine the 16-bit type and code
55 fields into a single 32-bit code field and we combine the per-error-type
56 code enums into a single ofp_error_code enum. This enables the generated
57 OFErrorMsg class to have a getCode method that can return all supported
58 error codes. Otherwise we'd need to do something like having per-error-type
59 subclasses of OFErrorMsg that had a getCode that returned the different
60 error codes for each error type, which would be less convenient for clients
61 and would also entail changing the LOXI OF input files and impacting other
62 language backends.
63 """
64 for version in of_g.target_version_list:
65 of_protocol = of_g.ir[version]
66 error_type = find(lambda e: e.name == "ofp_error_type", of_protocol.enums)
67 if error_type == None:
68 raise Exception("ofp_error_type enum not found; OF version: " + str(version))
69 error_code_entries = []
70 # For each error type value look up the corresponding error code enum.
71 # Add those values to the combined entries for the new ofp_error_code
72 # enum. The name of the new value is formed by concatenating the name
73 # of the error type value with the name of the old error code value.
74 for error_type_entry in error_type.entries:
75 # Strip off the OFPxx prefix
76 prefix_length = error_type_entry.name.find('_')
77 if prefix_length < 0:
78 raise Exception("OFPET prefix not found for ofp_error_type value " + error_type_entry.name + "; OF version: " + str(version))
79 error_type_entry_name = error_type_entry.name[prefix_length+1:]
80 if error_type_entry_name == "EXPERIMENTER":
81 # There isn't an error code enum defined for the experimenter error type
82 # FIXME: Need to add support for the message ofp_error_experimenter_msg format
83 continue
84 # The per-error-type code enums follow a naming conventions where
85 # the middle part of the enum name is the same as the name of the
86 # error type value (except lower-case).
87 error_code_enum_name = "ofp_" + error_type_entry_name.lower() + "_code"
88 # Look up the error code enum from the IR
89 error_code_enum = None
90 for i, enum in enumerate(of_protocol.enums):
91 if enum.name == error_code_enum_name:
92 error_code_enum = enum
93 # We don't want to generate code for the per-error-type
94 # enum so remove it from the IR
95 del of_protocol.enums[i]
96 break
97 if error_code_enum == None:
98 raise Exception("Error code enum not found: " + error_code_enum_name + "; OF version: " + str(version))
99 for error_code_entry in error_code_enum.entries:
100 # Strip off the prefix from the entry name
101 prefix_length = error_code_entry.name.find('_')
102 if prefix_length < 0:
103 raise Exception("Prefix not found for error code value " + error_code_entry.name + "; OF version: " + str(version))
104 error_code_entry_name = error_code_entry.name[prefix_length+1:]
105 # Combine the entry type name and the error code name
106 error_code_entry_name = error_type_entry_name + "_" + error_code_entry_name
107 # Combine the entry type value and the error code value
108 error_code_entry_value = (error_type_entry.value << 16) + error_code_entry.value
109 # Add the enum entry to the combined ofp_error_code
110 # Note that the "OFPEC" prefix is arbitrary. It will be stripped
111 # off again during Java code generation, but there needs to be
112 # some/any prefix
113 error_code_entries.append(OFEnumEntry("OFPEC_" + error_code_entry_name, error_code_entry_value, {}))
114 error_type_map[error_code_entry_name] = error_type_entry_name
115 # We've collected all of the entries. Now we can add the enum to the IR
116 of_protocol.enums.append(OFEnum("ofp_error_code", error_code_entries, {'wire_type': 'uint32_t'}))
117
118 # We also need to patch the of_error_msg class to combine the 16-bit error
119 # type and code fields into a single 32-bit error code field
120 error_msg = find(lambda c: c.name == "of_error_msg", of_protocol.classes)
121 if error_msg == None:
122 raise Exception("of_error_msg class not found; OF version: " + str(version))
123 err_type_index = None
124 for i, member in enumerate(error_msg.members):
125 if member.name == "err_type":
126 # Keep track of the error type index so we can remove it once
127 # we've finished iterating
128 err_type_index = i
129 elif member.name == 'code':
130 # Change the code to be a 32-bit ofp_error_code enum value
131 error_msg.members[i] = OFDataMember("code", "ofp_error_code")
132 if err_type_index == None:
133 raise Exception("err_type member of of_error_msg not found; OF version: " + str(version))
134 del error_msg.members[err_type_index]
135
136
137def gen_error_type(enum_entry):
138 return "OFErrorType." + error_type_map[enum_entry.name]
139
Andreas Wundsam27303462013-07-16 12:52:35 -0700140class JavaModel(object):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700141 # registry for enums that should not be generated
142 # set(${java_enum_name})
Rob Vaterlausfbd5b6b2013-09-24 15:55:47 -0700143 enum_blacklist = set(("OFDefinitions", "OFPortNo",))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700144 # registry for enum *entry* that should not be generated
145 # map: ${java_enum_name} -> set(${java_entry_entry_name})
Andreas Wundsam43526532013-08-01 22:03:50 -0700146 enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700147
148 # registry of interfaces that should not be generated
149 # set(java_names)
Andreas Wundsambe168f72013-08-03 22:49:35 -0700150 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
151 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700152 # registry of interface properties that should not be generated
153 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700154 read_blacklist = defaultdict(lambda: set(), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700155 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700156 write_blacklist = defaultdict(lambda: set(), OFOxm=set(('typeLen',)), OFAction=set(('type',)), OFInstruction=set(('type',)), OFFlowMod=set(('command', )), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700157 # interfaces that are virtual
Andreas Wundsam001b1822013-08-02 22:25:55 -0700158 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -0700159
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700160 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
Yotam Harchole5d92972013-08-22 14:18:36 -0700161 oxm_map = { "OFOxmInPort": OxmMapEntry("OFPort", "IN_PORT", False),
162 "OFOxmInPortMasked": OxmMapEntry("OFPort", "IN_PORT", True),
163 "OFOxmInPhyPort": OxmMapEntry("OFPort", "IN_PHY_PORT", False),
164 "OFOxmInPhyPortMasked": OxmMapEntry("OFPort", "IN_PHY_PORT", True),
165 "OFOxmMetadata": OxmMapEntry("OFMetadata", "METADATA", False),
166 "OFOxmMetadataMasked": OxmMapEntry("OFMetadata", "METADATA", True),
167 "OFOxmEthDst": OxmMapEntry("MacAddress", "ETH_DST", False),
168 "OFOxmEthDstMasked": OxmMapEntry("MacAddress", "ETH_DST", True),
169 "OFOxmEthSrc": OxmMapEntry("MacAddress", "ETH_SRC", False),
170 "OFOxmEthSrcMasked": OxmMapEntry("MacAddress", "ETH_SRC", True),
171 "OFOxmEthType": OxmMapEntry("EthType", "ETH_TYPE", False),
172 "OFOxmEthTypeMasked": OxmMapEntry("EthType", "ETH_TYPE", True),
173 "OFOxmVlanVid": OxmMapEntry("VlanVid", "VLAN_VID", False),
174 "OFOxmVlanVidMasked": OxmMapEntry("VlanVid", "VLAN_VID", True),
175 "OFOxmVlanPcp": OxmMapEntry("VlanPcp", "VLAN_PCP", False),
176 "OFOxmVlanPcpMasked": OxmMapEntry("VlanPcp", "VLAN_PCP", True),
177 "OFOxmIpDscp": OxmMapEntry("IpDscp", "IP_DSCP", False),
178 "OFOxmIpDscpMasked": OxmMapEntry("IpDscp", "IP_DSCP", True),
179 "OFOxmIpEcn": OxmMapEntry("IpEcn", "IP_ECN", False),
180 "OFOxmIpEcnMasked": OxmMapEntry("IpEcn", "IP_ECN", True),
181 "OFOxmIpProto": OxmMapEntry("IpProtocol", "IP_PROTO", False),
182 "OFOxmIpProtoMasked": OxmMapEntry("IpProtocol", "IP_PROTO", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700183 "OFOxmIpv4Src": OxmMapEntry("IPv4Address", "IPV4_SRC", False),
184 "OFOxmIpv4SrcMasked": OxmMapEntry("IPv4Address", "IPV4_SRC", True),
185 "OFOxmIpv4Dst": OxmMapEntry("IPv4Address", "IPV4_DST", False),
186 "OFOxmIpv4DstMasked": OxmMapEntry("IPv4Address", "IPV4_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700187 "OFOxmTcpSrc": OxmMapEntry("TransportPort", "TCP_SRC", False),
188 "OFOxmTcpSrcMasked": OxmMapEntry("TransportPort", "TCP_SRC", True),
189 "OFOxmTcpDst": OxmMapEntry("TransportPort", "TCP_DST", False),
190 "OFOxmTcpDstMasked": OxmMapEntry("TransportPort", "TCP_DST", True),
191 "OFOxmUdpSrc": OxmMapEntry("TransportPort", "UDP_SRC", False),
192 "OFOxmUdpSrcMasked": OxmMapEntry("TransportPort", "UDP_SRC", True),
193 "OFOxmUdpDst": OxmMapEntry("TransportPort", "UDP_DST", False),
194 "OFOxmUdpDstMasked": OxmMapEntry("TransportPort", "UDP_DST", True),
195 "OFOxmSctpSrc": OxmMapEntry("TransportPort", "SCTP_SRC", False),
196 "OFOxmSctpSrcMasked": OxmMapEntry("TransportPort", "SCTP_SRC", True),
197 "OFOxmSctpDst": OxmMapEntry("TransportPort", "SCTP_DST", False),
198 "OFOxmSctpDstMasked": OxmMapEntry("TransportPort", "SCTP_DST", True),
199 "OFOxmIcmpv4Type": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", False),
200 "OFOxmIcmpv4TypeMasked": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", True),
201 "OFOxmIcmpv4Code": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", False),
202 "OFOxmIcmpv4CodeMasked": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", True),
203 "OFOxmArpOp": OxmMapEntry("ArpOpcode", "ARP_OP", False),
204 "OFOxmArpOpMasked": OxmMapEntry("ArpOpcode", "ARP_OP", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700205 "OFOxmArpSpa": OxmMapEntry("IPv4Address", "ARP_SPA", False),
206 "OFOxmArpSpaMasked": OxmMapEntry("IPv4Address", "ARP_SPA", True),
207 "OFOxmArpTpa": OxmMapEntry("IPv4Address", "ARP_TPA", False),
208 "OFOxmArpTpaMasked": OxmMapEntry("IPv4Address", "ARP_TPA", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700209 "OFOxmArpSha": OxmMapEntry("MacAddress", "ARP_SHA", False),
210 "OFOxmArpShaMasked": OxmMapEntry("MacAddress", "ARP_SHA", True),
211 "OFOxmArpTha": OxmMapEntry("MacAddress", "ARP_THA", False),
212 "OFOxmArpThaMasked": OxmMapEntry("MacAddress", "ARP_THA", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700213 "OFOxmIpv6Src": OxmMapEntry("IPv6Address", "IPV6_SRC", False),
214 "OFOxmIpv6SrcMasked": OxmMapEntry("IPv6Address", "IPV6_SRC", True),
215 "OFOxmIpv6Dst": OxmMapEntry("IPv6Address", "IPV6_DST", False),
216 "OFOxmIpv6DstMasked": OxmMapEntry("IPv6Address", "IPV6_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700217 "OFOxmIpv6Flabel": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", False),
Yotam Harchola86e4252013-09-06 15:36:28 -0700218 "OFOxmIpv6FlabelMasked": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", True),
219 "OFOxmIcmpv6Type": OxmMapEntry("U8", "ICMPV6_TYPE", False),
220 "OFOxmIcmpv6TypeMasked": OxmMapEntry("U8", "ICMPV6_TYPE", True),
221 "OFOxmIcmpv6Code": OxmMapEntry("U8", "ICMPV6_CODE", False),
222 "OFOxmIcmpv6CodeMasked": OxmMapEntry("U8", "ICMPV6_CODE", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700223 "OFOxmIpv6NdTarget": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", False),
224 "OFOxmIpv6NdTargetMasked": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700225 "OFOxmIpv6NdSll": OxmMapEntry("MacAddress", "IPV6_ND_SLL", False),
226 "OFOxmIpv6NdSllMasked": OxmMapEntry("MacAddress", "IPV6_ND_SLL", True),
227 "OFOxmIpv6NdTll": OxmMapEntry("MacAddress", "IPV6_ND_TLL", False),
228 "OFOxmIpv6NdTllMasked": OxmMapEntry("MacAddress", "IPV6_ND_TLL", True),
229 "OFOxmMplsLabel": OxmMapEntry("U32", "MPLS_LABEL", False),
230 "OFOxmMplsLabelMasked": OxmMapEntry("U32", "MPLS_LABEL", True),
231 "OFOxmMplsTc": OxmMapEntry("U8", "MPLS_TC", False),
Yotam Harchola11f38b2013-09-26 15:38:17 -0700232 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True),
233 "OFOxmBsnInPortsMasked128": OxmMapEntry("OFPortMap", "BSN_IN_PORTS_MASKED_128", True)
Yotam Harchola86e4252013-09-06 15:36:28 -0700234 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700235
Andreas Wundsamf1949682013-09-23 14:48:31 -0700236 # Registry of nullable properties:
237 # ${java_class_name} -> set(${java_property_name})
238 nullable_map = defaultdict(lambda: set(),
239 )
240
241 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
242 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
243 # name: a name for the group
244 # mask: java name of the enum entry that defines the mask
245 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700246 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
247
Andreas Wundsamf1949682013-09-23 14:48:31 -0700248 # registry of MaskedEnumGroups (see above).
249 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700250 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700251 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
252 OFConfigFlags = (
253 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700254 ),
255 OFTableConfig = (
256 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
257 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700258 )
259
Andreas Wundsamf1949682013-09-23 14:48:31 -0700260 # represents a metadata property associated with an EnumClass
261 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700262 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700263 """
264 represents a metadata property associated with an Enum Class
265 @param name name of metadata property
266 @param type java_type instance describing the type
267 @value: Generator function f(entry) that generates the value
268 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700269 @property
270 def variable_name(self):
271 return self.name[0].lower() + self.name[1:]
272
273 @property
274 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700275 prefix = "is" if self.type == java_type.boolean else "get"
276 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700277
Andreas Wundsamf1949682013-09-23 14:48:31 -0700278 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700279 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
280
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700281 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700282 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700283 splits = enum_entry.name.split("_")
284 if len(splits)>=2:
285 m = re.match(r'\d+[MGTP]B', splits[1])
286 if m:
287 return "PortSpeed.SPEED_{}".format(splits[1])
288 return "PortSpeed.SPEED_NONE";
289
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700290 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700291 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700292 splits = enum_entry.name.split("_")
293 if len(splits)>=1:
294 if splits[0] == "STP":
295 return "true"
296 return "false"
297
Andreas Wundsamf1949682013-09-23 14:48:31 -0700298 # registry for metadata properties for enums
299 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700300 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700301 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
302 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700303 OFErrorCode = OFEnumMetadata((OFEnumPropertyMetadata("ErrorType", java_type.error_type, gen_error_type),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700304 )
305
Andreas Wundsam27303462013-07-16 12:52:35 -0700306 @property
307 @memoize
308 def versions(self):
309 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
310
311 @property
312 @memoize
313 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700314 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700315
316 for raw_version, of_protocol in of_g.ir.items():
317 jversion = JavaOFVersion(of_protocol.wire_version)
318
319 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700320 if not of_class.name in version_map_per_class:
321 version_map_per_class[of_class.name] = collections.OrderedDict()
322
Andreas Wundsam27303462013-07-16 12:52:35 -0700323 version_map_per_class[of_class.name][jversion] = of_class
324
325 interfaces = []
326 for class_name, version_map in version_map_per_class.items():
327 interfaces.append(JavaOFInterface(class_name, version_map))
328
Andreas Wundsambe168f72013-08-03 22:49:35 -0700329 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
330
Andreas Wundsam27303462013-07-16 12:52:35 -0700331 return interfaces
332
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700333 @memoize
334 def interface_by_name(self, name):
335 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
336
Andreas Wundsam27303462013-07-16 12:52:35 -0700337 @property
338 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700339 def all_classes(self):
340 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
341
342 @property
343 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700344 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700345 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700346
347 for version in self.versions:
348 of_protocol = of_g.ir[version.int_version]
349 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700350 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700351
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700352 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
353 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700354
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700355 # inelegant - need java name here
356 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700357 return enums
358
359 @memoize
360 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700361 res = find(lambda e: e.name == name, self.enums)
362 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700363 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700364 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700365
Andreas Wundsam5204de22013-07-30 11:34:45 -0700366 @property
367 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700368 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700369 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700370
371 factories = OrderedDict()
372
373 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
374 for base_class in sub_factory_classes:
375 package = base_class[2:].lower()
376 remove_prefix = base_class[2].lower() + base_class[3:]
377
378 # HACK need to have a better way to deal with parameterized base classes
379 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
380
381 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
382 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={})
383
384 factories[""] = OFFactory(
385 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700386 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700387 remove_prefix="",
388 members=[], base_class="OFMessage", sub_factories=OrderedDict(
389 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ))
390
391 for i in self.interfaces:
392 for n, factory in factories.items():
393 if n == "":
394 factory.members.append(i)
395 break
396 else:
397 super_class = self.interface_by_name(n)
398 if i.is_instance_of(super_class):
399 factory.members.append(i)
400 break
401 return factories.values()
Andreas Wundsam5204de22013-07-30 11:34:45 -0700402
403 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700404 """ return wether or not to generate implementation class clazz.
405 Now true for everything except OFTableModVer10.
406 @param clazz JavaOFClass instance
407 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700408 if clazz.interface.name.startswith("OFMatchV"):
409 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700410 elif clazz.name == "OFTableModVer10":
411 # tablemod ver 10 is a hack and has no oftype defined
412 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700413 if loxi_utils.class_is_message(clazz.interface.c_name):
414 return True
415 if loxi_utils.class_is_oxm(clazz.interface.c_name):
416 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700417 if loxi_utils.class_is_action(clazz.interface.c_name):
418 return True
419 if loxi_utils.class_is_instruction(clazz.interface.c_name):
420 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700421 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700422 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700423
424
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700425class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700426 @property
427 def factory_classes(self):
428 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700429 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700430 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700431 interface=self,
432 version=version
433 ) for version in model.versions ]
434
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700435 def method_name(self, member, builder=True):
436 n = member.variable_name
437 if n.startswith(self.remove_prefix):
438 n = n[len(self.remove_prefix):]
439 n = n[0].lower() + n[1:]
440 if builder:
441 return "build" + n[0].upper() + n[1:]
442 else:
443 return n
Andreas Wundsam5204de22013-07-30 11:34:45 -0700444
445OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700446class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
447 @property
448 def base_class(self):
449 return self.interface.base_class
450
451 @property
452 def versioned_base_class(self):
453 base_class_interface = model.interface_by_name(self.interface.base_class)
454 if base_class_interface and base_class_interface.has_version(self.version):
455 return base_class_interface.versioned_class(self.version)
456 else:
457 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700458
Andreas Wundsam27303462013-07-16 12:52:35 -0700459model = JavaModel()
460
461#######################################################################
462### OFVersion
463#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700464
465class JavaOFVersion(object):
466 """ Models a version of OpenFlow. contains methods to convert the internal
467 Loxi version to a java constant / a string """
468 def __init__(self, int_version):
469 self.int_version = int(int_version)
470
471 @property
472 def of_version(self):
473 return "1" + str(int(self.int_version) - 1)
474
475 @property
476 def constant_version(self):
477 return "OF_" + self.of_version
478
Andreas Wundsam27303462013-07-16 12:52:35 -0700479 def __repr__(self):
480 return "JavaOFVersion(%d)" % self.int_version
481
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700482 def __str__(self):
483 return of_g.param_version_names[self.int_version]
484
Andreas Wundsam27303462013-07-16 12:52:35 -0700485 def __hash__(self):
486 return hash(self.int_version)
487
488 def __eq__(self, other):
489 if other is None or type(self) != type(other):
490 return False
491 return (self.int_version,) == (other.int_version,)
492
493#######################################################################
494### Interface
495#######################################################################
496
497class JavaOFInterface(object):
498 """ Models an OpenFlow Message class for the purpose of the java class.
499 Version agnostic, in contrast to the loxi_ir python model.
500 """
501 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700502 """"
503 @param c_name: loxi style name (e.g., of_flow_add)
504 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
505 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700506 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700507 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700508 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700509 self.name = java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700510 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700511 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700512 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700513 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700514 self.constant_name = c_name.upper().replace("OF_", "")
515
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700516 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700517 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700518 if self.name != parent_interface:
519 self.parent_interface = parent_interface
520 else:
521 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700522
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700523 @property
524 @memoize
525 def all_parent_interfaces(self):
526 return [ "OFObject" ] + \
527 ([ self.parent_interface ] if self.parent_interface else [] )+ \
528 self.additional_parent_interfaces
529 @property
530 @memoize
531 def additional_parent_interfaces(self):
532 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
533 m = re.match(r'(.*)Request$', self.name)
534 if m:
535 reply_name = m.group(1) + "Reply"
536 if model.interface_by_name(reply_name):
537 return ["OFRequest<%s>" % reply_name ]
538 return []
539
540
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700541 def is_instance_of(self, other_class):
542 if self == other_class:
543 return True
544 parent = self.super_class
545 if parent is None:
546 return False
547 else:
548 return parent.is_instance_of(other_class)
549
550 @property
551 def super_class(self):
552 if not self.parent_interface:
553 return None
554 else:
555 return model.interface_by_name(self.parent_interface)
556
557
558 def inherited_declaration(self, type_spec="?"):
559 if self.type_annotation:
560 return "%s<%s>" % (self.name, type_spec)
561 else:
562 return "%s" % self.name
563
564 @property
565 def type_variable(self):
566 if self.type_annotation:
567 return "<T>"
568 else:
569 return "";
570
Andreas Wundsam27303462013-07-16 12:52:35 -0700571 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700572 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
573 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
574 # model (note, that the loxi model is on versioned classes). Should check/infer the
575 # inheritance information from the versioned lox_ir classes.
Andreas Wundsam001b1822013-08-02 22:25:55 -0700576 if re.match(r'OF.+StatsRequest$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700577 return ("", "OFStatsRequest", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700578 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700579 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700580 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700581 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700582 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFBsn.+$', self.name) and self.name != "OFBsnHeader":
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700583 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700584 elif loxi_utils.class_is_message(self.c_name) and re.match(r'OFNicira.+$', self.name) and self.name != "OFNiciraHeader":
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700585 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700586 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
587 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700588 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700589 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700590 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700591 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700592 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700593 if re.match(r'OFActionBsn.+', self.name):
594 return ("action", "OFActionBsn", None)
595 elif re.match(r'OFActionNicira.+', self.name):
596 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700597 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
598 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700599 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700600 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700601 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700602 return ("", "OFBsnVport", None)
603 elif self.name == "OFOxm":
604 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700605 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700606 if self.name in model.oxm_map:
607 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
608 else:
609 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700610 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700611 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700612 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700613 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700614 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700615 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700616 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700617 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700618 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700619 return ("", None, None)
620
621 @property
622 @memoize
623 def writeable_members(self):
624 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700625
626 @property
627 @memoize
628 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700629 return self.ir_model_members + self.virtual_members
630
631 @property
632 @memoize
633 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700634 """return a list of all members to be exposed by this interface. Corresponds to
635 the union of the members of the vesioned classes without length, fieldlength
636 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700637 all_versions = []
638 member_map = collections.OrderedDict()
639
640 for (version, of_class) in self.version_map.items():
641 for of_member in of_class.members:
642 if isinstance(of_member, OFLengthMember) or \
643 isinstance(of_member, OFFieldLengthMember) or \
644 isinstance(of_member, OFPadMember):
645 continue
646 if of_member.name not in member_map:
647 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
648
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700649 return tuple(m for m in member_map.values() if m.name not in model.read_blacklist[self.name])
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700650
651 @property
652 def virtual_members(self):
653 if self.name == "OFOxm":
654 return (
655 JavaVirtualMember(self, "value", java_type.generic_t),
656 JavaVirtualMember(self, "mask", java_type.generic_t),
657 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
658 JavaVirtualMember(self, "masked", java_type.boolean),
659 )
660 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
661 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
662 if self.name in model.oxm_map \
663 else java_type.make_match_field_jtype()
664
665 return (
666 JavaVirtualMember(self, "matchField", field_type),
667 JavaVirtualMember(self, "masked", java_type.boolean),
668 ) \
669 + \
670 (
671 ( JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type), ) if not find(lambda x: x.name == "mask", self.ir_model_members) else
672 ()
673 )
674 else:
675 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700676
677 @property
678 @memoize
679 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700680 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700681 return self.name in model.virtual_interfaces or all(ir_class.virtual for ir_class in self.version_map.values())
Andreas Wundsam27303462013-07-16 12:52:35 -0700682
683 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700684 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700685 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700686 return len(self.all_versions) == len(model.versions)
687
688 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700689 @memoize
690 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700691 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700692 return self.version_map.keys()
693
Andreas Wundsam5204de22013-07-30 11:34:45 -0700694 def has_version(self, version):
695 return version in self.version_map
696
Andreas Wundsam27303462013-07-16 12:52:35 -0700697 def versioned_class(self, version):
698 return JavaOFClass(self, version, self.version_map[version])
699
700 @property
701 @memoize
702 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700703 return [ self.versioned_class(version) for version in self.all_versions ]
704
705#######################################################################
706### (Versioned) Classes
707#######################################################################
708
709class JavaOFClass(object):
710 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700711 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700712 """
713 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700714 """
715 @param interface JavaOFInterface instance of the parent interface
716 @param version JavaOFVersion
717 @param ir_class OFClass from loxi_ir
718 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700719 self.interface = interface
720 self.ir_class = ir_class
721 self.c_name = self.ir_class.name
722 self.version = version
723 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700724 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700725 self.generated = False
726
727 @property
728 @memoize
729 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700730 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700731
732 @property
733 def name(self):
734 return "%sVer%s" % (self.interface.name, self.version.of_version)
735
736 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700737 def variable_name(self):
738 return self.name[3:]
739
740 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700741 def length(self):
742 if self.is_fixed_length:
743 return self.min_length
744 else:
745 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
746
747 @property
748 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700749 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700750 id_tuple = (self.ir_class.name, self.version.int_version)
751 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
752
753 @property
754 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700755 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700756 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
757 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700758
759 def all_properties(self):
760 return self.interface.members
761
762 def get_member(self, name):
763 for m in self.members:
764 if m.name == name:
765 return m
766
767 @property
768 @memoize
769 def data_members(self):
770 return [ prop for prop in self.members if prop.is_data ]
771
772 @property
773 @memoize
774 def fixed_value_members(self):
775 return [ prop for prop in self.members if prop.is_fixed_value ]
776
777 @property
778 @memoize
779 def public_members(self):
780 return [ prop for prop in self.members if prop.is_public ]
781
782 @property
783 @memoize
784 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700785 return self.ir_model_members + self.virtual_members
786
787 @property
788 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700789 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700790 return tuple(members)
791
792 @property
793 def virtual_members(self):
794 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
795 if self.interface.name in model.oxm_map:
796 oxm_entry = model.oxm_map[self.interface.name]
797 return (
798 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
799 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
800 )
801 else:
802 return (
803 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
804 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
805 )
806 else:
807 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700808
809 def all_versions(self):
810 return [ JavaOFVersion(int_version)
811 for int_version in of_g.unified[self.c_name]
812 if int_version != 'union' and int_version != 'object_id' ]
813
814 def version_is_inherited(self, version):
815 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
816
817 def inherited_from(self, version):
818 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
819
820 @property
821 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700822 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
823
824 @property
825 def discriminator(self):
826 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700827
828 @property
829 def is_extension(self):
830 return type_maps.message_is_extension(self.c_name, -1)
831
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700832 @property
833 def align(self):
834 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
835
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700836 @property
837 @memoize
838 def superclass(self):
839 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
840
841 @property
842 @memoize
843 def subclasses(self):
844 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
845
Andreas Wundsam27303462013-07-16 12:52:35 -0700846#######################################################################
847### Member
848#######################################################################
849
850
851class JavaMember(object):
852 """ Models a property (member) of an openflow class. """
853 def __init__(self, msg, name, java_type, member):
854 self.msg = msg
855 self.name = name
856 self.java_type = java_type
857 self.member = member
858 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700859
860 @property
861 def title_name(self):
862 return self.name[0].upper() + self.name[1:]
863
864 @property
865 def constant_name(self):
866 return self.c_name.upper()
867
868 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700869 def getter_name(self):
870 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
871
872 @property
873 def setter_name(self):
874 return "set" + self.title_name
875
876 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700877 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700878 if self.is_fixed_value:
879 return self.constant_name
880 else:
881 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700882
883 @property
884 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700885 if self.is_fixed_value:
886 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700887 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700888 default = self.java_type.default_op(self.msg.version)
889 if default == "null" and not self.is_nullable:
890 return None
891 else:
892 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700893
894 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700895 def enum_value(self):
896 if self.name == "version":
897 return "OFVersion.%s" % self.msg.version.constant_version
898
899 java_type = self.java_type.public_type;
900 try:
901 global model
902 enum = model.enum_by_name(java_type)
903 entry = enum.entry_by_version_value(self.msg.version, self.value)
904 return "%s.%s" % ( enum.name, entry.name)
905 except KeyError, e:
906 print e.message
907 return self.value
908
909 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700910 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700911 return isinstance(self.member, OFPadMember)
912
913 def is_type_value(self, version=None):
914 if(version==None):
915 return any(self.is_type_value(version) for version in self.msg.all_versions)
916 try:
917 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
918 except:
919 return False
920
921 @property
922 def is_field_length_value(self):
923 return isinstance(self.member, OFFieldLengthMember)
924
925 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700926 def is_discriminator(self):
927 return isinstance(self.member, OFDiscriminatorMember)
928
929 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700930 def is_length_value(self):
931 return isinstance(self.member, OFLengthMember)
932
933 @property
934 def is_public(self):
935 return not (self.is_pad or self.is_length_value)
936
937 @property
938 def is_data(self):
939 return isinstance(self.member, OFDataMember) and self.name != "version"
940
941 @property
942 def is_fixed_value(self):
943 return hasattr(self.member, "value") or self.name == "version" \
944 or ( self.name == "length" and self.msg.is_fixed_length) \
945 or ( self.name == "len" and self.msg.is_fixed_length)
946
947 @property
948 def value(self):
949 if self.name == "version":
950 return self.msg.version.int_version
951 elif self.name == "length" or self.name == "len":
952 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700953 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700954 return self.java_type.format_value(self.member.value)
955
956 @property
957 def priv_value(self):
958 if self.name == "version":
959 return self.msg.version.int_version
960 elif self.name == "length" or self.name == "len":
961 return self.msg.length
962 else:
963 return self.java_type.format_value(self.member.value, pub_type=False)
964
Andreas Wundsam27303462013-07-16 12:52:35 -0700965
966 @property
967 def is_writeable(self):
968 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
969
970 def get_type_value_info(self, version):
971 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700972
973 @property
974 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700975 if hasattr(self.member, "length"):
976 return self.member.length
977 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700978 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700979 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700980
981 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700982 def for_of_member(java_class, member):
983 if isinstance(member, OFPadMember):
984 return JavaMember(None, "", None, member)
985 else:
986 if member.name == 'len':
987 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700988 elif member.name == 'value_mask':
989 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700990 else:
991 name = java_type.name_c_to_camel(member.name)
992 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
993 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700994
995 @property
996 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700997 if not self.msg.c_name in of_g.unified:
998 print("%s not self.unified" % self.msg.c_name)
999 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001000 for version in of_g.unified[self.msg.c_name]:
1001 if version == 'union' or version =='object_id':
1002 continue
1003 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1004 continue
1005
Andreas Wundsam27303462013-07-16 12:52:35 -07001006 if not self.member.name in (f['name'] for f in of_g.unified[self.msg.c_name][version]['members']):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001007 return False
1008 return True
1009
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001010 @property
1011 def is_virtual(self):
1012 return False
1013
Andreas Wundsam27303462013-07-16 12:52:35 -07001014 def __hash__(self):
1015 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001016
Andreas Wundsam27303462013-07-16 12:52:35 -07001017 def __eq__(self, other):
1018 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001019 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001020 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001021
Andreas Wundsamf1949682013-09-23 14:48:31 -07001022 @property
1023 def is_nullable(self):
1024 return self.name in model.nullable_map[self.msg.name]
1025
1026
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001027class JavaVirtualMember(JavaMember):
1028 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1029 def __init__(self, msg, name, java_type, value=None):
1030 JavaMember.__init__(self, msg, name, java_type, member=None)
1031 self._value = value
1032
1033 @property
1034 def is_fixed_value(self):
1035 return True
1036
1037 @property
1038 def value(self):
1039 return self._value
1040
1041 @property
1042 def priv_value(self):
1043 return self._value
1044
1045
1046 @property
1047 def is_universal(self):
1048 return True
1049
1050 @property
1051 def is_virtual(self):
1052 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001053
1054#######################################################################
1055### Unit Test
1056#######################################################################
1057
Yotam Harchol466b3212013-08-15 12:14:46 -07001058class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001059 def __init__(self, java_class):
1060 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001061 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001062 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001063 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1064 name=java_class.c_name[3:]) + "{i}.data"
1065 test_class_name = self.java_class.name + "Test"
1066 self.test_units = []
1067 if test_data.exists(first_data_file_name):
1068 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1069 i = 1
1070 while test_data.exists(data_file_template.format(i=i)):
1071 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1072 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001073
Yotam Harchol466b3212013-08-15 12:14:46 -07001074 @property
1075 def package(self):
1076 return self.java_class.package
1077
1078 @property
1079 def has_test_data(self):
1080 return len(self.test_units) > 0
1081
1082 @property
1083 def length(self):
1084 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001085
Yotam Harchol466b3212013-08-15 12:14:46 -07001086 def get_test_unit(self, i):
1087 return self.test_units[i]
1088
1089
1090class JavaUnitTest(object):
1091 def __init__(self, java_class, file_name=None, test_class_name=None):
1092 self.java_class = java_class
1093 if file_name is None:
1094 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1095 name=java_class.c_name[3:])
1096 else:
1097 self.data_file_name = file_name
1098 if test_class_name is None:
1099 self.test_class_name = self.java_class.name + "Test"
1100 else:
1101 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001102
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001103 @property
1104 def package(self):
1105 return self.java_class.package
1106
1107 @property
1108 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001109 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001110
1111 @property
1112 def has_test_data(self):
1113 return test_data.exists(self.data_file_name)
1114
1115 @property
1116 @memoize
1117 def test_data(self):
1118 return test_data.read(self.data_file_name)
1119
1120
Andreas Wundsam27303462013-07-16 12:52:35 -07001121#######################################################################
1122### Enums
1123#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001124
Andreas Wundsam27303462013-07-16 12:52:35 -07001125class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001126 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001127 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001128
1129 if c_name == "of_stats_types":
1130 self.name = "OFStatsType"
1131 else:
1132 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001133
Andreas Wundsam27303462013-07-16 12:52:35 -07001134 # Port_features has constants that start with digits
1135 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001136
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001137 self.version_enums = version_enum_map
1138
1139 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1140 for version, ir_enum in version_enum_map.items():
1141 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001142 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1143
Andreas Wundsam27303462013-07-16 12:52:35 -07001144 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001145 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001146
1147 self.entries = [ e for e in self.entries if e.name not in model.enum_entry_blacklist[self.name] ]
Yotam Harchol791e4882013-09-05 16:32:56 -07001148 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001149
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001150 self.metadata = model.enum_metadata_map[self.name]
1151
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001152 def wire_type(self, version):
1153 ir_enum = self.version_enums[version]
1154 if "wire_type" in ir_enum.params:
1155 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1156 else:
1157 return java_type.u8
1158
1159 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001160 @memoize
1161 def is_bitmask(self):
1162 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1163
1164 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001165 def versions(self):
1166 return self.version_enums.keys()
1167
Andreas Wundsam27303462013-07-16 12:52:35 -07001168 @memoize
1169 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001170 res = find(lambda e: e.name == name, self.entries)
1171 if res:
1172 return res
1173 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001174 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1175
1176 @memoize
1177 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001178 res = find(lambda e: e.c_name == name, self.entries)
1179 if res:
1180 return res
1181 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001182 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1183
1184 @memoize
1185 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001186 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1187 if res:
1188 return res
1189 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001190 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1191
1192# values: Map JavaVersion->Value
1193class JavaEnumEntry(object):
1194 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001195 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001196 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1197 self.values = values
1198
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001199 @property
1200 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001201 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001202
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001203 def has_value(self, version):
1204 return version in self.values
1205
Andreas Wundsam27303462013-07-16 12:52:35 -07001206 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001207 return self.values[version]
1208
1209 def format_value(self, version):
1210 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001211 return res
1212
Andreas Wundsam27303462013-07-16 12:52:35 -07001213 def all_values(self, versions, not_present=None):
1214 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001215
1216 @property
1217 @memoize
1218 def masked_enum_group(self):
1219 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1220 return group
1221
1222 @property
1223 @memoize
1224 def is_mask(self):
1225 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])