blob: 7334a1f40c0d6e4233dc925cad6c362b7e15039f [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})
Andreas Wundsam55b71ce2013-10-01 19:26:46 -0700143 enum_blacklist = set(("OFDefinitions", "OFPortNo", "OFVlanId"))
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),
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700381 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={}, xid_generator=False)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700382
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(
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700388 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ),
389 xid_generator=True)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700390
391 for i in self.interfaces:
392 for n, factory in factories.items():
393 if n == "":
394 factory.members.append(i)
395 break
396 else:
397 super_class = self.interface_by_name(n)
398 if i.is_instance_of(super_class):
399 factory.members.append(i)
400 break
401 return factories.values()
Andreas Wundsam5204de22013-07-30 11:34:45 -0700402
403 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700404 """ return wether or not to generate implementation class clazz.
405 Now true for everything except OFTableModVer10.
406 @param clazz JavaOFClass instance
407 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700408 if clazz.interface.name.startswith("OFMatchV"):
409 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700410 elif clazz.name == "OFTableModVer10":
411 # tablemod ver 10 is a hack and has no oftype defined
412 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700413 if loxi_utils.class_is_message(clazz.interface.c_name):
414 return True
415 if loxi_utils.class_is_oxm(clazz.interface.c_name):
416 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700417 if loxi_utils.class_is_action(clazz.interface.c_name):
418 return True
419 if loxi_utils.class_is_instruction(clazz.interface.c_name):
420 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700421 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700422 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700423
424
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700425class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories", "xid_generator"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700426 @property
427 def factory_classes(self):
428 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700429 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700430 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700431 interface=self,
432 version=version
433 ) for version in model.versions ]
434
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700435 def method_name(self, member, builder=True):
436 n = member.variable_name
437 if n.startswith(self.remove_prefix):
438 n = n[len(self.remove_prefix):]
439 n = n[0].lower() + n[1:]
440 if builder:
441 return "build" + n[0].upper() + n[1:]
442 else:
443 return n
Andreas Wundsam5204de22013-07-30 11:34:45 -0700444
445OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700446class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
447 @property
448 def base_class(self):
449 return self.interface.base_class
450
451 @property
452 def versioned_base_class(self):
453 base_class_interface = model.interface_by_name(self.interface.base_class)
454 if base_class_interface and base_class_interface.has_version(self.version):
455 return base_class_interface.versioned_class(self.version)
456 else:
457 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700458
Andreas Wundsam27303462013-07-16 12:52:35 -0700459model = JavaModel()
460
461#######################################################################
462### OFVersion
463#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700464
465class JavaOFVersion(object):
466 """ Models a version of OpenFlow. contains methods to convert the internal
467 Loxi version to a java constant / a string """
468 def __init__(self, int_version):
469 self.int_version = int(int_version)
470
471 @property
472 def of_version(self):
473 return "1" + str(int(self.int_version) - 1)
474
475 @property
476 def constant_version(self):
477 return "OF_" + self.of_version
478
Andreas Wundsam27303462013-07-16 12:52:35 -0700479 def __repr__(self):
480 return "JavaOFVersion(%d)" % self.int_version
481
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700482 def __str__(self):
483 return of_g.param_version_names[self.int_version]
484
Andreas Wundsam27303462013-07-16 12:52:35 -0700485 def __hash__(self):
486 return hash(self.int_version)
487
488 def __eq__(self, other):
489 if other is None or type(self) != type(other):
490 return False
491 return (self.int_version,) == (other.int_version,)
492
493#######################################################################
494### Interface
495#######################################################################
496
497class JavaOFInterface(object):
498 """ Models an OpenFlow Message class for the purpose of the java class.
499 Version agnostic, in contrast to the loxi_ir python model.
500 """
501 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700502 """"
503 @param c_name: loxi style name (e.g., of_flow_add)
504 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
505 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700506 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700507 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700508 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700509 self.name = java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700510 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700511 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700512 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700513 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700514 self.constant_name = c_name.upper().replace("OF_", "")
515
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700516 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700517 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700518 if self.name != parent_interface:
519 self.parent_interface = parent_interface
520 else:
521 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700522
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700523 @property
524 @memoize
525 def all_parent_interfaces(self):
526 return [ "OFObject" ] + \
527 ([ self.parent_interface ] if self.parent_interface else [] )+ \
528 self.additional_parent_interfaces
529 @property
530 @memoize
531 def additional_parent_interfaces(self):
532 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
533 m = re.match(r'(.*)Request$', self.name)
534 if m:
535 reply_name = m.group(1) + "Reply"
536 if model.interface_by_name(reply_name):
537 return ["OFRequest<%s>" % reply_name ]
538 return []
539
540
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700541 def is_instance_of(self, other_class):
542 if self == other_class:
543 return True
544 parent = self.super_class
545 if parent is None:
546 return False
547 else:
548 return parent.is_instance_of(other_class)
549
550 @property
551 def super_class(self):
552 if not self.parent_interface:
553 return None
554 else:
555 return model.interface_by_name(self.parent_interface)
556
557
558 def inherited_declaration(self, type_spec="?"):
559 if self.type_annotation:
560 return "%s<%s>" % (self.name, type_spec)
561 else:
562 return "%s" % self.name
563
564 @property
565 def type_variable(self):
566 if self.type_annotation:
567 return "<T>"
568 else:
569 return "";
570
Andreas Wundsam27303462013-07-16 12:52:35 -0700571 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700572 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
573 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
574 # model (note, that the loxi model is on versioned classes). Should check/infer the
575 # inheritance information from the versioned lox_ir classes.
Andreas Wundsamd6b0cb02013-09-28 18:55:56 -0700576 if re.match(r'OFStatsRequest$', self.name):
577 return ("", "OFMessage", "T extends OFStatsReply")
578 elif re.match(r'OF.+StatsRequest$', self.name):
579 return ("", "OFStatsRequest<{}>".format(re.sub(r'Request$', 'Reply', self.name)), None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700580 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700581 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700582 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700583 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700584 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 -0700585 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700586 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 -0700587 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700588 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
589 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700590 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700591 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700592 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700593 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700594 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700595 if re.match(r'OFActionBsn.+', self.name):
596 return ("action", "OFActionBsn", None)
597 elif re.match(r'OFActionNicira.+', self.name):
598 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700599 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
600 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700601 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700602 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700603 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700604 return ("", "OFBsnVport", None)
605 elif self.name == "OFOxm":
606 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700607 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700608 if self.name in model.oxm_map:
609 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
610 else:
611 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700612 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700613 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700614 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700615 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700616 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700617 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700618 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700619 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700620 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700621 return ("", None, None)
622
623 @property
624 @memoize
625 def writeable_members(self):
626 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700627
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700628 @memoize
629 def member_by_name(self, name):
630 return find(lambda m: m.name == name, self.members)
631
Andreas Wundsam27303462013-07-16 12:52:35 -0700632 @property
633 @memoize
634 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700635 return self.ir_model_members + self.virtual_members
636
637 @property
638 @memoize
639 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700640 """return a list of all members to be exposed by this interface. Corresponds to
641 the union of the members of the vesioned classes without length, fieldlength
642 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700643 all_versions = []
644 member_map = collections.OrderedDict()
645
646 for (version, of_class) in self.version_map.items():
647 for of_member in of_class.members:
648 if isinstance(of_member, OFLengthMember) or \
649 isinstance(of_member, OFFieldLengthMember) or \
650 isinstance(of_member, OFPadMember):
651 continue
652 if of_member.name not in member_map:
653 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
654
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700655 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 -0700656
657 @property
658 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700659 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700660 if self.name == "OFOxm":
Andreas Wundsama0981022013-10-02 18:15:06 -0700661 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700662 JavaVirtualMember(self, "value", java_type.generic_t),
663 JavaVirtualMember(self, "mask", java_type.generic_t),
664 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
665 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsama0981022013-10-02 18:15:06 -0700666 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700667 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
668 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
669 if self.name in model.oxm_map \
670 else java_type.make_match_field_jtype()
671
Andreas Wundsama0981022013-10-02 18:15:06 -0700672 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700673 JavaVirtualMember(self, "matchField", field_type),
674 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsama0981022013-10-02 18:15:06 -0700675 ]
676 if not find(lambda x: x.name == "mask", self.ir_model_members):
677 virtual_members.append(JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
678
679 if not find(lambda m: m.name == "version", self.ir_model_members):
680 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
681
682 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700683
684 @property
685 @memoize
686 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700687 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700688 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 -0700689
690 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700691 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700692 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700693 return len(self.all_versions) == len(model.versions)
694
695 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700696 @memoize
697 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700698 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700699 return self.version_map.keys()
700
Andreas Wundsam5204de22013-07-30 11:34:45 -0700701 def has_version(self, version):
702 return version in self.version_map
703
Andreas Wundsam27303462013-07-16 12:52:35 -0700704 def versioned_class(self, version):
705 return JavaOFClass(self, version, self.version_map[version])
706
707 @property
708 @memoize
709 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700710 return [ self.versioned_class(version) for version in self.all_versions ]
711
712#######################################################################
713### (Versioned) Classes
714#######################################################################
715
716class JavaOFClass(object):
717 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700718 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700719 """
720 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700721 """
722 @param interface JavaOFInterface instance of the parent interface
723 @param version JavaOFVersion
724 @param ir_class OFClass from loxi_ir
725 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700726 self.interface = interface
727 self.ir_class = ir_class
728 self.c_name = self.ir_class.name
729 self.version = version
730 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700731 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700732 self.generated = False
733
734 @property
735 @memoize
736 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700737 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700738
739 @property
740 def name(self):
741 return "%sVer%s" % (self.interface.name, self.version.of_version)
742
743 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700744 def variable_name(self):
745 return self.name[3:]
746
747 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700748 def length(self):
749 if self.is_fixed_length:
750 return self.min_length
751 else:
752 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
753
754 @property
755 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700756 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700757 id_tuple = (self.ir_class.name, self.version.int_version)
758 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
759
760 @property
761 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700762 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700763 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
764 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700765
766 def all_properties(self):
767 return self.interface.members
768
769 def get_member(self, name):
770 for m in self.members:
771 if m.name == name:
772 return m
773
774 @property
775 @memoize
776 def data_members(self):
777 return [ prop for prop in self.members if prop.is_data ]
778
779 @property
780 @memoize
781 def fixed_value_members(self):
782 return [ prop for prop in self.members if prop.is_fixed_value ]
783
784 @property
785 @memoize
786 def public_members(self):
787 return [ prop for prop in self.members if prop.is_public ]
788
789 @property
790 @memoize
791 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700792 return self.ir_model_members + self.virtual_members
793
794 @property
Andreas Wundsama0981022013-10-02 18:15:06 -0700795 @memoize
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700796 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700797 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700798 return tuple(members)
799
800 @property
801 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700802 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700803 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
804 if self.interface.name in model.oxm_map:
805 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700806 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700807 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
808 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsama0981022013-10-02 18:15:06 -0700809 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700810 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700811 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700812 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
813 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsama0981022013-10-02 18:15:06 -0700814 ]
815
816 if not find(lambda m: m.name == "version", self.ir_model_members):
817 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
818
819 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700820
821 def all_versions(self):
822 return [ JavaOFVersion(int_version)
823 for int_version in of_g.unified[self.c_name]
824 if int_version != 'union' and int_version != 'object_id' ]
825
826 def version_is_inherited(self, version):
827 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
828
829 def inherited_from(self, version):
830 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
831
832 @property
833 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700834 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
835
836 @property
837 def discriminator(self):
838 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700839
840 @property
841 def is_extension(self):
842 return type_maps.message_is_extension(self.c_name, -1)
843
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700844 @property
845 def align(self):
846 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
847
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700848 @property
849 @memoize
850 def superclass(self):
851 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
852
853 @property
854 @memoize
855 def subclasses(self):
856 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
857
Andreas Wundsam27303462013-07-16 12:52:35 -0700858#######################################################################
859### Member
860#######################################################################
861
862
863class JavaMember(object):
864 """ Models a property (member) of an openflow class. """
865 def __init__(self, msg, name, java_type, member):
866 self.msg = msg
867 self.name = name
868 self.java_type = java_type
869 self.member = member
870 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700871
872 @property
873 def title_name(self):
874 return self.name[0].upper() + self.name[1:]
875
876 @property
877 def constant_name(self):
878 return self.c_name.upper()
879
880 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700881 def getter_name(self):
882 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
883
884 @property
885 def setter_name(self):
886 return "set" + self.title_name
887
888 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700889 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700890 if self.is_fixed_value:
891 return self.constant_name
892 else:
893 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700894
895 @property
896 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700897 if self.is_fixed_value:
898 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700899 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700900 default = self.java_type.default_op(self.msg.version)
901 if default == "null" and not self.is_nullable:
902 return None
903 else:
904 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700905
906 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700907 def enum_value(self):
908 if self.name == "version":
909 return "OFVersion.%s" % self.msg.version.constant_version
910
911 java_type = self.java_type.public_type;
912 try:
913 global model
914 enum = model.enum_by_name(java_type)
915 entry = enum.entry_by_version_value(self.msg.version, self.value)
916 return "%s.%s" % ( enum.name, entry.name)
917 except KeyError, e:
918 print e.message
919 return self.value
920
921 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700922 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700923 return isinstance(self.member, OFPadMember)
924
925 def is_type_value(self, version=None):
926 if(version==None):
927 return any(self.is_type_value(version) for version in self.msg.all_versions)
928 try:
929 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
930 except:
931 return False
932
933 @property
934 def is_field_length_value(self):
935 return isinstance(self.member, OFFieldLengthMember)
936
937 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700938 def is_discriminator(self):
939 return isinstance(self.member, OFDiscriminatorMember)
940
941 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700942 def is_length_value(self):
943 return isinstance(self.member, OFLengthMember)
944
945 @property
946 def is_public(self):
947 return not (self.is_pad or self.is_length_value)
948
949 @property
950 def is_data(self):
951 return isinstance(self.member, OFDataMember) and self.name != "version"
952
953 @property
954 def is_fixed_value(self):
955 return hasattr(self.member, "value") or self.name == "version" \
956 or ( self.name == "length" and self.msg.is_fixed_length) \
957 or ( self.name == "len" and self.msg.is_fixed_length)
958
959 @property
960 def value(self):
961 if self.name == "version":
962 return self.msg.version.int_version
963 elif self.name == "length" or self.name == "len":
964 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700965 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700966 return self.java_type.format_value(self.member.value)
967
968 @property
969 def priv_value(self):
970 if self.name == "version":
971 return self.msg.version.int_version
972 elif self.name == "length" or self.name == "len":
973 return self.msg.length
974 else:
975 return self.java_type.format_value(self.member.value, pub_type=False)
976
Andreas Wundsam27303462013-07-16 12:52:35 -0700977
978 @property
979 def is_writeable(self):
980 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
981
982 def get_type_value_info(self, version):
983 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700984
985 @property
986 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700987 if hasattr(self.member, "length"):
988 return self.member.length
989 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700990 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700991 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700992
993 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700994 def for_of_member(java_class, member):
995 if isinstance(member, OFPadMember):
996 return JavaMember(None, "", None, member)
997 else:
998 if member.name == 'len':
999 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001000 elif member.name == 'value_mask':
1001 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -07001002 else:
1003 name = java_type.name_c_to_camel(member.name)
1004 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
1005 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001006
1007 @property
1008 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -07001009 if not self.msg.c_name in of_g.unified:
1010 print("%s not self.unified" % self.msg.c_name)
1011 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001012 for version in of_g.unified[self.msg.c_name]:
1013 if version == 'union' or version =='object_id':
1014 continue
1015 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1016 continue
1017
Andreas Wundsam27303462013-07-16 12:52:35 -07001018 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 -07001019 return False
1020 return True
1021
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001022 @property
1023 def is_virtual(self):
1024 return False
1025
Andreas Wundsam27303462013-07-16 12:52:35 -07001026 def __hash__(self):
1027 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001028
Andreas Wundsam27303462013-07-16 12:52:35 -07001029 def __eq__(self, other):
1030 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001031 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001032 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001033
Andreas Wundsamf1949682013-09-23 14:48:31 -07001034 @property
1035 def is_nullable(self):
1036 return self.name in model.nullable_map[self.msg.name]
1037
1038
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001039class JavaVirtualMember(JavaMember):
1040 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1041 def __init__(self, msg, name, java_type, value=None):
1042 JavaMember.__init__(self, msg, name, java_type, member=None)
1043 self._value = value
1044
1045 @property
1046 def is_fixed_value(self):
1047 return True
1048
1049 @property
1050 def value(self):
1051 return self._value
1052
1053 @property
1054 def priv_value(self):
1055 return self._value
1056
1057
1058 @property
1059 def is_universal(self):
1060 return True
1061
1062 @property
1063 def is_virtual(self):
1064 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001065
1066#######################################################################
1067### Unit Test
1068#######################################################################
1069
Yotam Harchol466b3212013-08-15 12:14:46 -07001070class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001071 def __init__(self, java_class):
1072 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001073 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001074 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001075 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1076 name=java_class.c_name[3:]) + "{i}.data"
1077 test_class_name = self.java_class.name + "Test"
1078 self.test_units = []
1079 if test_data.exists(first_data_file_name):
1080 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1081 i = 1
1082 while test_data.exists(data_file_template.format(i=i)):
1083 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1084 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001085
Yotam Harchol466b3212013-08-15 12:14:46 -07001086 @property
1087 def package(self):
1088 return self.java_class.package
1089
1090 @property
1091 def has_test_data(self):
1092 return len(self.test_units) > 0
1093
1094 @property
1095 def length(self):
1096 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001097
Yotam Harchol466b3212013-08-15 12:14:46 -07001098 def get_test_unit(self, i):
1099 return self.test_units[i]
1100
1101
1102class JavaUnitTest(object):
1103 def __init__(self, java_class, file_name=None, test_class_name=None):
1104 self.java_class = java_class
1105 if file_name is None:
1106 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1107 name=java_class.c_name[3:])
1108 else:
1109 self.data_file_name = file_name
1110 if test_class_name is None:
1111 self.test_class_name = self.java_class.name + "Test"
1112 else:
1113 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001114
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001115 @property
1116 def package(self):
1117 return self.java_class.package
1118
1119 @property
1120 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001121 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001122
1123 @property
1124 def has_test_data(self):
1125 return test_data.exists(self.data_file_name)
1126
1127 @property
1128 @memoize
1129 def test_data(self):
1130 return test_data.read(self.data_file_name)
1131
1132
Andreas Wundsam27303462013-07-16 12:52:35 -07001133#######################################################################
1134### Enums
1135#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001136
Andreas Wundsam27303462013-07-16 12:52:35 -07001137class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001138 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001139 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001140
1141 if c_name == "of_stats_types":
1142 self.name = "OFStatsType"
1143 else:
1144 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001145
Andreas Wundsam27303462013-07-16 12:52:35 -07001146 # Port_features has constants that start with digits
1147 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001148
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001149 self.version_enums = version_enum_map
1150
1151 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1152 for version, ir_enum in version_enum_map.items():
1153 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001154 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1155
Andreas Wundsam27303462013-07-16 12:52:35 -07001156 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001157 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001158
1159 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 -07001160 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001161
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001162 self.metadata = model.enum_metadata_map[self.name]
1163
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001164 def wire_type(self, version):
1165 ir_enum = self.version_enums[version]
1166 if "wire_type" in ir_enum.params:
1167 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1168 else:
1169 return java_type.u8
1170
1171 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001172 @memoize
1173 def is_bitmask(self):
1174 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1175
1176 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001177 def versions(self):
1178 return self.version_enums.keys()
1179
Andreas Wundsam27303462013-07-16 12:52:35 -07001180 @memoize
1181 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001182 res = find(lambda e: e.name == name, self.entries)
1183 if res:
1184 return res
1185 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001186 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1187
1188 @memoize
1189 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001190 res = find(lambda e: e.c_name == name, self.entries)
1191 if res:
1192 return res
1193 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001194 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1195
1196 @memoize
1197 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001198 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1199 if res:
1200 return res
1201 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001202 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1203
1204# values: Map JavaVersion->Value
1205class JavaEnumEntry(object):
1206 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001207 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001208 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1209 self.values = values
1210
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001211 @property
1212 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001213 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001214
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001215 def has_value(self, version):
1216 return version in self.values
1217
Andreas Wundsam27303462013-07-16 12:52:35 -07001218 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001219 return self.values[version]
1220
1221 def format_value(self, version):
1222 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001223 return res
1224
Andreas Wundsam27303462013-07-16 12:52:35 -07001225 def all_values(self, versions, not_present=None):
1226 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001227
1228 @property
1229 @memoize
1230 def masked_enum_group(self):
1231 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1232 return group
1233
1234 @property
1235 @memoize
1236 def is_mask(self):
1237 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])