blob: b03c224677e85f5e180fe9b41343b6d2978c9cbf [file] [log] [blame]
Andreas Wundsam27303462013-07-16 12:52:35 -07001# Copyright 2013, Big Switch Networks, Inc.
2#
3# LoxiGen is licensed under the Eclipse Public License, version 1.0 (EPL), with
4# the following special exception:
5#
6# LOXI Exception
7#
8# As a special exception to the terms of the EPL, you may distribute libraries
9# generated by LoxiGen (LoxiGen Libraries) under the terms of your choice, provided
10# that copyright and licensing notices generated by LoxiGen are not altered or removed
11# from the LoxiGen Libraries and the notice provided below is (i) included in
12# the LoxiGen Libraries, if distributed in source code form and (ii) included in any
13# documentation for the LoxiGen Libraries, if distributed in binary form.
14#
15# Notice: "Copyright 2013, Big Switch Networks, Inc. This library was generated by the LoxiGen Compiler."
16#
17# You may not use this file except in compliance with the EPL or LOXI Exception. You may obtain
18# a copy of the EPL at:
19#
20# http://www.eclipse.org/legal/epl-v10.html
21#
22# Unless required by applicable law or agreed to in writing, software
23# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
24# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
25# EPL for the specific language governing permissions and limitations
26# under the EPL.
27
Andreas Wundsam40e14f72013-05-06 14:49:08 -070028# Prototype of an Intermediate Object model for the java code generator
29# A lot of this stuff could/should probably be merged with the python utilities
30
Andreas Wundsam27303462013-07-16 12:52:35 -070031import collections
Andreas Wundsam5204de22013-07-30 11:34:45 -070032from collections import namedtuple, defaultdict, OrderedDict
Andreas Wundsam27303462013-07-16 12:52:35 -070033import logging
Andreas Wundsam40e14f72013-05-06 14:49:08 -070034import os
35import pdb
36import re
37
Andreas Wundsam5204de22013-07-30 11:34:45 -070038from generic_utils import find, memoize, OrderedSet, OrderedDefaultDict
Andreas Wundsam27303462013-07-16 12:52:35 -070039import of_g
40from loxi_ir import *
Andreas Wundsam40e14f72013-05-06 14:49:08 -070041import loxi_front_end.type_maps as type_maps
Andreas Wundsam5204de22013-07-30 11:34:45 -070042import loxi_utils.loxi_utils as loxi_utils
Andreas Wundsam40e14f72013-05-06 14:49:08 -070043import py_gen.util as py_utils
Andreas Wundsam5204de22013-07-30 11:34:45 -070044import test_data
Andreas Wundsam40e14f72013-05-06 14:49:08 -070045
Andreas Wundsam27303462013-07-16 12:52:35 -070046import java_gen.java_type as java_type
Andreas Wundsame0d52be2013-08-22 07:52:13 -070047from java_gen.java_type import erase_type_annotation
Andreas Wundsam40e14f72013-05-06 14:49:08 -070048
Rob Vaterlaus4d311942013-09-24 13:41:44 -070049# Key is modified name of error code; value is OFErrorType value string
50error_type_map = {}
51
52def adjust_ir():
53 """
54 For Java we change of_error_message to combine the 16-bit type and code
55 fields into a single 32-bit code field and we combine the per-error-type
56 code enums into a single ofp_error_code enum. This enables the generated
57 OFErrorMsg class to have a getCode method that can return all supported
58 error codes. Otherwise we'd need to do something like having per-error-type
59 subclasses of OFErrorMsg that had a getCode that returned the different
60 error codes for each error type, which would be less convenient for clients
61 and would also entail changing the LOXI OF input files and impacting other
62 language backends.
63 """
64 for version in of_g.target_version_list:
65 of_protocol = of_g.ir[version]
66 error_type = find(lambda e: e.name == "ofp_error_type", of_protocol.enums)
67 if error_type == None:
68 raise Exception("ofp_error_type enum not found; OF version: " + str(version))
69 error_code_entries = []
70 # For each error type value look up the corresponding error code enum.
71 # Add those values to the combined entries for the new ofp_error_code
72 # enum. The name of the new value is formed by concatenating the name
73 # of the error type value with the name of the old error code value.
74 for error_type_entry in error_type.entries:
75 # Strip off the OFPxx prefix
76 prefix_length = error_type_entry.name.find('_')
77 if prefix_length < 0:
78 raise Exception("OFPET prefix not found for ofp_error_type value " + error_type_entry.name + "; OF version: " + str(version))
79 error_type_entry_name = error_type_entry.name[prefix_length+1:]
80 if error_type_entry_name == "EXPERIMENTER":
81 # There isn't an error code enum defined for the experimenter error type
82 # FIXME: Need to add support for the message ofp_error_experimenter_msg format
83 continue
84 # The per-error-type code enums follow a naming conventions where
85 # the middle part of the enum name is the same as the name of the
86 # error type value (except lower-case).
87 error_code_enum_name = "ofp_" + error_type_entry_name.lower() + "_code"
88 # Look up the error code enum from the IR
89 error_code_enum = None
90 for i, enum in enumerate(of_protocol.enums):
91 if enum.name == error_code_enum_name:
92 error_code_enum = enum
93 # We don't want to generate code for the per-error-type
94 # enum so remove it from the IR
95 del of_protocol.enums[i]
96 break
97 if error_code_enum == None:
98 raise Exception("Error code enum not found: " + error_code_enum_name + "; OF version: " + str(version))
99 for error_code_entry in error_code_enum.entries:
100 # Strip off the prefix from the entry name
101 prefix_length = error_code_entry.name.find('_')
102 if prefix_length < 0:
103 raise Exception("Prefix not found for error code value " + error_code_entry.name + "; OF version: " + str(version))
104 error_code_entry_name = error_code_entry.name[prefix_length+1:]
105 # Combine the entry type name and the error code name
106 error_code_entry_name = error_type_entry_name + "_" + error_code_entry_name
107 # Combine the entry type value and the error code value
108 error_code_entry_value = (error_type_entry.value << 16) + error_code_entry.value
109 # Add the enum entry to the combined ofp_error_code
110 # Note that the "OFPEC" prefix is arbitrary. It will be stripped
111 # off again during Java code generation, but there needs to be
112 # some/any prefix
113 error_code_entries.append(OFEnumEntry("OFPEC_" + error_code_entry_name, error_code_entry_value, {}))
114 error_type_map[error_code_entry_name] = error_type_entry_name
115 # We've collected all of the entries. Now we can add the enum to the IR
116 of_protocol.enums.append(OFEnum("ofp_error_code", error_code_entries, {'wire_type': 'uint32_t'}))
117
118 # We also need to patch the of_error_msg class to combine the 16-bit error
119 # type and code fields into a single 32-bit error code field
120 error_msg = find(lambda c: c.name == "of_error_msg", of_protocol.classes)
121 if error_msg == None:
122 raise Exception("of_error_msg class not found; OF version: " + str(version))
123 err_type_index = None
124 for i, member in enumerate(error_msg.members):
125 if member.name == "err_type":
126 # Keep track of the error type index so we can remove it once
127 # we've finished iterating
128 err_type_index = i
129 elif member.name == 'code':
130 # Change the code to be a 32-bit ofp_error_code enum value
131 error_msg.members[i] = OFDataMember("code", "ofp_error_code")
132 if err_type_index == None:
133 raise Exception("err_type member of of_error_msg not found; OF version: " + str(version))
134 del error_msg.members[err_type_index]
135
136
137def gen_error_type(enum_entry):
138 return "OFErrorType." + error_type_map[enum_entry.name]
139
Andreas Wundsam27303462013-07-16 12:52:35 -0700140class JavaModel(object):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700141 # registry for enums that should not be generated
142 # set(${java_enum_name})
Rob Vaterlausfbd5b6b2013-09-24 15:55:47 -0700143 enum_blacklist = set(("OFDefinitions", "OFPortNo",))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700144 # registry for enum *entry* that should not be generated
145 # map: ${java_enum_name} -> set(${java_entry_entry_name})
Andreas Wundsam43526532013-08-01 22:03:50 -0700146 enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700147
148 # registry of interfaces that should not be generated
149 # set(java_names)
Andreas Wundsambe168f72013-08-03 22:49:35 -0700150 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
151 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700152 # registry of interface properties that should not be generated
153 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700154 read_blacklist = defaultdict(lambda: set(), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700155 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700156 write_blacklist = defaultdict(lambda: set(), OFOxm=set(('typeLen',)), OFAction=set(('type',)), OFInstruction=set(('type',)), OFFlowMod=set(('command', )), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -0700157 # interfaces that are virtual
Andreas Wundsam001b1822013-08-02 22:25:55 -0700158 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -0700159
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700160 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
Yotam Harchole5d92972013-08-22 14:18:36 -0700161 oxm_map = { "OFOxmInPort": OxmMapEntry("OFPort", "IN_PORT", False),
162 "OFOxmInPortMasked": OxmMapEntry("OFPort", "IN_PORT", True),
163 "OFOxmInPhyPort": OxmMapEntry("OFPort", "IN_PHY_PORT", False),
164 "OFOxmInPhyPortMasked": OxmMapEntry("OFPort", "IN_PHY_PORT", True),
165 "OFOxmMetadata": OxmMapEntry("OFMetadata", "METADATA", False),
166 "OFOxmMetadataMasked": OxmMapEntry("OFMetadata", "METADATA", True),
167 "OFOxmEthDst": OxmMapEntry("MacAddress", "ETH_DST", False),
168 "OFOxmEthDstMasked": OxmMapEntry("MacAddress", "ETH_DST", True),
169 "OFOxmEthSrc": OxmMapEntry("MacAddress", "ETH_SRC", False),
170 "OFOxmEthSrcMasked": OxmMapEntry("MacAddress", "ETH_SRC", True),
171 "OFOxmEthType": OxmMapEntry("EthType", "ETH_TYPE", False),
172 "OFOxmEthTypeMasked": OxmMapEntry("EthType", "ETH_TYPE", True),
173 "OFOxmVlanVid": OxmMapEntry("VlanVid", "VLAN_VID", False),
174 "OFOxmVlanVidMasked": OxmMapEntry("VlanVid", "VLAN_VID", True),
175 "OFOxmVlanPcp": OxmMapEntry("VlanPcp", "VLAN_PCP", False),
176 "OFOxmVlanPcpMasked": OxmMapEntry("VlanPcp", "VLAN_PCP", True),
177 "OFOxmIpDscp": OxmMapEntry("IpDscp", "IP_DSCP", False),
178 "OFOxmIpDscpMasked": OxmMapEntry("IpDscp", "IP_DSCP", True),
179 "OFOxmIpEcn": OxmMapEntry("IpEcn", "IP_ECN", False),
180 "OFOxmIpEcnMasked": OxmMapEntry("IpEcn", "IP_ECN", True),
181 "OFOxmIpProto": OxmMapEntry("IpProtocol", "IP_PROTO", False),
182 "OFOxmIpProtoMasked": OxmMapEntry("IpProtocol", "IP_PROTO", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700183 "OFOxmIpv4Src": OxmMapEntry("IPv4Address", "IPV4_SRC", False),
184 "OFOxmIpv4SrcMasked": OxmMapEntry("IPv4Address", "IPV4_SRC", True),
185 "OFOxmIpv4Dst": OxmMapEntry("IPv4Address", "IPV4_DST", False),
186 "OFOxmIpv4DstMasked": OxmMapEntry("IPv4Address", "IPV4_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700187 "OFOxmTcpSrc": OxmMapEntry("TransportPort", "TCP_SRC", False),
188 "OFOxmTcpSrcMasked": OxmMapEntry("TransportPort", "TCP_SRC", True),
189 "OFOxmTcpDst": OxmMapEntry("TransportPort", "TCP_DST", False),
190 "OFOxmTcpDstMasked": OxmMapEntry("TransportPort", "TCP_DST", True),
191 "OFOxmUdpSrc": OxmMapEntry("TransportPort", "UDP_SRC", False),
192 "OFOxmUdpSrcMasked": OxmMapEntry("TransportPort", "UDP_SRC", True),
193 "OFOxmUdpDst": OxmMapEntry("TransportPort", "UDP_DST", False),
194 "OFOxmUdpDstMasked": OxmMapEntry("TransportPort", "UDP_DST", True),
195 "OFOxmSctpSrc": OxmMapEntry("TransportPort", "SCTP_SRC", False),
196 "OFOxmSctpSrcMasked": OxmMapEntry("TransportPort", "SCTP_SRC", True),
197 "OFOxmSctpDst": OxmMapEntry("TransportPort", "SCTP_DST", False),
198 "OFOxmSctpDstMasked": OxmMapEntry("TransportPort", "SCTP_DST", True),
199 "OFOxmIcmpv4Type": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", False),
200 "OFOxmIcmpv4TypeMasked": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", True),
201 "OFOxmIcmpv4Code": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", False),
202 "OFOxmIcmpv4CodeMasked": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", True),
203 "OFOxmArpOp": OxmMapEntry("ArpOpcode", "ARP_OP", False),
204 "OFOxmArpOpMasked": OxmMapEntry("ArpOpcode", "ARP_OP", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700205 "OFOxmArpSpa": OxmMapEntry("IPv4Address", "ARP_SPA", False),
206 "OFOxmArpSpaMasked": OxmMapEntry("IPv4Address", "ARP_SPA", True),
207 "OFOxmArpTpa": OxmMapEntry("IPv4Address", "ARP_TPA", False),
208 "OFOxmArpTpaMasked": OxmMapEntry("IPv4Address", "ARP_TPA", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700209 "OFOxmArpSha": OxmMapEntry("MacAddress", "ARP_SHA", False),
210 "OFOxmArpShaMasked": OxmMapEntry("MacAddress", "ARP_SHA", True),
211 "OFOxmArpTha": OxmMapEntry("MacAddress", "ARP_THA", False),
212 "OFOxmArpThaMasked": OxmMapEntry("MacAddress", "ARP_THA", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700213 "OFOxmIpv6Src": OxmMapEntry("IPv6Address", "IPV6_SRC", False),
214 "OFOxmIpv6SrcMasked": OxmMapEntry("IPv6Address", "IPV6_SRC", True),
215 "OFOxmIpv6Dst": OxmMapEntry("IPv6Address", "IPV6_DST", False),
216 "OFOxmIpv6DstMasked": OxmMapEntry("IPv6Address", "IPV6_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700217 "OFOxmIpv6Flabel": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", False),
Yotam Harchola86e4252013-09-06 15:36:28 -0700218 "OFOxmIpv6FlabelMasked": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", True),
219 "OFOxmIcmpv6Type": OxmMapEntry("U8", "ICMPV6_TYPE", False),
220 "OFOxmIcmpv6TypeMasked": OxmMapEntry("U8", "ICMPV6_TYPE", True),
221 "OFOxmIcmpv6Code": OxmMapEntry("U8", "ICMPV6_CODE", False),
222 "OFOxmIcmpv6CodeMasked": OxmMapEntry("U8", "ICMPV6_CODE", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700223 "OFOxmIpv6NdTarget": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", False),
224 "OFOxmIpv6NdTargetMasked": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700225 "OFOxmIpv6NdSll": OxmMapEntry("MacAddress", "IPV6_ND_SLL", False),
226 "OFOxmIpv6NdSllMasked": OxmMapEntry("MacAddress", "IPV6_ND_SLL", True),
227 "OFOxmIpv6NdTll": OxmMapEntry("MacAddress", "IPV6_ND_TLL", False),
228 "OFOxmIpv6NdTllMasked": OxmMapEntry("MacAddress", "IPV6_ND_TLL", True),
229 "OFOxmMplsLabel": OxmMapEntry("U32", "MPLS_LABEL", False),
230 "OFOxmMplsLabelMasked": OxmMapEntry("U32", "MPLS_LABEL", True),
231 "OFOxmMplsTc": OxmMapEntry("U8", "MPLS_TC", False),
232 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True)
233 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700234
Andreas Wundsamf1949682013-09-23 14:48:31 -0700235 # Registry of nullable properties:
236 # ${java_class_name} -> set(${java_property_name})
237 nullable_map = defaultdict(lambda: set(),
238 )
239
240 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
241 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
242 # name: a name for the group
243 # mask: java name of the enum entry that defines the mask
244 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700245 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
246
Andreas Wundsamf1949682013-09-23 14:48:31 -0700247 # registry of MaskedEnumGroups (see above).
248 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700249 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700250 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
251 OFConfigFlags = (
252 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700253 ),
254 OFTableConfig = (
255 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
256 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700257 )
258
Andreas Wundsamf1949682013-09-23 14:48:31 -0700259 # represents a metadata property associated with an EnumClass
260 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700261 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700262 """
263 represents a metadata property associated with an Enum Class
264 @param name name of metadata property
265 @param type java_type instance describing the type
266 @value: Generator function f(entry) that generates the value
267 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700268 @property
269 def variable_name(self):
270 return self.name[0].lower() + self.name[1:]
271
272 @property
273 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700274 prefix = "is" if self.type == java_type.boolean else "get"
275 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700276
Andreas Wundsamf1949682013-09-23 14:48:31 -0700277 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700278 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
279
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700280 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700281 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700282 splits = enum_entry.name.split("_")
283 if len(splits)>=2:
284 m = re.match(r'\d+[MGTP]B', splits[1])
285 if m:
286 return "PortSpeed.SPEED_{}".format(splits[1])
287 return "PortSpeed.SPEED_NONE";
288
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700289 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700290 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700291 splits = enum_entry.name.split("_")
292 if len(splits)>=1:
293 if splits[0] == "STP":
294 return "true"
295 return "false"
296
Andreas Wundsamf1949682013-09-23 14:48:31 -0700297 # registry for metadata properties for enums
298 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700299 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700300 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
301 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700302 OFErrorCode = OFEnumMetadata((OFEnumPropertyMetadata("ErrorType", java_type.error_type, gen_error_type),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700303 )
304
Andreas Wundsam27303462013-07-16 12:52:35 -0700305 @property
306 @memoize
307 def versions(self):
308 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
309
310 @property
311 @memoize
312 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700313 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700314
315 for raw_version, of_protocol in of_g.ir.items():
316 jversion = JavaOFVersion(of_protocol.wire_version)
317
318 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700319 if not of_class.name in version_map_per_class:
320 version_map_per_class[of_class.name] = collections.OrderedDict()
321
Andreas Wundsam27303462013-07-16 12:52:35 -0700322 version_map_per_class[of_class.name][jversion] = of_class
323
324 interfaces = []
325 for class_name, version_map in version_map_per_class.items():
326 interfaces.append(JavaOFInterface(class_name, version_map))
327
Andreas Wundsambe168f72013-08-03 22:49:35 -0700328 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
329
Andreas Wundsam27303462013-07-16 12:52:35 -0700330 return interfaces
331
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700332 @memoize
333 def interface_by_name(self, name):
334 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
335
Andreas Wundsam27303462013-07-16 12:52:35 -0700336 @property
337 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700338 def all_classes(self):
339 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
340
341 @property
342 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700343 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700344 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700345
346 for version in self.versions:
347 of_protocol = of_g.ir[version.int_version]
348 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700349 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700350
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700351 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
352 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700353
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700354 # inelegant - need java name here
355 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700356 return enums
357
358 @memoize
359 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700360 res = find(lambda e: e.name == name, self.enums)
361 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700362 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700363 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700364
Andreas Wundsam5204de22013-07-30 11:34:45 -0700365 @property
366 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700367 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700368 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700369
370 factories = OrderedDict()
371
372 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
373 for base_class in sub_factory_classes:
374 package = base_class[2:].lower()
375 remove_prefix = base_class[2].lower() + base_class[3:]
376
377 # HACK need to have a better way to deal with parameterized base classes
378 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
379
380 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
381 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={})
382
383 factories[""] = OFFactory(
384 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700385 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700386 remove_prefix="",
387 members=[], base_class="OFMessage", sub_factories=OrderedDict(
388 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ))
389
390 for i in self.interfaces:
391 for n, factory in factories.items():
392 if n == "":
393 factory.members.append(i)
394 break
395 else:
396 super_class = self.interface_by_name(n)
397 if i.is_instance_of(super_class):
398 factory.members.append(i)
399 break
400 return factories.values()
Andreas Wundsam5204de22013-07-30 11:34:45 -0700401
402 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700403 """ return wether or not to generate implementation class clazz.
404 Now true for everything except OFTableModVer10.
405 @param clazz JavaOFClass instance
406 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700407 if clazz.interface.name.startswith("OFMatchV"):
408 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700409 elif clazz.name == "OFTableModVer10":
410 # tablemod ver 10 is a hack and has no oftype defined
411 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700412 if loxi_utils.class_is_message(clazz.interface.c_name):
413 return True
414 if loxi_utils.class_is_oxm(clazz.interface.c_name):
415 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700416 if loxi_utils.class_is_action(clazz.interface.c_name):
417 return True
418 if loxi_utils.class_is_instruction(clazz.interface.c_name):
419 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700420 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700421 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700422
423
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700424class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700425 @property
426 def factory_classes(self):
427 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700428 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700429 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700430 interface=self,
431 version=version
432 ) for version in model.versions ]
433
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700434 def method_name(self, member, builder=True):
435 n = member.variable_name
436 if n.startswith(self.remove_prefix):
437 n = n[len(self.remove_prefix):]
438 n = n[0].lower() + n[1:]
439 if builder:
440 return "build" + n[0].upper() + n[1:]
441 else:
442 return n
Andreas Wundsam5204de22013-07-30 11:34:45 -0700443
444OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700445class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
446 @property
447 def base_class(self):
448 return self.interface.base_class
449
450 @property
451 def versioned_base_class(self):
452 base_class_interface = model.interface_by_name(self.interface.base_class)
453 if base_class_interface and base_class_interface.has_version(self.version):
454 return base_class_interface.versioned_class(self.version)
455 else:
456 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700457
Andreas Wundsam27303462013-07-16 12:52:35 -0700458model = JavaModel()
459
460#######################################################################
461### OFVersion
462#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700463
464class JavaOFVersion(object):
465 """ Models a version of OpenFlow. contains methods to convert the internal
466 Loxi version to a java constant / a string """
467 def __init__(self, int_version):
468 self.int_version = int(int_version)
469
470 @property
471 def of_version(self):
472 return "1" + str(int(self.int_version) - 1)
473
474 @property
475 def constant_version(self):
476 return "OF_" + self.of_version
477
Andreas Wundsam27303462013-07-16 12:52:35 -0700478 def __repr__(self):
479 return "JavaOFVersion(%d)" % self.int_version
480
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700481 def __str__(self):
482 return of_g.param_version_names[self.int_version]
483
Andreas Wundsam27303462013-07-16 12:52:35 -0700484 def __hash__(self):
485 return hash(self.int_version)
486
487 def __eq__(self, other):
488 if other is None or type(self) != type(other):
489 return False
490 return (self.int_version,) == (other.int_version,)
491
492#######################################################################
493### Interface
494#######################################################################
495
496class JavaOFInterface(object):
497 """ Models an OpenFlow Message class for the purpose of the java class.
498 Version agnostic, in contrast to the loxi_ir python model.
499 """
500 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700501 """"
502 @param c_name: loxi style name (e.g., of_flow_add)
503 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
504 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700505 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700506 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700507 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700508 self.name = java_type.name_c_to_caps_camel(c_name) if c_name != "of_header" else "OFMessage"
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700509 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700510 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700511 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700512 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700513 self.constant_name = c_name.upper().replace("OF_", "")
514
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700515 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700516 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700517 if self.name != parent_interface:
518 self.parent_interface = parent_interface
519 else:
520 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700521
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700522 @property
523 @memoize
524 def all_parent_interfaces(self):
525 return [ "OFObject" ] + \
526 ([ self.parent_interface ] if self.parent_interface else [] )+ \
527 self.additional_parent_interfaces
528 @property
529 @memoize
530 def additional_parent_interfaces(self):
531 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
532 m = re.match(r'(.*)Request$', self.name)
533 if m:
534 reply_name = m.group(1) + "Reply"
535 if model.interface_by_name(reply_name):
536 return ["OFRequest<%s>" % reply_name ]
537 return []
538
539
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700540 def is_instance_of(self, other_class):
541 if self == other_class:
542 return True
543 parent = self.super_class
544 if parent is None:
545 return False
546 else:
547 return parent.is_instance_of(other_class)
548
549 @property
550 def super_class(self):
551 if not self.parent_interface:
552 return None
553 else:
554 return model.interface_by_name(self.parent_interface)
555
556
557 def inherited_declaration(self, type_spec="?"):
558 if self.type_annotation:
559 return "%s<%s>" % (self.name, type_spec)
560 else:
561 return "%s" % self.name
562
563 @property
564 def type_variable(self):
565 if self.type_annotation:
566 return "<T>"
567 else:
568 return "";
569
Andreas Wundsam27303462013-07-16 12:52:35 -0700570 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700571 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
572 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
573 # model (note, that the loxi model is on versioned classes). Should check/infer the
574 # inheritance information from the versioned lox_ir classes.
Andreas Wundsam001b1822013-08-02 22:25:55 -0700575 if re.match(r'OF.+StatsRequest$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700576 return ("", "OFStatsRequest", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700577 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700578 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700579 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700580 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700581 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 -0700582 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700583 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 -0700584 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700585 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
586 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700587 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700588 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700589 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700590 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700591 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700592 if re.match(r'OFActionBsn.+', self.name):
593 return ("action", "OFActionBsn", None)
594 elif re.match(r'OFActionNicira.+', self.name):
595 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700596 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
597 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700598 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700599 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700600 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700601 return ("", "OFBsnVport", None)
602 elif self.name == "OFOxm":
603 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700604 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700605 if self.name in model.oxm_map:
606 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
607 else:
608 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700609 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700610 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700611 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700612 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700613 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700614 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700615 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700616 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700617 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700618 return ("", None, None)
619
620 @property
621 @memoize
622 def writeable_members(self):
623 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700624
625 @property
626 @memoize
627 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700628 return self.ir_model_members + self.virtual_members
629
630 @property
631 @memoize
632 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700633 """return a list of all members to be exposed by this interface. Corresponds to
634 the union of the members of the vesioned classes without length, fieldlength
635 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700636 all_versions = []
637 member_map = collections.OrderedDict()
638
639 for (version, of_class) in self.version_map.items():
640 for of_member in of_class.members:
641 if isinstance(of_member, OFLengthMember) or \
642 isinstance(of_member, OFFieldLengthMember) or \
643 isinstance(of_member, OFPadMember):
644 continue
645 if of_member.name not in member_map:
646 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
647
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700648 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 -0700649
650 @property
651 def virtual_members(self):
652 if self.name == "OFOxm":
653 return (
654 JavaVirtualMember(self, "value", java_type.generic_t),
655 JavaVirtualMember(self, "mask", java_type.generic_t),
656 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
657 JavaVirtualMember(self, "masked", java_type.boolean),
658 )
659 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
660 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
661 if self.name in model.oxm_map \
662 else java_type.make_match_field_jtype()
663
664 return (
665 JavaVirtualMember(self, "matchField", field_type),
666 JavaVirtualMember(self, "masked", java_type.boolean),
667 ) \
668 + \
669 (
670 ( 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
671 ()
672 )
673 else:
674 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700675
676 @property
677 @memoize
678 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700679 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700680 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 -0700681
682 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700683 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700684 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700685 return len(self.all_versions) == len(model.versions)
686
687 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700688 @memoize
689 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700690 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700691 return self.version_map.keys()
692
Andreas Wundsam5204de22013-07-30 11:34:45 -0700693 def has_version(self, version):
694 return version in self.version_map
695
Andreas Wundsam27303462013-07-16 12:52:35 -0700696 def versioned_class(self, version):
697 return JavaOFClass(self, version, self.version_map[version])
698
699 @property
700 @memoize
701 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700702 return [ self.versioned_class(version) for version in self.all_versions ]
703
704#######################################################################
705### (Versioned) Classes
706#######################################################################
707
708class JavaOFClass(object):
709 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700710 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700711 """
712 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700713 """
714 @param interface JavaOFInterface instance of the parent interface
715 @param version JavaOFVersion
716 @param ir_class OFClass from loxi_ir
717 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700718 self.interface = interface
719 self.ir_class = ir_class
720 self.c_name = self.ir_class.name
721 self.version = version
722 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700723 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700724 self.generated = False
725
726 @property
727 @memoize
728 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700729 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700730
731 @property
732 def name(self):
733 return "%sVer%s" % (self.interface.name, self.version.of_version)
734
735 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700736 def variable_name(self):
737 return self.name[3:]
738
739 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700740 def length(self):
741 if self.is_fixed_length:
742 return self.min_length
743 else:
744 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
745
746 @property
747 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700748 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700749 id_tuple = (self.ir_class.name, self.version.int_version)
750 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
751
752 @property
753 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700754 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700755 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
756 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700757
758 def all_properties(self):
759 return self.interface.members
760
761 def get_member(self, name):
762 for m in self.members:
763 if m.name == name:
764 return m
765
766 @property
767 @memoize
768 def data_members(self):
769 return [ prop for prop in self.members if prop.is_data ]
770
771 @property
772 @memoize
773 def fixed_value_members(self):
774 return [ prop for prop in self.members if prop.is_fixed_value ]
775
776 @property
777 @memoize
778 def public_members(self):
779 return [ prop for prop in self.members if prop.is_public ]
780
781 @property
782 @memoize
783 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700784 return self.ir_model_members + self.virtual_members
785
786 @property
787 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700788 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700789 return tuple(members)
790
791 @property
792 def virtual_members(self):
793 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
794 if self.interface.name in model.oxm_map:
795 oxm_entry = model.oxm_map[self.interface.name]
796 return (
797 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
798 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
799 )
800 else:
801 return (
802 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
803 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
804 )
805 else:
806 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700807
808 def all_versions(self):
809 return [ JavaOFVersion(int_version)
810 for int_version in of_g.unified[self.c_name]
811 if int_version != 'union' and int_version != 'object_id' ]
812
813 def version_is_inherited(self, version):
814 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
815
816 def inherited_from(self, version):
817 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
818
819 @property
820 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700821 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
822
823 @property
824 def discriminator(self):
825 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700826
827 @property
828 def is_extension(self):
829 return type_maps.message_is_extension(self.c_name, -1)
830
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700831 @property
832 def align(self):
833 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
834
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700835 @property
836 @memoize
837 def superclass(self):
838 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
839
840 @property
841 @memoize
842 def subclasses(self):
843 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
844
Andreas Wundsam27303462013-07-16 12:52:35 -0700845#######################################################################
846### Member
847#######################################################################
848
849
850class JavaMember(object):
851 """ Models a property (member) of an openflow class. """
852 def __init__(self, msg, name, java_type, member):
853 self.msg = msg
854 self.name = name
855 self.java_type = java_type
856 self.member = member
857 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700858
859 @property
860 def title_name(self):
861 return self.name[0].upper() + self.name[1:]
862
863 @property
864 def constant_name(self):
865 return self.c_name.upper()
866
867 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700868 def getter_name(self):
869 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
870
871 @property
872 def setter_name(self):
873 return "set" + self.title_name
874
875 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700876 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700877 if self.is_fixed_value:
878 return self.constant_name
879 else:
880 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700881
882 @property
883 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700884 if self.is_fixed_value:
885 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700886 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700887 default = self.java_type.default_op(self.msg.version)
888 if default == "null" and not self.is_nullable:
889 return None
890 else:
891 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700892
893 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700894 def enum_value(self):
895 if self.name == "version":
896 return "OFVersion.%s" % self.msg.version.constant_version
897
898 java_type = self.java_type.public_type;
899 try:
900 global model
901 enum = model.enum_by_name(java_type)
902 entry = enum.entry_by_version_value(self.msg.version, self.value)
903 return "%s.%s" % ( enum.name, entry.name)
904 except KeyError, e:
905 print e.message
906 return self.value
907
908 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700909 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700910 return isinstance(self.member, OFPadMember)
911
912 def is_type_value(self, version=None):
913 if(version==None):
914 return any(self.is_type_value(version) for version in self.msg.all_versions)
915 try:
916 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
917 except:
918 return False
919
920 @property
921 def is_field_length_value(self):
922 return isinstance(self.member, OFFieldLengthMember)
923
924 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700925 def is_discriminator(self):
926 return isinstance(self.member, OFDiscriminatorMember)
927
928 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700929 def is_length_value(self):
930 return isinstance(self.member, OFLengthMember)
931
932 @property
933 def is_public(self):
934 return not (self.is_pad or self.is_length_value)
935
936 @property
937 def is_data(self):
938 return isinstance(self.member, OFDataMember) and self.name != "version"
939
940 @property
941 def is_fixed_value(self):
942 return hasattr(self.member, "value") or self.name == "version" \
943 or ( self.name == "length" and self.msg.is_fixed_length) \
944 or ( self.name == "len" and self.msg.is_fixed_length)
945
946 @property
947 def value(self):
948 if self.name == "version":
949 return self.msg.version.int_version
950 elif self.name == "length" or self.name == "len":
951 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700952 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700953 return self.java_type.format_value(self.member.value)
954
955 @property
956 def priv_value(self):
957 if self.name == "version":
958 return self.msg.version.int_version
959 elif self.name == "length" or self.name == "len":
960 return self.msg.length
961 else:
962 return self.java_type.format_value(self.member.value, pub_type=False)
963
Andreas Wundsam27303462013-07-16 12:52:35 -0700964
965 @property
966 def is_writeable(self):
967 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
968
969 def get_type_value_info(self, version):
970 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700971
972 @property
973 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700974 if hasattr(self.member, "length"):
975 return self.member.length
976 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700977 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700978 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700979
980 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700981 def for_of_member(java_class, member):
982 if isinstance(member, OFPadMember):
983 return JavaMember(None, "", None, member)
984 else:
985 if member.name == 'len':
986 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700987 elif member.name == 'value_mask':
988 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700989 else:
990 name = java_type.name_c_to_camel(member.name)
991 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
992 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700993
994 @property
995 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700996 if not self.msg.c_name in of_g.unified:
997 print("%s not self.unified" % self.msg.c_name)
998 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700999 for version in of_g.unified[self.msg.c_name]:
1000 if version == 'union' or version =='object_id':
1001 continue
1002 if 'use_version' in of_g.unified[self.msg.c_name][version]:
1003 continue
1004
Andreas Wundsam27303462013-07-16 12:52:35 -07001005 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 -07001006 return False
1007 return True
1008
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001009 @property
1010 def is_virtual(self):
1011 return False
1012
Andreas Wundsam27303462013-07-16 12:52:35 -07001013 def __hash__(self):
1014 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001015
Andreas Wundsam27303462013-07-16 12:52:35 -07001016 def __eq__(self, other):
1017 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001018 return False
Andreas Wundsam27303462013-07-16 12:52:35 -07001019 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001020
Andreas Wundsamf1949682013-09-23 14:48:31 -07001021 @property
1022 def is_nullable(self):
1023 return self.name in model.nullable_map[self.msg.name]
1024
1025
Andreas Wundsam2be7da52013-08-22 07:34:25 -07001026class JavaVirtualMember(JavaMember):
1027 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
1028 def __init__(self, msg, name, java_type, value=None):
1029 JavaMember.__init__(self, msg, name, java_type, member=None)
1030 self._value = value
1031
1032 @property
1033 def is_fixed_value(self):
1034 return True
1035
1036 @property
1037 def value(self):
1038 return self._value
1039
1040 @property
1041 def priv_value(self):
1042 return self._value
1043
1044
1045 @property
1046 def is_universal(self):
1047 return True
1048
1049 @property
1050 def is_virtual(self):
1051 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001052
1053#######################################################################
1054### Unit Test
1055#######################################################################
1056
Yotam Harchol466b3212013-08-15 12:14:46 -07001057class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001058 def __init__(self, java_class):
1059 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001060 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001061 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001062 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1063 name=java_class.c_name[3:]) + "{i}.data"
1064 test_class_name = self.java_class.name + "Test"
1065 self.test_units = []
1066 if test_data.exists(first_data_file_name):
1067 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1068 i = 1
1069 while test_data.exists(data_file_template.format(i=i)):
1070 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1071 i = i + 1
Andreas Wundsamf1949682013-09-23 14:48:31 -07001072
Yotam Harchol466b3212013-08-15 12:14:46 -07001073 @property
1074 def package(self):
1075 return self.java_class.package
1076
1077 @property
1078 def has_test_data(self):
1079 return len(self.test_units) > 0
1080
1081 @property
1082 def length(self):
1083 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001084
Yotam Harchol466b3212013-08-15 12:14:46 -07001085 def get_test_unit(self, i):
1086 return self.test_units[i]
1087
1088
1089class JavaUnitTest(object):
1090 def __init__(self, java_class, file_name=None, test_class_name=None):
1091 self.java_class = java_class
1092 if file_name is None:
1093 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1094 name=java_class.c_name[3:])
1095 else:
1096 self.data_file_name = file_name
1097 if test_class_name is None:
1098 self.test_class_name = self.java_class.name + "Test"
1099 else:
1100 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001101
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001102 @property
1103 def package(self):
1104 return self.java_class.package
1105
1106 @property
1107 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001108 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001109
1110 @property
1111 def has_test_data(self):
1112 return test_data.exists(self.data_file_name)
1113
1114 @property
1115 @memoize
1116 def test_data(self):
1117 return test_data.read(self.data_file_name)
1118
1119
Andreas Wundsam27303462013-07-16 12:52:35 -07001120#######################################################################
1121### Enums
1122#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001123
Andreas Wundsam27303462013-07-16 12:52:35 -07001124class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001125 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001126 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001127
1128 if c_name == "of_stats_types":
1129 self.name = "OFStatsType"
1130 else:
1131 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001132
Andreas Wundsam27303462013-07-16 12:52:35 -07001133 # Port_features has constants that start with digits
1134 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001135
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001136 self.version_enums = version_enum_map
1137
1138 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1139 for version, ir_enum in version_enum_map.items():
1140 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001141 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1142
Andreas Wundsam27303462013-07-16 12:52:35 -07001143 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001144 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001145
1146 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 -07001147 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001148
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001149 self.metadata = model.enum_metadata_map[self.name]
1150
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001151 def wire_type(self, version):
1152 ir_enum = self.version_enums[version]
1153 if "wire_type" in ir_enum.params:
1154 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1155 else:
1156 return java_type.u8
1157
1158 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001159 @memoize
1160 def is_bitmask(self):
1161 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1162
1163 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001164 def versions(self):
1165 return self.version_enums.keys()
1166
Andreas Wundsam27303462013-07-16 12:52:35 -07001167 @memoize
1168 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001169 res = find(lambda e: e.name == name, self.entries)
1170 if res:
1171 return res
1172 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001173 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1174
1175 @memoize
1176 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001177 res = find(lambda e: e.c_name == name, self.entries)
1178 if res:
1179 return res
1180 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001181 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1182
1183 @memoize
1184 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001185 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1186 if res:
1187 return res
1188 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001189 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1190
1191# values: Map JavaVersion->Value
1192class JavaEnumEntry(object):
1193 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001194 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001195 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1196 self.values = values
1197
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001198 @property
1199 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001200 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001201
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001202 def has_value(self, version):
1203 return version in self.values
1204
Andreas Wundsam27303462013-07-16 12:52:35 -07001205 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001206 return self.values[version]
1207
1208 def format_value(self, version):
1209 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001210 return res
1211
Andreas Wundsam27303462013-07-16 12:52:35 -07001212 def all_values(self, versions, not_present=None):
1213 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001214
1215 @property
1216 @memoize
1217 def masked_enum_group(self):
1218 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1219 return group
1220
1221 @property
1222 @memoize
1223 def is_mask(self):
1224 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])