blob: 410a1dd5e2679f1d9ff309a910da65a85262a243 [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),
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):
659 if self.name == "OFOxm":
660 return (
661 JavaVirtualMember(self, "value", java_type.generic_t),
662 JavaVirtualMember(self, "mask", java_type.generic_t),
663 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
664 JavaVirtualMember(self, "masked", java_type.boolean),
665 )
666 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
667 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
668 if self.name in model.oxm_map \
669 else java_type.make_match_field_jtype()
670
671 return (
672 JavaVirtualMember(self, "matchField", field_type),
673 JavaVirtualMember(self, "masked", java_type.boolean),
674 ) \
675 + \
676 (
677 ( 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
678 ()
679 )
680 else:
681 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700682
683 @property
684 @memoize
685 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700686 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700687 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 -0700688
689 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700690 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700691 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700692 return len(self.all_versions) == len(model.versions)
693
694 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700695 @memoize
696 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700697 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700698 return self.version_map.keys()
699
Andreas Wundsam5204de22013-07-30 11:34:45 -0700700 def has_version(self, version):
701 return version in self.version_map
702
Andreas Wundsam27303462013-07-16 12:52:35 -0700703 def versioned_class(self, version):
704 return JavaOFClass(self, version, self.version_map[version])
705
706 @property
707 @memoize
708 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700709 return [ self.versioned_class(version) for version in self.all_versions ]
710
711#######################################################################
712### (Versioned) Classes
713#######################################################################
714
715class JavaOFClass(object):
716 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700717 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700718 """
719 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700720 """
721 @param interface JavaOFInterface instance of the parent interface
722 @param version JavaOFVersion
723 @param ir_class OFClass from loxi_ir
724 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700725 self.interface = interface
726 self.ir_class = ir_class
727 self.c_name = self.ir_class.name
728 self.version = version
729 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700730 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700731 self.generated = False
732
733 @property
734 @memoize
735 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700736 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700737
738 @property
739 def name(self):
740 return "%sVer%s" % (self.interface.name, self.version.of_version)
741
742 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700743 def variable_name(self):
744 return self.name[3:]
745
746 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700747 def length(self):
748 if self.is_fixed_length:
749 return self.min_length
750 else:
751 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
752
753 @property
754 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700755 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700756 id_tuple = (self.ir_class.name, self.version.int_version)
757 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
758
759 @property
760 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700761 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700762 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
763 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700764
765 def all_properties(self):
766 return self.interface.members
767
768 def get_member(self, name):
769 for m in self.members:
770 if m.name == name:
771 return m
772
773 @property
774 @memoize
775 def data_members(self):
776 return [ prop for prop in self.members if prop.is_data ]
777
778 @property
779 @memoize
780 def fixed_value_members(self):
781 return [ prop for prop in self.members if prop.is_fixed_value ]
782
783 @property
784 @memoize
785 def public_members(self):
786 return [ prop for prop in self.members if prop.is_public ]
787
788 @property
789 @memoize
790 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700791 return self.ir_model_members + self.virtual_members
792
793 @property
794 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700795 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700796 return tuple(members)
797
798 @property
799 def virtual_members(self):
800 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
801 if self.interface.name in model.oxm_map:
802 oxm_entry = model.oxm_map[self.interface.name]
803 return (
804 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
805 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
806 )
807 else:
808 return (
809 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
810 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
811 )
812 else:
813 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700814
815 def all_versions(self):
816 return [ JavaOFVersion(int_version)
817 for int_version in of_g.unified[self.c_name]
818 if int_version != 'union' and int_version != 'object_id' ]
819
820 def version_is_inherited(self, version):
821 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
822
823 def inherited_from(self, version):
824 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
825
826 @property
827 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700828 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
829
830 @property
831 def discriminator(self):
832 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700833
834 @property
835 def is_extension(self):
836 return type_maps.message_is_extension(self.c_name, -1)
837
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700838 @property
839 def align(self):
840 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
841
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700842 @property
843 @memoize
844 def superclass(self):
845 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
846
847 @property
848 @memoize
849 def subclasses(self):
850 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
851
Andreas Wundsam27303462013-07-16 12:52:35 -0700852#######################################################################
853### Member
854#######################################################################
855
856
857class JavaMember(object):
858 """ Models a property (member) of an openflow class. """
859 def __init__(self, msg, name, java_type, member):
860 self.msg = msg
861 self.name = name
862 self.java_type = java_type
863 self.member = member
864 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700865
866 @property
867 def title_name(self):
868 return self.name[0].upper() + self.name[1:]
869
870 @property
871 def constant_name(self):
872 return self.c_name.upper()
873
874 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700875 def getter_name(self):
876 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
877
878 @property
879 def setter_name(self):
880 return "set" + self.title_name
881
882 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700883 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700884 if self.is_fixed_value:
885 return self.constant_name
886 else:
887 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700888
889 @property
890 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700891 if self.is_fixed_value:
892 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700893 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700894 default = self.java_type.default_op(self.msg.version)
895 if default == "null" and not self.is_nullable:
896 return None
897 else:
898 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700899
900 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700901 def enum_value(self):
902 if self.name == "version":
903 return "OFVersion.%s" % self.msg.version.constant_version
904
905 java_type = self.java_type.public_type;
906 try:
907 global model
908 enum = model.enum_by_name(java_type)
909 entry = enum.entry_by_version_value(self.msg.version, self.value)
910 return "%s.%s" % ( enum.name, entry.name)
911 except KeyError, e:
912 print e.message
913 return self.value
914
915 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700916 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700917 return isinstance(self.member, OFPadMember)
918
919 def is_type_value(self, version=None):
920 if(version==None):
921 return any(self.is_type_value(version) for version in self.msg.all_versions)
922 try:
923 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
924 except:
925 return False
926
927 @property
928 def is_field_length_value(self):
929 return isinstance(self.member, OFFieldLengthMember)
930
931 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700932 def is_discriminator(self):
933 return isinstance(self.member, OFDiscriminatorMember)
934
935 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700936 def is_length_value(self):
937 return isinstance(self.member, OFLengthMember)
938
939 @property
940 def is_public(self):
941 return not (self.is_pad or self.is_length_value)
942
943 @property
944 def is_data(self):
945 return isinstance(self.member, OFDataMember) and self.name != "version"
946
947 @property
948 def is_fixed_value(self):
949 return hasattr(self.member, "value") or self.name == "version" \
950 or ( self.name == "length" and self.msg.is_fixed_length) \
951 or ( self.name == "len" and self.msg.is_fixed_length)
952
953 @property
954 def value(self):
955 if self.name == "version":
956 return self.msg.version.int_version
957 elif self.name == "length" or self.name == "len":
958 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700959 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700960 return self.java_type.format_value(self.member.value)
961
962 @property
963 def priv_value(self):
964 if self.name == "version":
965 return self.msg.version.int_version
966 elif self.name == "length" or self.name == "len":
967 return self.msg.length
968 else:
969 return self.java_type.format_value(self.member.value, pub_type=False)
970
Andreas Wundsam27303462013-07-16 12:52:35 -0700971
972 @property
973 def is_writeable(self):
974 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
975
976 def get_type_value_info(self, version):
977 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700978
979 @property
980 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700981 if hasattr(self.member, "length"):
982 return self.member.length
983 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700984 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700985 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700986
987 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700988 def for_of_member(java_class, member):
989 if isinstance(member, OFPadMember):
990 return JavaMember(None, "", None, member)
991 else:
992 if member.name == 'len':
993 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700994 elif member.name == 'value_mask':
995 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700996 else:
997 name = java_type.name_c_to_camel(member.name)
998 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
999 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001000
1001 @property
1002 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -07001003 if not self.msg.c_name in of_g.unified:
1004 print("%s not self.unified" % self.msg.c_name)
1005 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001006 for version in of_g.unified[self.msg.c_name]:
1007 if version == 'union' or version =='object_id':
1008 continue
1009 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1010 continue
1011
Andreas Wundsam27303462013-07-16 12:52:35 -07001012 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 -07001013 return False
1014 return True
1015
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001016 @property
1017 def is_virtual(self):
1018 return False
1019
Andreas Wundsam27303462013-07-16 12:52:35 -07001020 def __hash__(self):
1021 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001022
Andreas Wundsam27303462013-07-16 12:52:35 -07001023 def __eq__(self, other):
1024 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001025 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001026 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001027
Andreas Wundsamf1949682013-09-23 14:48:31 -07001028 @property
1029 def is_nullable(self):
1030 return self.name in model.nullable_map[self.msg.name]
1031
1032
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001033class JavaVirtualMember(JavaMember):
1034 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1035 def __init__(self, msg, name, java_type, value=None):
1036 JavaMember.__init__(self, msg, name, java_type, member=None)
1037 self._value = value
1038
1039 @property
1040 def is_fixed_value(self):
1041 return True
1042
1043 @property
1044 def value(self):
1045 return self._value
1046
1047 @property
1048 def priv_value(self):
1049 return self._value
1050
1051
1052 @property
1053 def is_universal(self):
1054 return True
1055
1056 @property
1057 def is_virtual(self):
1058 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001059
1060#######################################################################
1061### Unit Test
1062#######################################################################
1063
Yotam Harchol466b3212013-08-15 12:14:46 -07001064class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001065 def __init__(self, java_class):
1066 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001067 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001068 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001069 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1070 name=java_class.c_name[3:]) + "{i}.data"
1071 test_class_name = self.java_class.name + "Test"
1072 self.test_units = []
1073 if test_data.exists(first_data_file_name):
1074 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1075 i = 1
1076 while test_data.exists(data_file_template.format(i=i)):
1077 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1078 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001079
Yotam Harchol466b3212013-08-15 12:14:46 -07001080 @property
1081 def package(self):
1082 return self.java_class.package
1083
1084 @property
1085 def has_test_data(self):
1086 return len(self.test_units) > 0
1087
1088 @property
1089 def length(self):
1090 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001091
Yotam Harchol466b3212013-08-15 12:14:46 -07001092 def get_test_unit(self, i):
1093 return self.test_units[i]
1094
1095
1096class JavaUnitTest(object):
1097 def __init__(self, java_class, file_name=None, test_class_name=None):
1098 self.java_class = java_class
1099 if file_name is None:
1100 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1101 name=java_class.c_name[3:])
1102 else:
1103 self.data_file_name = file_name
1104 if test_class_name is None:
1105 self.test_class_name = self.java_class.name + "Test"
1106 else:
1107 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001108
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001109 @property
1110 def package(self):
1111 return self.java_class.package
1112
1113 @property
1114 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001115 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001116
1117 @property
1118 def has_test_data(self):
1119 return test_data.exists(self.data_file_name)
1120
1121 @property
1122 @memoize
1123 def test_data(self):
1124 return test_data.read(self.data_file_name)
1125
1126
Andreas Wundsam27303462013-07-16 12:52:35 -07001127#######################################################################
1128### Enums
1129#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001130
Andreas Wundsam27303462013-07-16 12:52:35 -07001131class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001132 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001133 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001134
1135 if c_name == "of_stats_types":
1136 self.name = "OFStatsType"
1137 else:
1138 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001139
Andreas Wundsam27303462013-07-16 12:52:35 -07001140 # Port_features has constants that start with digits
1141 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001142
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001143 self.version_enums = version_enum_map
1144
1145 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1146 for version, ir_enum in version_enum_map.items():
1147 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001148 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1149
Andreas Wundsam27303462013-07-16 12:52:35 -07001150 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001151 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001152
1153 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 -07001154 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001155
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001156 self.metadata = model.enum_metadata_map[self.name]
1157
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001158 def wire_type(self, version):
1159 ir_enum = self.version_enums[version]
1160 if "wire_type" in ir_enum.params:
1161 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1162 else:
1163 return java_type.u8
1164
1165 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001166 @memoize
1167 def is_bitmask(self):
1168 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1169
1170 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001171 def versions(self):
1172 return self.version_enums.keys()
1173
Andreas Wundsam27303462013-07-16 12:52:35 -07001174 @memoize
1175 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001176 res = find(lambda e: e.name == name, self.entries)
1177 if res:
1178 return res
1179 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001180 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1181
1182 @memoize
1183 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001184 res = find(lambda e: e.c_name == name, self.entries)
1185 if res:
1186 return res
1187 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001188 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1189
1190 @memoize
1191 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001192 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1193 if res:
1194 return res
1195 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001196 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1197
1198# values: Map JavaVersion->Value
1199class JavaEnumEntry(object):
1200 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001201 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001202 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1203 self.values = values
1204
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001205 @property
1206 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001207 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001208
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001209 def has_value(self, version):
1210 return version in self.values
1211
Andreas Wundsam27303462013-07-16 12:52:35 -07001212 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001213 return self.values[version]
1214
1215 def format_value(self, version):
1216 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001217 return res
1218
Andreas Wundsam27303462013-07-16 12:52:35 -07001219 def all_values(self, versions, not_present=None):
1220 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001221
1222 @property
1223 @memoize
1224 def masked_enum_group(self):
1225 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1226 return group
1227
1228 @property
1229 @memoize
1230 def is_mask(self):
1231 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])