blob: 1bc75e63e390f6a14d75ccfa52e512da6410f643 [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 Harchol595c6442013-09-27 16:29:08 -0700233 "OFOxmBsnInPorts128": OxmMapEntry("OFPortBitmap", "BSN_IN_PORTS_128", False),
234 "OFOxmBsnInPorts128Masked": OxmMapEntry("OFPortBitmap", "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),
383 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={})
384
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(
390 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ))
391
392 for i in self.interfaces:
393 for n, factory in factories.items():
394 if n == "":
395 factory.members.append(i)
396 break
397 else:
398 super_class = self.interface_by_name(n)
399 if i.is_instance_of(super_class):
400 factory.members.append(i)
401 break
402 return factories.values()
Yotam Harchol595c6442013-09-27 16:29:08 -0700403
404 @memoize
405 def factory_of(self, interface):
406 for factory in self.of_factories:
407 if interface in factory.members:
408 return factory
409 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700410
411 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700412 """ return wether or not to generate implementation class clazz.
413 Now true for everything except OFTableModVer10.
414 @param clazz JavaOFClass instance
415 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700416 if clazz.interface.name.startswith("OFMatchV"):
417 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700418 elif clazz.name == "OFTableModVer10":
419 # tablemod ver 10 is a hack and has no oftype defined
420 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700421 if loxi_utils.class_is_message(clazz.interface.c_name):
422 return True
423 if loxi_utils.class_is_oxm(clazz.interface.c_name):
424 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700425 if loxi_utils.class_is_action(clazz.interface.c_name):
426 return True
427 if loxi_utils.class_is_instruction(clazz.interface.c_name):
428 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700429 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700430 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700431
432
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700433class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700434 @property
435 def factory_classes(self):
436 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700437 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700438 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700439 interface=self,
440 version=version
441 ) for version in model.versions ]
442
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700443 def method_name(self, member, builder=True):
444 n = member.variable_name
445 if n.startswith(self.remove_prefix):
446 n = n[len(self.remove_prefix):]
447 n = n[0].lower() + n[1:]
448 if builder:
449 return "build" + n[0].upper() + n[1:]
450 else:
451 return n
Yotam Harchol595c6442013-09-27 16:29:08 -0700452
453 def of_version(self, version):
454 for fc in self.factory_classes:
455 if fc.version == version:
456 return fc
457 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700458
459OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700460class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
461 @property
462 def base_class(self):
463 return self.interface.base_class
464
465 @property
466 def versioned_base_class(self):
467 base_class_interface = model.interface_by_name(self.interface.base_class)
468 if base_class_interface and base_class_interface.has_version(self.version):
469 return base_class_interface.versioned_class(self.version)
470 else:
471 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700472
Andreas Wundsam27303462013-07-16 12:52:35 -0700473model = JavaModel()
474
475#######################################################################
476### OFVersion
477#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700478
479class JavaOFVersion(object):
480 """ Models a version of OpenFlow. contains methods to convert the internal
481 Loxi version to a java constant / a string """
482 def __init__(self, int_version):
483 self.int_version = int(int_version)
484
485 @property
486 def of_version(self):
487 return "1" + str(int(self.int_version) - 1)
488
489 @property
490 def constant_version(self):
491 return "OF_" + self.of_version
492
Andreas Wundsam27303462013-07-16 12:52:35 -0700493 def __repr__(self):
494 return "JavaOFVersion(%d)" % self.int_version
495
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700496 def __str__(self):
497 return of_g.param_version_names[self.int_version]
498
Andreas Wundsam27303462013-07-16 12:52:35 -0700499 def __hash__(self):
500 return hash(self.int_version)
501
502 def __eq__(self, other):
503 if other is None or type(self) != type(other):
504 return False
505 return (self.int_version,) == (other.int_version,)
506
507#######################################################################
508### Interface
509#######################################################################
510
511class JavaOFInterface(object):
512 """ Models an OpenFlow Message class for the purpose of the java class.
513 Version agnostic, in contrast to the loxi_ir python model.
514 """
515 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700516 """"
517 @param c_name: loxi style name (e.g., of_flow_add)
518 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
519 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700520 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700521 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700522 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700523 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 -0700524 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700525 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700526 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700527 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700528 self.constant_name = c_name.upper().replace("OF_", "")
529
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700530 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700531 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700532 if self.name != parent_interface:
533 self.parent_interface = parent_interface
534 else:
535 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700536
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700537 @property
538 @memoize
539 def all_parent_interfaces(self):
540 return [ "OFObject" ] + \
541 ([ self.parent_interface ] if self.parent_interface else [] )+ \
542 self.additional_parent_interfaces
543 @property
544 @memoize
545 def additional_parent_interfaces(self):
546 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
547 m = re.match(r'(.*)Request$', self.name)
548 if m:
549 reply_name = m.group(1) + "Reply"
550 if model.interface_by_name(reply_name):
551 return ["OFRequest<%s>" % reply_name ]
552 return []
553
554
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700555 def is_instance_of(self, other_class):
556 if self == other_class:
557 return True
558 parent = self.super_class
559 if parent is None:
560 return False
561 else:
562 return parent.is_instance_of(other_class)
563
564 @property
565 def super_class(self):
566 if not self.parent_interface:
567 return None
568 else:
569 return model.interface_by_name(self.parent_interface)
570
571
572 def inherited_declaration(self, type_spec="?"):
573 if self.type_annotation:
574 return "%s<%s>" % (self.name, type_spec)
575 else:
576 return "%s" % self.name
577
578 @property
579 def type_variable(self):
580 if self.type_annotation:
581 return "<T>"
582 else:
583 return "";
584
Andreas Wundsam27303462013-07-16 12:52:35 -0700585 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700586 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
587 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
588 # model (note, that the loxi model is on versioned classes). Should check/infer the
589 # inheritance information from the versioned lox_ir classes.
Andreas Wundsam001b1822013-08-02 22:25:55 -0700590 if re.match(r'OF.+StatsRequest$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700591 return ("", "OFStatsRequest", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700592 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700593 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700594 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700595 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700596 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 -0700597 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700598 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 -0700599 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700600 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
601 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700602 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700603 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700604 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700605 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700606 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700607 if re.match(r'OFActionBsn.+', self.name):
608 return ("action", "OFActionBsn", None)
609 elif re.match(r'OFActionNicira.+', self.name):
610 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700611 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
612 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700613 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700614 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700615 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700616 return ("", "OFBsnVport", None)
617 elif self.name == "OFOxm":
618 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700619 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700620 if self.name in model.oxm_map:
621 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
622 else:
623 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700624 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700625 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700626 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700627 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700628 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700629 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700630 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700631 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700632 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700633 return ("", None, None)
634
635 @property
636 @memoize
637 def writeable_members(self):
638 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700639
640 @property
641 @memoize
642 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700643 return self.ir_model_members + self.virtual_members
644
645 @property
646 @memoize
647 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700648 """return a list of all members to be exposed by this interface. Corresponds to
649 the union of the members of the vesioned classes without length, fieldlength
650 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700651 all_versions = []
652 member_map = collections.OrderedDict()
653
654 for (version, of_class) in self.version_map.items():
655 for of_member in of_class.members:
656 if isinstance(of_member, OFLengthMember) or \
657 isinstance(of_member, OFFieldLengthMember) or \
658 isinstance(of_member, OFPadMember):
659 continue
660 if of_member.name not in member_map:
661 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
662
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700663 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 -0700664
665 @property
666 def virtual_members(self):
667 if self.name == "OFOxm":
668 return (
669 JavaVirtualMember(self, "value", java_type.generic_t),
670 JavaVirtualMember(self, "mask", java_type.generic_t),
671 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
672 JavaVirtualMember(self, "masked", java_type.boolean),
673 )
674 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
675 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
676 if self.name in model.oxm_map \
677 else java_type.make_match_field_jtype()
678
679 return (
680 JavaVirtualMember(self, "matchField", field_type),
681 JavaVirtualMember(self, "masked", java_type.boolean),
682 ) \
683 + \
684 (
685 ( 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
686 ()
687 )
688 else:
689 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700690
691 @property
692 @memoize
693 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700694 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700695 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 -0700696
697 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700698 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700699 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700700 return len(self.all_versions) == len(model.versions)
701
702 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700703 @memoize
704 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700705 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700706 return self.version_map.keys()
707
Andreas Wundsam5204de22013-07-30 11:34:45 -0700708 def has_version(self, version):
709 return version in self.version_map
710
Andreas Wundsam27303462013-07-16 12:52:35 -0700711 def versioned_class(self, version):
712 return JavaOFClass(self, version, self.version_map[version])
713
714 @property
715 @memoize
716 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700717 return [ self.versioned_class(version) for version in self.all_versions ]
718
719#######################################################################
720### (Versioned) Classes
721#######################################################################
722
723class JavaOFClass(object):
724 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700725 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700726 """
727 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700728 """
729 @param interface JavaOFInterface instance of the parent interface
730 @param version JavaOFVersion
731 @param ir_class OFClass from loxi_ir
732 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700733 self.interface = interface
734 self.ir_class = ir_class
735 self.c_name = self.ir_class.name
736 self.version = version
737 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700738 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700739 self.generated = False
740
741 @property
742 @memoize
743 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700744 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700745
746 @property
747 def name(self):
748 return "%sVer%s" % (self.interface.name, self.version.of_version)
749
750 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700751 def variable_name(self):
752 return self.name[3:]
753
754 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700755 def length(self):
756 if self.is_fixed_length:
757 return self.min_length
758 else:
759 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
760
761 @property
762 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700763 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700764 id_tuple = (self.ir_class.name, self.version.int_version)
765 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
766
767 @property
768 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700769 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700770 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
771 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700772
773 def all_properties(self):
774 return self.interface.members
775
776 def get_member(self, name):
777 for m in self.members:
778 if m.name == name:
779 return m
780
781 @property
782 @memoize
783 def data_members(self):
784 return [ prop for prop in self.members if prop.is_data ]
785
786 @property
787 @memoize
788 def fixed_value_members(self):
789 return [ prop for prop in self.members if prop.is_fixed_value ]
790
791 @property
792 @memoize
793 def public_members(self):
794 return [ prop for prop in self.members if prop.is_public ]
795
796 @property
797 @memoize
798 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700799 return self.ir_model_members + self.virtual_members
800
801 @property
802 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700803 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700804 return tuple(members)
805
806 @property
807 def virtual_members(self):
808 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
809 if self.interface.name in model.oxm_map:
810 oxm_entry = model.oxm_map[self.interface.name]
811 return (
812 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
813 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
814 )
815 else:
816 return (
817 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
818 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
819 )
820 else:
821 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700822
823 def all_versions(self):
824 return [ JavaOFVersion(int_version)
825 for int_version in of_g.unified[self.c_name]
826 if int_version != 'union' and int_version != 'object_id' ]
827
828 def version_is_inherited(self, version):
829 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
830
831 def inherited_from(self, version):
832 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
833
834 @property
835 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700836 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
837
838 @property
839 def discriminator(self):
840 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700841
842 @property
843 def is_extension(self):
844 return type_maps.message_is_extension(self.c_name, -1)
845
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700846 @property
847 def align(self):
848 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
849
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700850 @property
851 @memoize
852 def superclass(self):
853 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
854
855 @property
856 @memoize
857 def subclasses(self):
858 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
859
Andreas Wundsam27303462013-07-16 12:52:35 -0700860#######################################################################
861### Member
862#######################################################################
863
864
865class JavaMember(object):
866 """ Models a property (member) of an openflow class. """
867 def __init__(self, msg, name, java_type, member):
868 self.msg = msg
869 self.name = name
870 self.java_type = java_type
871 self.member = member
872 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700873
874 @property
875 def title_name(self):
876 return self.name[0].upper() + self.name[1:]
877
878 @property
879 def constant_name(self):
880 return self.c_name.upper()
881
882 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700883 def getter_name(self):
884 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
885
886 @property
887 def setter_name(self):
888 return "set" + self.title_name
889
890 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700891 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700892 if self.is_fixed_value:
893 return self.constant_name
894 else:
895 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700896
897 @property
898 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700899 if self.is_fixed_value:
900 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700901 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700902 default = self.java_type.default_op(self.msg.version)
903 if default == "null" and not self.is_nullable:
904 return None
905 else:
906 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700907
908 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700909 def enum_value(self):
910 if self.name == "version":
911 return "OFVersion.%s" % self.msg.version.constant_version
912
913 java_type = self.java_type.public_type;
914 try:
915 global model
916 enum = model.enum_by_name(java_type)
917 entry = enum.entry_by_version_value(self.msg.version, self.value)
918 return "%s.%s" % ( enum.name, entry.name)
919 except KeyError, e:
920 print e.message
921 return self.value
922
923 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700924 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700925 return isinstance(self.member, OFPadMember)
926
927 def is_type_value(self, version=None):
928 if(version==None):
929 return any(self.is_type_value(version) for version in self.msg.all_versions)
930 try:
931 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
932 except:
933 return False
934
935 @property
936 def is_field_length_value(self):
937 return isinstance(self.member, OFFieldLengthMember)
938
939 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700940 def is_discriminator(self):
941 return isinstance(self.member, OFDiscriminatorMember)
942
943 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700944 def is_length_value(self):
945 return isinstance(self.member, OFLengthMember)
946
947 @property
948 def is_public(self):
949 return not (self.is_pad or self.is_length_value)
950
951 @property
952 def is_data(self):
953 return isinstance(self.member, OFDataMember) and self.name != "version"
954
955 @property
956 def is_fixed_value(self):
957 return hasattr(self.member, "value") or self.name == "version" \
958 or ( self.name == "length" and self.msg.is_fixed_length) \
959 or ( self.name == "len" and self.msg.is_fixed_length)
960
961 @property
962 def value(self):
963 if self.name == "version":
964 return self.msg.version.int_version
965 elif self.name == "length" or self.name == "len":
966 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700967 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700968 return self.java_type.format_value(self.member.value)
969
970 @property
971 def priv_value(self):
972 if self.name == "version":
973 return self.msg.version.int_version
974 elif self.name == "length" or self.name == "len":
975 return self.msg.length
976 else:
977 return self.java_type.format_value(self.member.value, pub_type=False)
978
Andreas Wundsam27303462013-07-16 12:52:35 -0700979
980 @property
981 def is_writeable(self):
982 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
983
984 def get_type_value_info(self, version):
985 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700986
987 @property
988 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700989 if hasattr(self.member, "length"):
990 return self.member.length
991 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700992 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700993 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700994
995 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700996 def for_of_member(java_class, member):
997 if isinstance(member, OFPadMember):
998 return JavaMember(None, "", None, member)
999 else:
1000 if member.name == 'len':
1001 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001002 elif member.name == 'value_mask':
1003 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -07001004 else:
1005 name = java_type.name_c_to_camel(member.name)
1006 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
1007 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001008
1009 @property
1010 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -07001011 if not self.msg.c_name in of_g.unified:
1012 print("%s not self.unified" % self.msg.c_name)
1013 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001014 for version in of_g.unified[self.msg.c_name]:
1015 if version == 'union' or version =='object_id':
1016 continue
1017 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1018 continue
1019
Andreas Wundsam27303462013-07-16 12:52:35 -07001020 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 -07001021 return False
1022 return True
1023
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001024 @property
1025 def is_virtual(self):
1026 return False
1027
Andreas Wundsam27303462013-07-16 12:52:35 -07001028 def __hash__(self):
1029 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001030
Andreas Wundsam27303462013-07-16 12:52:35 -07001031 def __eq__(self, other):
1032 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001033 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001034 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001035
Andreas Wundsamf1949682013-09-23 14:48:31 -07001036 @property
1037 def is_nullable(self):
1038 return self.name in model.nullable_map[self.msg.name]
1039
1040
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001041class JavaVirtualMember(JavaMember):
1042 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1043 def __init__(self, msg, name, java_type, value=None):
1044 JavaMember.__init__(self, msg, name, java_type, member=None)
1045 self._value = value
1046
1047 @property
1048 def is_fixed_value(self):
1049 return True
1050
1051 @property
1052 def value(self):
1053 return self._value
1054
1055 @property
1056 def priv_value(self):
1057 return self._value
1058
1059
1060 @property
1061 def is_universal(self):
1062 return True
1063
1064 @property
1065 def is_virtual(self):
1066 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001067
1068#######################################################################
1069### Unit Test
1070#######################################################################
1071
Yotam Harchol466b3212013-08-15 12:14:46 -07001072class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001073 def __init__(self, java_class):
1074 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001075 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001076 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001077 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1078 name=java_class.c_name[3:]) + "{i}.data"
1079 test_class_name = self.java_class.name + "Test"
1080 self.test_units = []
1081 if test_data.exists(first_data_file_name):
1082 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1083 i = 1
1084 while test_data.exists(data_file_template.format(i=i)):
1085 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1086 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001087
Yotam Harchol466b3212013-08-15 12:14:46 -07001088 @property
1089 def package(self):
1090 return self.java_class.package
1091
1092 @property
1093 def has_test_data(self):
1094 return len(self.test_units) > 0
1095
1096 @property
1097 def length(self):
1098 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001099
Yotam Harchol466b3212013-08-15 12:14:46 -07001100 def get_test_unit(self, i):
1101 return self.test_units[i]
1102
1103
1104class JavaUnitTest(object):
1105 def __init__(self, java_class, file_name=None, test_class_name=None):
1106 self.java_class = java_class
1107 if file_name is None:
1108 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1109 name=java_class.c_name[3:])
1110 else:
1111 self.data_file_name = file_name
1112 if test_class_name is None:
1113 self.test_class_name = self.java_class.name + "Test"
1114 else:
1115 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001116
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001117 @property
1118 def package(self):
1119 return self.java_class.package
1120
1121 @property
1122 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001123 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001124
1125 @property
1126 def interface(self):
1127 return self.java_class.interface
1128
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001129 @property
1130 def has_test_data(self):
1131 return test_data.exists(self.data_file_name)
1132
1133 @property
1134 @memoize
1135 def test_data(self):
1136 return test_data.read(self.data_file_name)
1137
1138
Andreas Wundsam27303462013-07-16 12:52:35 -07001139#######################################################################
1140### Enums
1141#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001142
Andreas Wundsam27303462013-07-16 12:52:35 -07001143class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001144 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001145 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001146
1147 if c_name == "of_stats_types":
1148 self.name = "OFStatsType"
1149 else:
1150 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001151
Andreas Wundsam27303462013-07-16 12:52:35 -07001152 # Port_features has constants that start with digits
1153 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001154
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001155 self.version_enums = version_enum_map
1156
1157 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1158 for version, ir_enum in version_enum_map.items():
1159 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001160 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1161
Andreas Wundsam27303462013-07-16 12:52:35 -07001162 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001163 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001164
1165 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 -07001166 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001167
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001168 self.metadata = model.enum_metadata_map[self.name]
1169
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001170 def wire_type(self, version):
1171 ir_enum = self.version_enums[version]
1172 if "wire_type" in ir_enum.params:
1173 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1174 else:
1175 return java_type.u8
1176
1177 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001178 @memoize
1179 def is_bitmask(self):
1180 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1181
1182 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001183 def versions(self):
1184 return self.version_enums.keys()
1185
Andreas Wundsam27303462013-07-16 12:52:35 -07001186 @memoize
1187 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001188 res = find(lambda e: e.name == name, self.entries)
1189 if res:
1190 return res
1191 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001192 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1193
1194 @memoize
1195 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001196 res = find(lambda e: e.c_name == name, self.entries)
1197 if res:
1198 return res
1199 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001200 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1201
1202 @memoize
1203 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001204 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1205 if res:
1206 return res
1207 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001208 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1209
1210# values: Map JavaVersion->Value
1211class JavaEnumEntry(object):
1212 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001213 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001214 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1215 self.values = values
1216
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001217 @property
1218 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001219 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001220
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001221 def has_value(self, version):
1222 return version in self.values
1223
Andreas Wundsam27303462013-07-16 12:52:35 -07001224 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001225 return self.values[version]
1226
1227 def format_value(self, version):
1228 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001229 return res
1230
Andreas Wundsam27303462013-07-16 12:52:35 -07001231 def all_values(self, versions, not_present=None):
1232 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001233
1234 @property
1235 @memoize
1236 def masked_enum_group(self):
1237 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1238 return group
1239
1240 @property
1241 @memoize
1242 def is_mask(self):
1243 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])