blob: f30ab1ba3fe9d630d3479a86020175c4f7fa65f3 [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),
Yotam Harchol2c535582013-10-01 15:50:20 -0700233 "OFOxmBsnInPorts128": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", False),
234 "OFOxmBsnInPorts128Masked": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", True)
Yotam Harchola86e4252013-09-06 15:36:28 -0700235 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700236
Andreas Wundsamf1949682013-09-23 14:48:31 -0700237 # Registry of nullable properties:
238 # ${java_class_name} -> set(${java_property_name})
239 nullable_map = defaultdict(lambda: set(),
240 )
241
242 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
243 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
244 # name: a name for the group
245 # mask: java name of the enum entry that defines the mask
246 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700247 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
248
Andreas Wundsamf1949682013-09-23 14:48:31 -0700249 # registry of MaskedEnumGroups (see above).
250 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700251 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700252 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
253 OFConfigFlags = (
254 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700255 ),
256 OFTableConfig = (
257 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
258 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700259 )
260
Andreas Wundsamf1949682013-09-23 14:48:31 -0700261 # represents a metadata property associated with an EnumClass
262 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700263 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700264 """
265 represents a metadata property associated with an Enum Class
266 @param name name of metadata property
267 @param type java_type instance describing the type
268 @value: Generator function f(entry) that generates the value
269 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700270 @property
271 def variable_name(self):
272 return self.name[0].lower() + self.name[1:]
273
274 @property
275 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700276 prefix = "is" if self.type == java_type.boolean else "get"
277 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700278
Andreas Wundsamf1949682013-09-23 14:48:31 -0700279 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700280 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
281
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700282 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700283 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700284 splits = enum_entry.name.split("_")
285 if len(splits)>=2:
286 m = re.match(r'\d+[MGTP]B', splits[1])
287 if m:
288 return "PortSpeed.SPEED_{}".format(splits[1])
289 return "PortSpeed.SPEED_NONE";
290
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700291 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700292 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700293 splits = enum_entry.name.split("_")
294 if len(splits)>=1:
295 if splits[0] == "STP":
296 return "true"
297 return "false"
298
Andreas Wundsamf1949682013-09-23 14:48:31 -0700299 # registry for metadata properties for enums
300 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700301 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700302 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
303 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700304 OFErrorCode = OFEnumMetadata((OFEnumPropertyMetadata("ErrorType", java_type.error_type, gen_error_type),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700305 )
306
Andreas Wundsam27303462013-07-16 12:52:35 -0700307 @property
308 @memoize
309 def versions(self):
310 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
311
312 @property
313 @memoize
314 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700315 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700316
317 for raw_version, of_protocol in of_g.ir.items():
318 jversion = JavaOFVersion(of_protocol.wire_version)
319
320 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700321 if not of_class.name in version_map_per_class:
322 version_map_per_class[of_class.name] = collections.OrderedDict()
323
Andreas Wundsam27303462013-07-16 12:52:35 -0700324 version_map_per_class[of_class.name][jversion] = of_class
325
326 interfaces = []
327 for class_name, version_map in version_map_per_class.items():
328 interfaces.append(JavaOFInterface(class_name, version_map))
329
Andreas Wundsambe168f72013-08-03 22:49:35 -0700330 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
331
Andreas Wundsam27303462013-07-16 12:52:35 -0700332 return interfaces
333
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700334 @memoize
335 def interface_by_name(self, name):
336 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
337
Andreas Wundsam27303462013-07-16 12:52:35 -0700338 @property
339 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700340 def all_classes(self):
341 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
342
343 @property
344 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700345 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700346 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700347
348 for version in self.versions:
349 of_protocol = of_g.ir[version.int_version]
350 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700351 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700352
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700353 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
354 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700355
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700356 # inelegant - need java name here
357 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700358 return enums
359
360 @memoize
361 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700362 res = find(lambda e: e.name == name, self.enums)
363 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700364 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700365 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700366
Andreas Wundsam5204de22013-07-30 11:34:45 -0700367 @property
368 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700369 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700370 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700371
372 factories = OrderedDict()
373
374 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
375 for base_class in sub_factory_classes:
376 package = base_class[2:].lower()
377 remove_prefix = base_class[2].lower() + base_class[3:]
378
379 # HACK need to have a better way to deal with parameterized base classes
380 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
381
382 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700383 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={}, xid_generator=False)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700384
385 factories[""] = OFFactory(
386 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700387 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700388 remove_prefix="",
389 members=[], base_class="OFMessage", sub_factories=OrderedDict(
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700390 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ),
391 xid_generator=True)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700392
393 for i in self.interfaces:
394 for n, factory in factories.items():
395 if n == "":
396 factory.members.append(i)
397 break
398 else:
399 super_class = self.interface_by_name(n)
400 if i.is_instance_of(super_class):
401 factory.members.append(i)
402 break
403 return factories.values()
Yotam Harchol595c6442013-09-27 16:29:08 -0700404
405 @memoize
406 def factory_of(self, interface):
407 for factory in self.of_factories:
408 if interface in factory.members:
409 return factory
410 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700411
412 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700413 """ return wether or not to generate implementation class clazz.
414 Now true for everything except OFTableModVer10.
415 @param clazz JavaOFClass instance
416 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700417 if clazz.interface.name.startswith("OFMatchV"):
418 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700419 elif clazz.name == "OFTableModVer10":
420 # tablemod ver 10 is a hack and has no oftype defined
421 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700422 if loxi_utils.class_is_message(clazz.interface.c_name):
423 return True
424 if loxi_utils.class_is_oxm(clazz.interface.c_name):
425 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700426 if loxi_utils.class_is_action(clazz.interface.c_name):
427 return True
428 if loxi_utils.class_is_instruction(clazz.interface.c_name):
429 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700430 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700431 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700432
433
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700434class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories", "xid_generator"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700435 @property
436 def factory_classes(self):
437 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700438 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700439 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700440 interface=self,
441 version=version
442 ) for version in model.versions ]
443
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700444 def method_name(self, member, builder=True):
445 n = member.variable_name
446 if n.startswith(self.remove_prefix):
447 n = n[len(self.remove_prefix):]
448 n = n[0].lower() + n[1:]
449 if builder:
450 return "build" + n[0].upper() + n[1:]
451 else:
452 return n
Yotam Harchol595c6442013-09-27 16:29:08 -0700453
454 def of_version(self, version):
455 for fc in self.factory_classes:
456 if fc.version == version:
457 return fc
458 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700459
460OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700461class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
462 @property
463 def base_class(self):
464 return self.interface.base_class
465
466 @property
467 def versioned_base_class(self):
468 base_class_interface = model.interface_by_name(self.interface.base_class)
469 if base_class_interface and base_class_interface.has_version(self.version):
470 return base_class_interface.versioned_class(self.version)
471 else:
472 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700473
Andreas Wundsam27303462013-07-16 12:52:35 -0700474model = JavaModel()
475
476#######################################################################
477### OFVersion
478#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700479
480class JavaOFVersion(object):
481 """ Models a version of OpenFlow. contains methods to convert the internal
482 Loxi version to a java constant / a string """
483 def __init__(self, int_version):
484 self.int_version = int(int_version)
485
486 @property
487 def of_version(self):
488 return "1" + str(int(self.int_version) - 1)
489
490 @property
491 def constant_version(self):
492 return "OF_" + self.of_version
493
Andreas Wundsam27303462013-07-16 12:52:35 -0700494 def __repr__(self):
495 return "JavaOFVersion(%d)" % self.int_version
496
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700497 def __str__(self):
498 return of_g.param_version_names[self.int_version]
499
Andreas Wundsam27303462013-07-16 12:52:35 -0700500 def __hash__(self):
501 return hash(self.int_version)
502
503 def __eq__(self, other):
504 if other is None or type(self) != type(other):
505 return False
506 return (self.int_version,) == (other.int_version,)
507
508#######################################################################
509### Interface
510#######################################################################
511
512class JavaOFInterface(object):
513 """ Models an OpenFlow Message class for the purpose of the java class.
514 Version agnostic, in contrast to the loxi_ir python model.
515 """
516 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700517 """"
518 @param c_name: loxi style name (e.g., of_flow_add)
519 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
520 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700521 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700522 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700523 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700524 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 -0700525 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700526 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700527 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700528 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700529 self.constant_name = c_name.upper().replace("OF_", "")
530
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700531 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700532 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700533 if self.name != parent_interface:
534 self.parent_interface = parent_interface
535 else:
536 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700537
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700538 @property
539 @memoize
540 def all_parent_interfaces(self):
541 return [ "OFObject" ] + \
542 ([ self.parent_interface ] if self.parent_interface else [] )+ \
543 self.additional_parent_interfaces
544 @property
545 @memoize
546 def additional_parent_interfaces(self):
547 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
548 m = re.match(r'(.*)Request$', self.name)
549 if m:
550 reply_name = m.group(1) + "Reply"
551 if model.interface_by_name(reply_name):
552 return ["OFRequest<%s>" % reply_name ]
553 return []
554
555
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700556 def is_instance_of(self, other_class):
557 if self == other_class:
558 return True
559 parent = self.super_class
560 if parent is None:
561 return False
562 else:
563 return parent.is_instance_of(other_class)
564
565 @property
566 def super_class(self):
567 if not self.parent_interface:
568 return None
569 else:
570 return model.interface_by_name(self.parent_interface)
571
572
573 def inherited_declaration(self, type_spec="?"):
574 if self.type_annotation:
575 return "%s<%s>" % (self.name, type_spec)
576 else:
577 return "%s" % self.name
578
579 @property
580 def type_variable(self):
581 if self.type_annotation:
582 return "<T>"
583 else:
584 return "";
585
Andreas Wundsam27303462013-07-16 12:52:35 -0700586 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700587 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
588 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
589 # model (note, that the loxi model is on versioned classes). Should check/infer the
590 # inheritance information from the versioned lox_ir classes.
Andreas Wundsamd6b0cb02013-09-28 18:55:56 -0700591 if re.match(r'OFStatsRequest$', self.name):
592 return ("", "OFMessage", "T extends OFStatsReply")
593 elif re.match(r'OF.+StatsRequest$', self.name):
594 return ("", "OFStatsRequest<{}>".format(re.sub(r'Request$', 'Reply', self.name)), None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700595 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700596 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700597 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700598 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700599 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 -0700600 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700601 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 -0700602 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700603 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
604 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700605 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700606 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700607 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700608 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700609 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700610 if re.match(r'OFActionBsn.+', self.name):
611 return ("action", "OFActionBsn", None)
612 elif re.match(r'OFActionNicira.+', self.name):
613 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700614 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
615 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700616 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700617 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700618 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700619 return ("", "OFBsnVport", None)
620 elif self.name == "OFOxm":
621 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700622 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700623 if self.name in model.oxm_map:
624 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
625 else:
626 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700627 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700628 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700629 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700630 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700631 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700632 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700633 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700634 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700635 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700636 return ("", None, None)
637
638 @property
639 @memoize
640 def writeable_members(self):
641 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700642
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700643 @memoize
644 def member_by_name(self, name):
645 return find(lambda m: m.name == name, self.members)
646
Andreas Wundsam27303462013-07-16 12:52:35 -0700647 @property
648 @memoize
649 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700650 return self.ir_model_members + self.virtual_members
651
652 @property
653 @memoize
654 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700655 """return a list of all members to be exposed by this interface. Corresponds to
656 the union of the members of the vesioned classes without length, fieldlength
657 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700658 all_versions = []
659 member_map = collections.OrderedDict()
660
661 for (version, of_class) in self.version_map.items():
662 for of_member in of_class.members:
663 if isinstance(of_member, OFLengthMember) or \
664 isinstance(of_member, OFFieldLengthMember) or \
665 isinstance(of_member, OFPadMember):
666 continue
667 if of_member.name not in member_map:
668 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
669
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700670 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 -0700671
672 @property
673 def virtual_members(self):
674 if self.name == "OFOxm":
675 return (
676 JavaVirtualMember(self, "value", java_type.generic_t),
677 JavaVirtualMember(self, "mask", java_type.generic_t),
678 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
679 JavaVirtualMember(self, "masked", java_type.boolean),
680 )
681 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
682 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
683 if self.name in model.oxm_map \
684 else java_type.make_match_field_jtype()
685
686 return (
687 JavaVirtualMember(self, "matchField", field_type),
688 JavaVirtualMember(self, "masked", java_type.boolean),
689 ) \
690 + \
691 (
692 ( 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
693 ()
694 )
695 else:
696 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700697
698 @property
699 @memoize
700 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700701 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700702 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 -0700703
704 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700705 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700706 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700707 return len(self.all_versions) == len(model.versions)
708
709 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700710 @memoize
711 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700712 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700713 return self.version_map.keys()
714
Andreas Wundsam5204de22013-07-30 11:34:45 -0700715 def has_version(self, version):
716 return version in self.version_map
717
Andreas Wundsam27303462013-07-16 12:52:35 -0700718 def versioned_class(self, version):
719 return JavaOFClass(self, version, self.version_map[version])
720
721 @property
722 @memoize
723 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700724 return [ self.versioned_class(version) for version in self.all_versions ]
725
726#######################################################################
727### (Versioned) Classes
728#######################################################################
729
730class JavaOFClass(object):
731 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700732 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700733 """
734 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700735 """
736 @param interface JavaOFInterface instance of the parent interface
737 @param version JavaOFVersion
738 @param ir_class OFClass from loxi_ir
739 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700740 self.interface = interface
741 self.ir_class = ir_class
742 self.c_name = self.ir_class.name
743 self.version = version
744 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700745 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700746 self.generated = False
747
748 @property
749 @memoize
750 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700751 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700752
753 @property
754 def name(self):
755 return "%sVer%s" % (self.interface.name, self.version.of_version)
756
757 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700758 def variable_name(self):
759 return self.name[3:]
760
761 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700762 def length(self):
763 if self.is_fixed_length:
764 return self.min_length
765 else:
766 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
767
768 @property
769 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700770 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700771 id_tuple = (self.ir_class.name, self.version.int_version)
772 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
773
774 @property
775 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700776 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700777 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
778 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700779
780 def all_properties(self):
781 return self.interface.members
782
783 def get_member(self, name):
784 for m in self.members:
785 if m.name == name:
786 return m
787
788 @property
789 @memoize
790 def data_members(self):
791 return [ prop for prop in self.members if prop.is_data ]
792
793 @property
794 @memoize
795 def fixed_value_members(self):
796 return [ prop for prop in self.members if prop.is_fixed_value ]
797
798 @property
799 @memoize
800 def public_members(self):
801 return [ prop for prop in self.members if prop.is_public ]
802
803 @property
804 @memoize
805 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700806 return self.ir_model_members + self.virtual_members
807
808 @property
809 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700810 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700811 return tuple(members)
812
813 @property
814 def virtual_members(self):
815 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
816 if self.interface.name in model.oxm_map:
817 oxm_entry = model.oxm_map[self.interface.name]
818 return (
819 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
820 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
821 )
822 else:
823 return (
824 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
825 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
826 )
827 else:
828 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700829
830 def all_versions(self):
831 return [ JavaOFVersion(int_version)
832 for int_version in of_g.unified[self.c_name]
833 if int_version != 'union' and int_version != 'object_id' ]
834
835 def version_is_inherited(self, version):
836 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
837
838 def inherited_from(self, version):
839 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
840
841 @property
842 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700843 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
844
845 @property
846 def discriminator(self):
847 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700848
849 @property
850 def is_extension(self):
851 return type_maps.message_is_extension(self.c_name, -1)
852
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700853 @property
854 def align(self):
855 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
856
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700857 @property
858 @memoize
859 def superclass(self):
860 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
861
862 @property
863 @memoize
864 def subclasses(self):
865 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
866
Andreas Wundsam27303462013-07-16 12:52:35 -0700867#######################################################################
868### Member
869#######################################################################
870
871
872class JavaMember(object):
873 """ Models a property (member) of an openflow class. """
874 def __init__(self, msg, name, java_type, member):
875 self.msg = msg
876 self.name = name
877 self.java_type = java_type
878 self.member = member
879 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700880
881 @property
882 def title_name(self):
883 return self.name[0].upper() + self.name[1:]
884
885 @property
886 def constant_name(self):
887 return self.c_name.upper()
888
889 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700890 def getter_name(self):
891 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
892
893 @property
894 def setter_name(self):
895 return "set" + self.title_name
896
897 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700898 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700899 if self.is_fixed_value:
900 return self.constant_name
901 else:
902 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700903
904 @property
905 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700906 if self.is_fixed_value:
907 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700908 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700909 default = self.java_type.default_op(self.msg.version)
910 if default == "null" and not self.is_nullable:
911 return None
912 else:
913 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700914
915 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700916 def enum_value(self):
917 if self.name == "version":
918 return "OFVersion.%s" % self.msg.version.constant_version
919
920 java_type = self.java_type.public_type;
921 try:
922 global model
923 enum = model.enum_by_name(java_type)
924 entry = enum.entry_by_version_value(self.msg.version, self.value)
925 return "%s.%s" % ( enum.name, entry.name)
926 except KeyError, e:
927 print e.message
928 return self.value
929
930 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700931 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700932 return isinstance(self.member, OFPadMember)
933
934 def is_type_value(self, version=None):
935 if(version==None):
936 return any(self.is_type_value(version) for version in self.msg.all_versions)
937 try:
938 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
939 except:
940 return False
941
942 @property
943 def is_field_length_value(self):
944 return isinstance(self.member, OFFieldLengthMember)
945
946 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700947 def is_discriminator(self):
948 return isinstance(self.member, OFDiscriminatorMember)
949
950 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700951 def is_length_value(self):
952 return isinstance(self.member, OFLengthMember)
953
954 @property
955 def is_public(self):
956 return not (self.is_pad or self.is_length_value)
957
958 @property
959 def is_data(self):
960 return isinstance(self.member, OFDataMember) and self.name != "version"
961
962 @property
963 def is_fixed_value(self):
964 return hasattr(self.member, "value") or self.name == "version" \
965 or ( self.name == "length" and self.msg.is_fixed_length) \
966 or ( self.name == "len" and self.msg.is_fixed_length)
967
968 @property
969 def value(self):
970 if self.name == "version":
971 return self.msg.version.int_version
972 elif self.name == "length" or self.name == "len":
973 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700974 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700975 return self.java_type.format_value(self.member.value)
976
977 @property
978 def priv_value(self):
979 if self.name == "version":
980 return self.msg.version.int_version
981 elif self.name == "length" or self.name == "len":
982 return self.msg.length
983 else:
984 return self.java_type.format_value(self.member.value, pub_type=False)
985
Andreas Wundsam27303462013-07-16 12:52:35 -0700986
987 @property
988 def is_writeable(self):
989 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
990
991 def get_type_value_info(self, version):
992 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700993
994 @property
995 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700996 if hasattr(self.member, "length"):
997 return self.member.length
998 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700999 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -07001000 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001001
1002 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -07001003 def for_of_member(java_class, member):
1004 if isinstance(member, OFPadMember):
1005 return JavaMember(None, "", None, member)
1006 else:
1007 if member.name == 'len':
1008 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001009 elif member.name == 'value_mask':
1010 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -07001011 else:
1012 name = java_type.name_c_to_camel(member.name)
1013 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
1014 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001015
1016 @property
1017 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -07001018 if not self.msg.c_name in of_g.unified:
1019 print("%s not self.unified" % self.msg.c_name)
1020 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001021 for version in of_g.unified[self.msg.c_name]:
1022 if version == 'union' or version =='object_id':
1023 continue
1024 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1025 continue
1026
Andreas Wundsam27303462013-07-16 12:52:35 -07001027 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 -07001028 return False
1029 return True
1030
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001031 @property
1032 def is_virtual(self):
1033 return False
1034
Andreas Wundsam27303462013-07-16 12:52:35 -07001035 def __hash__(self):
1036 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001037
Andreas Wundsam27303462013-07-16 12:52:35 -07001038 def __eq__(self, other):
1039 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001040 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001041 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001042
Andreas Wundsamf1949682013-09-23 14:48:31 -07001043 @property
1044 def is_nullable(self):
1045 return self.name in model.nullable_map[self.msg.name]
1046
1047
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001048class JavaVirtualMember(JavaMember):
1049 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1050 def __init__(self, msg, name, java_type, value=None):
1051 JavaMember.__init__(self, msg, name, java_type, member=None)
1052 self._value = value
1053
1054 @property
1055 def is_fixed_value(self):
1056 return True
1057
1058 @property
1059 def value(self):
1060 return self._value
1061
1062 @property
1063 def priv_value(self):
1064 return self._value
1065
1066
1067 @property
1068 def is_universal(self):
1069 return True
1070
1071 @property
1072 def is_virtual(self):
1073 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001074
1075#######################################################################
1076### Unit Test
1077#######################################################################
1078
Yotam Harchol466b3212013-08-15 12:14:46 -07001079class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001080 def __init__(self, java_class):
1081 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001082 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001083 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001084 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1085 name=java_class.c_name[3:]) + "{i}.data"
1086 test_class_name = self.java_class.name + "Test"
1087 self.test_units = []
1088 if test_data.exists(first_data_file_name):
1089 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1090 i = 1
1091 while test_data.exists(data_file_template.format(i=i)):
1092 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1093 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001094
Yotam Harchol466b3212013-08-15 12:14:46 -07001095 @property
1096 def package(self):
1097 return self.java_class.package
1098
1099 @property
1100 def has_test_data(self):
1101 return len(self.test_units) > 0
1102
1103 @property
1104 def length(self):
1105 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001106
Yotam Harchol466b3212013-08-15 12:14:46 -07001107 def get_test_unit(self, i):
1108 return self.test_units[i]
1109
1110
1111class JavaUnitTest(object):
1112 def __init__(self, java_class, file_name=None, test_class_name=None):
1113 self.java_class = java_class
1114 if file_name is None:
1115 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1116 name=java_class.c_name[3:])
1117 else:
1118 self.data_file_name = file_name
1119 if test_class_name is None:
1120 self.test_class_name = self.java_class.name + "Test"
1121 else:
1122 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001123
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001124 @property
1125 def package(self):
1126 return self.java_class.package
1127
1128 @property
1129 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001130 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001131
1132 @property
1133 def interface(self):
1134 return self.java_class.interface
1135
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001136 @property
1137 def has_test_data(self):
1138 return test_data.exists(self.data_file_name)
1139
1140 @property
1141 @memoize
1142 def test_data(self):
1143 return test_data.read(self.data_file_name)
1144
1145
Andreas Wundsam27303462013-07-16 12:52:35 -07001146#######################################################################
1147### Enums
1148#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001149
Andreas Wundsam27303462013-07-16 12:52:35 -07001150class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001151 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001152 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001153
1154 if c_name == "of_stats_types":
1155 self.name = "OFStatsType"
1156 else:
1157 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001158
Andreas Wundsam27303462013-07-16 12:52:35 -07001159 # Port_features has constants that start with digits
1160 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001161
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001162 self.version_enums = version_enum_map
1163
1164 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1165 for version, ir_enum in version_enum_map.items():
1166 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001167 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1168
Andreas Wundsam27303462013-07-16 12:52:35 -07001169 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001170 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001171
1172 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 -07001173 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001174
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001175 self.metadata = model.enum_metadata_map[self.name]
1176
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001177 def wire_type(self, version):
1178 ir_enum = self.version_enums[version]
1179 if "wire_type" in ir_enum.params:
1180 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1181 else:
1182 return java_type.u8
1183
1184 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001185 @memoize
1186 def is_bitmask(self):
1187 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1188
1189 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001190 def versions(self):
1191 return self.version_enums.keys()
1192
Andreas Wundsam27303462013-07-16 12:52:35 -07001193 @memoize
1194 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001195 res = find(lambda e: e.name == name, self.entries)
1196 if res:
1197 return res
1198 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001199 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1200
1201 @memoize
1202 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001203 res = find(lambda e: e.c_name == name, self.entries)
1204 if res:
1205 return res
1206 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001207 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1208
1209 @memoize
1210 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001211 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1212 if res:
1213 return res
1214 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001215 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1216
1217# values: Map JavaVersion->Value
1218class JavaEnumEntry(object):
1219 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001220 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001221 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1222 self.values = values
1223
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001224 @property
1225 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001226 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001227
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001228 def has_value(self, version):
1229 return version in self.values
1230
Andreas Wundsam27303462013-07-16 12:52:35 -07001231 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001232 return self.values[version]
1233
1234 def format_value(self, version):
1235 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001236 return res
1237
Andreas Wundsam27303462013-07-16 12:52:35 -07001238 def all_values(self, versions, not_present=None):
1239 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001240
1241 @property
1242 @memoize
1243 def masked_enum_group(self):
1244 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1245 return group
1246
1247 @property
1248 @memoize
1249 def is_mask(self):
1250 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])