blob: 033ff2039a3acf4d757e3c36edcc3b511cb27af5 [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),
232 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True)
233 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700234
Andreas Wundsamf1949682013-09-23 14:48:31 -0700235 # Registry of nullable properties:
236 # ${java_class_name} -> set(${java_property_name})
237 nullable_map = defaultdict(lambda: set(),
238 )
239
240 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
241 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
242 # name: a name for the group
243 # mask: java name of the enum entry that defines the mask
244 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700245 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
246
Andreas Wundsamf1949682013-09-23 14:48:31 -0700247 # registry of MaskedEnumGroups (see above).
248 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700249 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700250 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
251 OFConfigFlags = (
252 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700253 ),
254 OFTableConfig = (
255 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
256 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700257 )
258
Andreas Wundsamf1949682013-09-23 14:48:31 -0700259 # represents a metadata property associated with an EnumClass
260 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700261 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700262 """
263 represents a metadata property associated with an Enum Class
264 @param name name of metadata property
265 @param type java_type instance describing the type
266 @value: Generator function f(entry) that generates the value
267 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700268 @property
269 def variable_name(self):
270 return self.name[0].lower() + self.name[1:]
271
272 @property
273 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700274 prefix = "is" if self.type == java_type.boolean else "get"
275 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700276
Andreas Wundsamf1949682013-09-23 14:48:31 -0700277 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700278 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
279
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700280 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700281 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700282 splits = enum_entry.name.split("_")
283 if len(splits)>=2:
284 m = re.match(r'\d+[MGTP]B', splits[1])
285 if m:
286 return "PortSpeed.SPEED_{}".format(splits[1])
287 return "PortSpeed.SPEED_NONE";
288
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700289 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700290 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700291 splits = enum_entry.name.split("_")
292 if len(splits)>=1:
293 if splits[0] == "STP":
294 return "true"
295 return "false"
296
Andreas Wundsamf1949682013-09-23 14:48:31 -0700297 # registry for metadata properties for enums
298 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700299 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700300 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
301 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700302 OFErrorCode = OFEnumMetadata((OFEnumPropertyMetadata("ErrorType", java_type.error_type, gen_error_type),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700303 )
304
Andreas Wundsam27303462013-07-16 12:52:35 -0700305 @property
306 @memoize
307 def versions(self):
308 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
309
310 @property
311 @memoize
312 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700313 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700314
315 for raw_version, of_protocol in of_g.ir.items():
316 jversion = JavaOFVersion(of_protocol.wire_version)
317
318 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700319 if not of_class.name in version_map_per_class:
320 version_map_per_class[of_class.name] = collections.OrderedDict()
321
Andreas Wundsam27303462013-07-16 12:52:35 -0700322 version_map_per_class[of_class.name][jversion] = of_class
323
324 interfaces = []
325 for class_name, version_map in version_map_per_class.items():
326 interfaces.append(JavaOFInterface(class_name, version_map))
327
Andreas Wundsambe168f72013-08-03 22:49:35 -0700328 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
329
Andreas Wundsam27303462013-07-16 12:52:35 -0700330 return interfaces
331
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700332 @memoize
333 def interface_by_name(self, name):
334 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
335
Andreas Wundsam27303462013-07-16 12:52:35 -0700336 @property
337 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700338 def all_classes(self):
339 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
340
341 @property
342 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700343 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700344 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700345
346 for version in self.versions:
347 of_protocol = of_g.ir[version.int_version]
348 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700349 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700350
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700351 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
352 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700353
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700354 # inelegant - need java name here
355 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700356 return enums
357
358 @memoize
359 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700360 res = find(lambda e: e.name == name, self.enums)
361 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700362 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700363 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700364
Andreas Wundsam5204de22013-07-30 11:34:45 -0700365 @property
366 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700367 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700368 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700369
370 factories = OrderedDict()
371
372 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
373 for base_class in sub_factory_classes:
374 package = base_class[2:].lower()
375 remove_prefix = base_class[2].lower() + base_class[3:]
376
377 # HACK need to have a better way to deal with parameterized base classes
378 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
379
380 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
381 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={})
382
383 factories[""] = OFFactory(
384 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700385 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700386 remove_prefix="",
387 members=[], base_class="OFMessage", sub_factories=OrderedDict(
388 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ))
389
390 for i in self.interfaces:
391 for n, factory in factories.items():
392 if n == "":
393 factory.members.append(i)
394 break
395 else:
396 super_class = self.interface_by_name(n)
397 if i.is_instance_of(super_class):
398 factory.members.append(i)
399 break
400 return factories.values()
Andreas Wundsam5204de22013-07-30 11:34:45 -0700401
402 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700403 """ return wether or not to generate implementation class clazz.
404 Now true for everything except OFTableModVer10.
405 @param clazz JavaOFClass instance
406 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700407 if clazz.interface.name.startswith("OFMatchV"):
408 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700409 elif clazz.name == "OFTableModVer10":
410 # tablemod ver 10 is a hack and has no oftype defined
411 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700412 if loxi_utils.class_is_message(clazz.interface.c_name):
413 return True
414 if loxi_utils.class_is_oxm(clazz.interface.c_name):
415 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700416 if loxi_utils.class_is_action(clazz.interface.c_name):
417 return True
418 if loxi_utils.class_is_instruction(clazz.interface.c_name):
419 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700420 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700421 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700422
423
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700424class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700425 @property
426 def factory_classes(self):
427 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700428 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700429 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700430 interface=self,
431 version=version
432 ) for version in model.versions ]
433
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700434 def method_name(self, member, builder=True):
435 n = member.variable_name
436 if n.startswith(self.remove_prefix):
437 n = n[len(self.remove_prefix):]
438 n = n[0].lower() + n[1:]
439 if builder:
440 return "build" + n[0].upper() + n[1:]
441 else:
442 return n
Andreas Wundsam5204de22013-07-30 11:34:45 -0700443
444OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700445class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
446 @property
447 def base_class(self):
448 return self.interface.base_class
449
450 @property
451 def versioned_base_class(self):
452 base_class_interface = model.interface_by_name(self.interface.base_class)
453 if base_class_interface and base_class_interface.has_version(self.version):
454 return base_class_interface.versioned_class(self.version)
455 else:
456 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700457
Andreas Wundsam27303462013-07-16 12:52:35 -0700458model = JavaModel()
459
460#######################################################################
461### OFVersion
462#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700463
464class JavaOFVersion(object):
465 """ Models a version of OpenFlow. contains methods to convert the internal
466 Loxi version to a java constant / a string """
467 def __init__(self, int_version):
468 self.int_version = int(int_version)
469
470 @property
471 def of_version(self):
472 return "1" + str(int(self.int_version) - 1)
473
474 @property
475 def constant_version(self):
476 return "OF_" + self.of_version
477
Andreas Wundsam27303462013-07-16 12:52:35 -0700478 def __repr__(self):
479 return "JavaOFVersion(%d)" % self.int_version
480
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700481 def __str__(self):
482 return of_g.param_version_names[self.int_version]
483
Andreas Wundsam27303462013-07-16 12:52:35 -0700484 def __hash__(self):
485 return hash(self.int_version)
486
487 def __eq__(self, other):
488 if other is None or type(self) != type(other):
489 return False
490 return (self.int_version,) == (other.int_version,)
491
492#######################################################################
493### Interface
494#######################################################################
495
496class JavaOFInterface(object):
497 """ Models an OpenFlow Message class for the purpose of the java class.
498 Version agnostic, in contrast to the loxi_ir python model.
499 """
500 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700501 """"
502 @param c_name: loxi style name (e.g., of_flow_add)
503 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
504 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700505 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700506 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700507 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700508 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 -0700509 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700510 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700511 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700512 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700513 self.constant_name = c_name.upper().replace("OF_", "")
514
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700515 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700516 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700517 if self.name != parent_interface:
518 self.parent_interface = parent_interface
519 else:
520 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700521
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700522 @property
523 @memoize
524 def all_parent_interfaces(self):
525 return [ "OFObject" ] + \
526 ([ self.parent_interface ] if self.parent_interface else [] )+ \
527 self.additional_parent_interfaces
528 @property
529 @memoize
530 def additional_parent_interfaces(self):
531 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
532 m = re.match(r'(.*)Request$', self.name)
533 if m:
534 reply_name = m.group(1) + "Reply"
535 if model.interface_by_name(reply_name):
536 return ["OFRequest<%s>" % reply_name ]
537 return []
538
539
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700540 def is_instance_of(self, other_class):
541 if self == other_class:
542 return True
543 parent = self.super_class
544 if parent is None:
545 return False
546 else:
547 return parent.is_instance_of(other_class)
548
549 @property
550 def super_class(self):
551 if not self.parent_interface:
552 return None
553 else:
554 return model.interface_by_name(self.parent_interface)
555
556
557 def inherited_declaration(self, type_spec="?"):
558 if self.type_annotation:
559 return "%s<%s>" % (self.name, type_spec)
560 else:
561 return "%s" % self.name
562
563 @property
564 def type_variable(self):
565 if self.type_annotation:
566 return "<T>"
567 else:
568 return "";
569
Andreas Wundsam27303462013-07-16 12:52:35 -0700570 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700571 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
572 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
573 # model (note, that the loxi model is on versioned classes). Should check/infer the
574 # inheritance information from the versioned lox_ir classes.
Andreas Wundsamd6b0cb02013-09-28 18:55:56 -0700575 if re.match(r'OFStatsRequest$', self.name):
576 return ("", "OFMessage", "T extends OFStatsReply")
577 elif re.match(r'OF.+StatsRequest$', self.name):
578 return ("", "OFStatsRequest<{}>".format(re.sub(r'Request$', 'Reply', self.name)), None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700579 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700580 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700581 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700582 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700583 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 -0700584 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700585 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 -0700586 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700587 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
588 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700589 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700590 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700591 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700592 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700593 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700594 if re.match(r'OFActionBsn.+', self.name):
595 return ("action", "OFActionBsn", None)
596 elif re.match(r'OFActionNicira.+', self.name):
597 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700598 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
599 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700600 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700601 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700602 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700603 return ("", "OFBsnVport", None)
604 elif self.name == "OFOxm":
605 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700606 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700607 if self.name in model.oxm_map:
608 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
609 else:
610 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700611 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700612 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700613 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700614 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700615 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700616 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700617 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700618 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700619 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700620 return ("", None, None)
621
622 @property
623 @memoize
624 def writeable_members(self):
625 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700626
627 @property
628 @memoize
629 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700630 return self.ir_model_members + self.virtual_members
631
632 @property
633 @memoize
634 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700635 """return a list of all members to be exposed by this interface. Corresponds to
636 the union of the members of the vesioned classes without length, fieldlength
637 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700638 all_versions = []
639 member_map = collections.OrderedDict()
640
641 for (version, of_class) in self.version_map.items():
642 for of_member in of_class.members:
643 if isinstance(of_member, OFLengthMember) or \
644 isinstance(of_member, OFFieldLengthMember) or \
645 isinstance(of_member, OFPadMember):
646 continue
647 if of_member.name not in member_map:
648 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
649
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700650 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 -0700651
652 @property
653 def virtual_members(self):
654 if self.name == "OFOxm":
655 return (
656 JavaVirtualMember(self, "value", java_type.generic_t),
657 JavaVirtualMember(self, "mask", java_type.generic_t),
658 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
659 JavaVirtualMember(self, "masked", java_type.boolean),
660 )
661 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
662 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
663 if self.name in model.oxm_map \
664 else java_type.make_match_field_jtype()
665
666 return (
667 JavaVirtualMember(self, "matchField", field_type),
668 JavaVirtualMember(self, "masked", java_type.boolean),
669 ) \
670 + \
671 (
672 ( 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
673 ()
674 )
675 else:
676 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700677
678 @property
679 @memoize
680 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700681 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700682 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 -0700683
684 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700685 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700686 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700687 return len(self.all_versions) == len(model.versions)
688
689 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700690 @memoize
691 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700692 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700693 return self.version_map.keys()
694
Andreas Wundsam5204de22013-07-30 11:34:45 -0700695 def has_version(self, version):
696 return version in self.version_map
697
Andreas Wundsam27303462013-07-16 12:52:35 -0700698 def versioned_class(self, version):
699 return JavaOFClass(self, version, self.version_map[version])
700
701 @property
702 @memoize
703 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700704 return [ self.versioned_class(version) for version in self.all_versions ]
705
706#######################################################################
707### (Versioned) Classes
708#######################################################################
709
710class JavaOFClass(object):
711 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700712 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700713 """
714 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700715 """
716 @param interface JavaOFInterface instance of the parent interface
717 @param version JavaOFVersion
718 @param ir_class OFClass from loxi_ir
719 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700720 self.interface = interface
721 self.ir_class = ir_class
722 self.c_name = self.ir_class.name
723 self.version = version
724 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700725 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700726 self.generated = False
727
728 @property
729 @memoize
730 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700731 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700732
733 @property
734 def name(self):
735 return "%sVer%s" % (self.interface.name, self.version.of_version)
736
737 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700738 def variable_name(self):
739 return self.name[3:]
740
741 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700742 def length(self):
743 if self.is_fixed_length:
744 return self.min_length
745 else:
746 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
747
748 @property
749 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700750 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700751 id_tuple = (self.ir_class.name, self.version.int_version)
752 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
753
754 @property
755 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700756 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700757 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
758 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700759
760 def all_properties(self):
761 return self.interface.members
762
763 def get_member(self, name):
764 for m in self.members:
765 if m.name == name:
766 return m
767
768 @property
769 @memoize
770 def data_members(self):
771 return [ prop for prop in self.members if prop.is_data ]
772
773 @property
774 @memoize
775 def fixed_value_members(self):
776 return [ prop for prop in self.members if prop.is_fixed_value ]
777
778 @property
779 @memoize
780 def public_members(self):
781 return [ prop for prop in self.members if prop.is_public ]
782
783 @property
784 @memoize
785 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700786 return self.ir_model_members + self.virtual_members
787
788 @property
789 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700790 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700791 return tuple(members)
792
793 @property
794 def virtual_members(self):
795 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
796 if self.interface.name in model.oxm_map:
797 oxm_entry = model.oxm_map[self.interface.name]
798 return (
799 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
800 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
801 )
802 else:
803 return (
804 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
805 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
806 )
807 else:
808 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700809
810 def all_versions(self):
811 return [ JavaOFVersion(int_version)
812 for int_version in of_g.unified[self.c_name]
813 if int_version != 'union' and int_version != 'object_id' ]
814
815 def version_is_inherited(self, version):
816 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
817
818 def inherited_from(self, version):
819 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
820
821 @property
822 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700823 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
824
825 @property
826 def discriminator(self):
827 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700828
829 @property
830 def is_extension(self):
831 return type_maps.message_is_extension(self.c_name, -1)
832
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700833 @property
834 def align(self):
835 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
836
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700837 @property
838 @memoize
839 def superclass(self):
840 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
841
842 @property
843 @memoize
844 def subclasses(self):
845 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
846
Andreas Wundsam27303462013-07-16 12:52:35 -0700847#######################################################################
848### Member
849#######################################################################
850
851
852class JavaMember(object):
853 """ Models a property (member) of an openflow class. """
854 def __init__(self, msg, name, java_type, member):
855 self.msg = msg
856 self.name = name
857 self.java_type = java_type
858 self.member = member
859 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700860
861 @property
862 def title_name(self):
863 return self.name[0].upper() + self.name[1:]
864
865 @property
866 def constant_name(self):
867 return self.c_name.upper()
868
869 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700870 def getter_name(self):
871 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
872
873 @property
874 def setter_name(self):
875 return "set" + self.title_name
876
877 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700878 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700879 if self.is_fixed_value:
880 return self.constant_name
881 else:
882 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700883
884 @property
885 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700886 if self.is_fixed_value:
887 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700888 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700889 default = self.java_type.default_op(self.msg.version)
890 if default == "null" and not self.is_nullable:
891 return None
892 else:
893 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700894
895 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700896 def enum_value(self):
897 if self.name == "version":
898 return "OFVersion.%s" % self.msg.version.constant_version
899
900 java_type = self.java_type.public_type;
901 try:
902 global model
903 enum = model.enum_by_name(java_type)
904 entry = enum.entry_by_version_value(self.msg.version, self.value)
905 return "%s.%s" % ( enum.name, entry.name)
906 except KeyError, e:
907 print e.message
908 return self.value
909
910 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700911 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700912 return isinstance(self.member, OFPadMember)
913
914 def is_type_value(self, version=None):
915 if(version==None):
916 return any(self.is_type_value(version) for version in self.msg.all_versions)
917 try:
918 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
919 except:
920 return False
921
922 @property
923 def is_field_length_value(self):
924 return isinstance(self.member, OFFieldLengthMember)
925
926 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700927 def is_discriminator(self):
928 return isinstance(self.member, OFDiscriminatorMember)
929
930 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700931 def is_length_value(self):
932 return isinstance(self.member, OFLengthMember)
933
934 @property
935 def is_public(self):
936 return not (self.is_pad or self.is_length_value)
937
938 @property
939 def is_data(self):
940 return isinstance(self.member, OFDataMember) and self.name != "version"
941
942 @property
943 def is_fixed_value(self):
944 return hasattr(self.member, "value") or self.name == "version" \
945 or ( self.name == "length" and self.msg.is_fixed_length) \
946 or ( self.name == "len" and self.msg.is_fixed_length)
947
948 @property
949 def value(self):
950 if self.name == "version":
951 return self.msg.version.int_version
952 elif self.name == "length" or self.name == "len":
953 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700954 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700955 return self.java_type.format_value(self.member.value)
956
957 @property
958 def priv_value(self):
959 if self.name == "version":
960 return self.msg.version.int_version
961 elif self.name == "length" or self.name == "len":
962 return self.msg.length
963 else:
964 return self.java_type.format_value(self.member.value, pub_type=False)
965
Andreas Wundsam27303462013-07-16 12:52:35 -0700966
967 @property
968 def is_writeable(self):
969 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
970
971 def get_type_value_info(self, version):
972 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700973
974 @property
975 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700976 if hasattr(self.member, "length"):
977 return self.member.length
978 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700979 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700980 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700981
982 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700983 def for_of_member(java_class, member):
984 if isinstance(member, OFPadMember):
985 return JavaMember(None, "", None, member)
986 else:
987 if member.name == 'len':
988 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700989 elif member.name == 'value_mask':
990 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700991 else:
992 name = java_type.name_c_to_camel(member.name)
993 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
994 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700995
996 @property
997 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700998 if not self.msg.c_name in of_g.unified:
999 print("%s not self.unified" % self.msg.c_name)
1000 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001001 for version in of_g.unified[self.msg.c_name]:
1002 if version == 'union' or version =='object_id':
1003 continue
1004 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1005 continue
1006
Andreas Wundsam27303462013-07-16 12:52:35 -07001007 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 -07001008 return False
1009 return True
1010
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001011 @property
1012 def is_virtual(self):
1013 return False
1014
Andreas Wundsam27303462013-07-16 12:52:35 -07001015 def __hash__(self):
1016 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001017
Andreas Wundsam27303462013-07-16 12:52:35 -07001018 def __eq__(self, other):
1019 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001020 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001021 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001022
Andreas Wundsamf1949682013-09-23 14:48:31 -07001023 @property
1024 def is_nullable(self):
1025 return self.name in model.nullable_map[self.msg.name]
1026
1027
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001028class JavaVirtualMember(JavaMember):
1029 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1030 def __init__(self, msg, name, java_type, value=None):
1031 JavaMember.__init__(self, msg, name, java_type, member=None)
1032 self._value = value
1033
1034 @property
1035 def is_fixed_value(self):
1036 return True
1037
1038 @property
1039 def value(self):
1040 return self._value
1041
1042 @property
1043 def priv_value(self):
1044 return self._value
1045
1046
1047 @property
1048 def is_universal(self):
1049 return True
1050
1051 @property
1052 def is_virtual(self):
1053 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001054
1055#######################################################################
1056### Unit Test
1057#######################################################################
1058
Yotam Harchol466b3212013-08-15 12:14:46 -07001059class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001060 def __init__(self, java_class):
1061 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001062 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001063 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001064 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1065 name=java_class.c_name[3:]) + "{i}.data"
1066 test_class_name = self.java_class.name + "Test"
1067 self.test_units = []
1068 if test_data.exists(first_data_file_name):
1069 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1070 i = 1
1071 while test_data.exists(data_file_template.format(i=i)):
1072 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1073 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001074
Yotam Harchol466b3212013-08-15 12:14:46 -07001075 @property
1076 def package(self):
1077 return self.java_class.package
1078
1079 @property
1080 def has_test_data(self):
1081 return len(self.test_units) > 0
1082
1083 @property
1084 def length(self):
1085 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001086
Yotam Harchol466b3212013-08-15 12:14:46 -07001087 def get_test_unit(self, i):
1088 return self.test_units[i]
1089
1090
1091class JavaUnitTest(object):
1092 def __init__(self, java_class, file_name=None, test_class_name=None):
1093 self.java_class = java_class
1094 if file_name is None:
1095 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1096 name=java_class.c_name[3:])
1097 else:
1098 self.data_file_name = file_name
1099 if test_class_name is None:
1100 self.test_class_name = self.java_class.name + "Test"
1101 else:
1102 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001103
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001104 @property
1105 def package(self):
1106 return self.java_class.package
1107
1108 @property
1109 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001110 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001111
1112 @property
1113 def has_test_data(self):
1114 return test_data.exists(self.data_file_name)
1115
1116 @property
1117 @memoize
1118 def test_data(self):
1119 return test_data.read(self.data_file_name)
1120
1121
Andreas Wundsam27303462013-07-16 12:52:35 -07001122#######################################################################
1123### Enums
1124#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001125
Andreas Wundsam27303462013-07-16 12:52:35 -07001126class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001127 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001128 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001129
1130 if c_name == "of_stats_types":
1131 self.name = "OFStatsType"
1132 else:
1133 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001134
Andreas Wundsam27303462013-07-16 12:52:35 -07001135 # Port_features has constants that start with digits
1136 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001137
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001138 self.version_enums = version_enum_map
1139
1140 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1141 for version, ir_enum in version_enum_map.items():
1142 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001143 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1144
Andreas Wundsam27303462013-07-16 12:52:35 -07001145 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001146 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001147
1148 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 -07001149 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001150
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001151 self.metadata = model.enum_metadata_map[self.name]
1152
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001153 def wire_type(self, version):
1154 ir_enum = self.version_enums[version]
1155 if "wire_type" in ir_enum.params:
1156 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1157 else:
1158 return java_type.u8
1159
1160 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001161 @memoize
1162 def is_bitmask(self):
1163 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1164
1165 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001166 def versions(self):
1167 return self.version_enums.keys()
1168
Andreas Wundsam27303462013-07-16 12:52:35 -07001169 @memoize
1170 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001171 res = find(lambda e: e.name == name, self.entries)
1172 if res:
1173 return res
1174 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001175 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1176
1177 @memoize
1178 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001179 res = find(lambda e: e.c_name == name, self.entries)
1180 if res:
1181 return res
1182 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001183 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1184
1185 @memoize
1186 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001187 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1188 if res:
1189 return res
1190 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001191 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1192
1193# values: Map JavaVersion->Value
1194class JavaEnumEntry(object):
1195 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001196 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001197 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1198 self.values = values
1199
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001200 @property
1201 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001202 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001203
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001204 def has_value(self, version):
1205 return version in self.values
1206
Andreas Wundsam27303462013-07-16 12:52:35 -07001207 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001208 return self.values[version]
1209
1210 def format_value(self, version):
1211 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001212 return res
1213
Andreas Wundsam27303462013-07-16 12:52:35 -07001214 def all_values(self, versions, not_present=None):
1215 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001216
1217 @property
1218 @memoize
1219 def masked_enum_group(self):
1220 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1221 return group
1222
1223 @property
1224 @memoize
1225 def is_mask(self):
1226 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])