blob: c0741895c4b5549381ace9fa1e29d6a8c3150e3d [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):
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700141 enum_blacklist = set(("OFDefinitions", "OFPortNo"))
Andreas Wundsam43526532013-08-01 22:03:50 -0700142 enum_entry_blacklist = defaultdict(lambda: set(), OFFlowWildcards=set([ "NW_DST_BITS", "NW_SRC_BITS", "NW_SRC_SHIFT", "NW_DST_SHIFT" ]))
Andreas Wundsambe168f72013-08-03 22:49:35 -0700143 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
144 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700145 read_blacklist = defaultdict(lambda: set(), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
146 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 Wundsam001b1822013-08-02 22:25:55 -0700147 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -0700148
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700149 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
Yotam Harchole5d92972013-08-22 14:18:36 -0700150 oxm_map = { "OFOxmInPort": OxmMapEntry("OFPort", "IN_PORT", False),
151 "OFOxmInPortMasked": OxmMapEntry("OFPort", "IN_PORT", True),
152 "OFOxmInPhyPort": OxmMapEntry("OFPort", "IN_PHY_PORT", False),
153 "OFOxmInPhyPortMasked": OxmMapEntry("OFPort", "IN_PHY_PORT", True),
154 "OFOxmMetadata": OxmMapEntry("OFMetadata", "METADATA", False),
155 "OFOxmMetadataMasked": OxmMapEntry("OFMetadata", "METADATA", True),
156 "OFOxmEthDst": OxmMapEntry("MacAddress", "ETH_DST", False),
157 "OFOxmEthDstMasked": OxmMapEntry("MacAddress", "ETH_DST", True),
158 "OFOxmEthSrc": OxmMapEntry("MacAddress", "ETH_SRC", False),
159 "OFOxmEthSrcMasked": OxmMapEntry("MacAddress", "ETH_SRC", True),
160 "OFOxmEthType": OxmMapEntry("EthType", "ETH_TYPE", False),
161 "OFOxmEthTypeMasked": OxmMapEntry("EthType", "ETH_TYPE", True),
162 "OFOxmVlanVid": OxmMapEntry("VlanVid", "VLAN_VID", False),
163 "OFOxmVlanVidMasked": OxmMapEntry("VlanVid", "VLAN_VID", True),
164 "OFOxmVlanPcp": OxmMapEntry("VlanPcp", "VLAN_PCP", False),
165 "OFOxmVlanPcpMasked": OxmMapEntry("VlanPcp", "VLAN_PCP", True),
166 "OFOxmIpDscp": OxmMapEntry("IpDscp", "IP_DSCP", False),
167 "OFOxmIpDscpMasked": OxmMapEntry("IpDscp", "IP_DSCP", True),
168 "OFOxmIpEcn": OxmMapEntry("IpEcn", "IP_ECN", False),
169 "OFOxmIpEcnMasked": OxmMapEntry("IpEcn", "IP_ECN", True),
170 "OFOxmIpProto": OxmMapEntry("IpProtocol", "IP_PROTO", False),
171 "OFOxmIpProtoMasked": OxmMapEntry("IpProtocol", "IP_PROTO", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700172 "OFOxmIpv4Src": OxmMapEntry("IPv4Address", "IPV4_SRC", False),
173 "OFOxmIpv4SrcMasked": OxmMapEntry("IPv4Address", "IPV4_SRC", True),
174 "OFOxmIpv4Dst": OxmMapEntry("IPv4Address", "IPV4_DST", False),
175 "OFOxmIpv4DstMasked": OxmMapEntry("IPv4Address", "IPV4_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700176 "OFOxmTcpSrc": OxmMapEntry("TransportPort", "TCP_SRC", False),
177 "OFOxmTcpSrcMasked": OxmMapEntry("TransportPort", "TCP_SRC", True),
178 "OFOxmTcpDst": OxmMapEntry("TransportPort", "TCP_DST", False),
179 "OFOxmTcpDstMasked": OxmMapEntry("TransportPort", "TCP_DST", True),
180 "OFOxmUdpSrc": OxmMapEntry("TransportPort", "UDP_SRC", False),
181 "OFOxmUdpSrcMasked": OxmMapEntry("TransportPort", "UDP_SRC", True),
182 "OFOxmUdpDst": OxmMapEntry("TransportPort", "UDP_DST", False),
183 "OFOxmUdpDstMasked": OxmMapEntry("TransportPort", "UDP_DST", True),
184 "OFOxmSctpSrc": OxmMapEntry("TransportPort", "SCTP_SRC", False),
185 "OFOxmSctpSrcMasked": OxmMapEntry("TransportPort", "SCTP_SRC", True),
186 "OFOxmSctpDst": OxmMapEntry("TransportPort", "SCTP_DST", False),
187 "OFOxmSctpDstMasked": OxmMapEntry("TransportPort", "SCTP_DST", True),
188 "OFOxmIcmpv4Type": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", False),
189 "OFOxmIcmpv4TypeMasked": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", True),
190 "OFOxmIcmpv4Code": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", False),
191 "OFOxmIcmpv4CodeMasked": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", True),
192 "OFOxmArpOp": OxmMapEntry("ArpOpcode", "ARP_OP", False),
193 "OFOxmArpOpMasked": OxmMapEntry("ArpOpcode", "ARP_OP", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700194 "OFOxmArpSpa": OxmMapEntry("IPv4Address", "ARP_SPA", False),
195 "OFOxmArpSpaMasked": OxmMapEntry("IPv4Address", "ARP_SPA", True),
196 "OFOxmArpTpa": OxmMapEntry("IPv4Address", "ARP_TPA", False),
197 "OFOxmArpTpaMasked": OxmMapEntry("IPv4Address", "ARP_TPA", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700198 "OFOxmArpSha": OxmMapEntry("MacAddress", "ARP_SHA", False),
199 "OFOxmArpShaMasked": OxmMapEntry("MacAddress", "ARP_SHA", True),
200 "OFOxmArpTha": OxmMapEntry("MacAddress", "ARP_THA", False),
201 "OFOxmArpThaMasked": OxmMapEntry("MacAddress", "ARP_THA", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700202 "OFOxmIpv6Src": OxmMapEntry("IPv6Address", "IPV6_SRC", False),
203 "OFOxmIpv6SrcMasked": OxmMapEntry("IPv6Address", "IPV6_SRC", True),
204 "OFOxmIpv6Dst": OxmMapEntry("IPv6Address", "IPV6_DST", False),
205 "OFOxmIpv6DstMasked": OxmMapEntry("IPv6Address", "IPV6_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700206 "OFOxmIpv6Flabel": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", False),
Yotam Harchola86e4252013-09-06 15:36:28 -0700207 "OFOxmIpv6FlabelMasked": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", True),
208 "OFOxmIcmpv6Type": OxmMapEntry("U8", "ICMPV6_TYPE", False),
209 "OFOxmIcmpv6TypeMasked": OxmMapEntry("U8", "ICMPV6_TYPE", True),
210 "OFOxmIcmpv6Code": OxmMapEntry("U8", "ICMPV6_CODE", False),
211 "OFOxmIcmpv6CodeMasked": OxmMapEntry("U8", "ICMPV6_CODE", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700212 "OFOxmIpv6NdTarget": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", False),
213 "OFOxmIpv6NdTargetMasked": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700214 "OFOxmIpv6NdSll": OxmMapEntry("MacAddress", "IPV6_ND_SLL", False),
215 "OFOxmIpv6NdSllMasked": OxmMapEntry("MacAddress", "IPV6_ND_SLL", True),
216 "OFOxmIpv6NdTll": OxmMapEntry("MacAddress", "IPV6_ND_TLL", False),
217 "OFOxmIpv6NdTllMasked": OxmMapEntry("MacAddress", "IPV6_ND_TLL", True),
218 "OFOxmMplsLabel": OxmMapEntry("U32", "MPLS_LABEL", False),
219 "OFOxmMplsLabelMasked": OxmMapEntry("U32", "MPLS_LABEL", True),
220 "OFOxmMplsTc": OxmMapEntry("U8", "MPLS_TC", False),
221 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True)
222 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700223
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700224 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
225
226 masked_enum_groups = defaultdict(lambda: (),
227 OFPortState= (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), )
228 )
229
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700230 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700231 @property
232 def variable_name(self):
233 return self.name[0].lower() + self.name[1:]
234
235 @property
236 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700237 prefix = "is" if self.type == java_type.boolean else "get"
238 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700239
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700240 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
241
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700242 def gen_port_speed(enum_entry):
243 splits = enum_entry.name.split("_")
244 if len(splits)>=2:
245 m = re.match(r'\d+[MGTP]B', splits[1])
246 if m:
247 return "PortSpeed.SPEED_{}".format(splits[1])
248 return "PortSpeed.SPEED_NONE";
249
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700250 def gen_stp_state(enum_entry):
251 splits = enum_entry.name.split("_")
252 if len(splits)>=1:
253 if splits[0] == "STP":
254 return "true"
255 return "false"
256
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700257 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700258 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
259 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Rob Vaterlaus4d311942013-09-24 13:41:44 -0700260 OFErrorCode = OFEnumMetadata((OFEnumPropertyMetadata("ErrorType", java_type.error_type, gen_error_type),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700261 )
262
Andreas Wundsam27303462013-07-16 12:52:35 -0700263 @property
264 @memoize
265 def versions(self):
266 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
267
268 @property
269 @memoize
270 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700271 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700272
273 for raw_version, of_protocol in of_g.ir.items():
274 jversion = JavaOFVersion(of_protocol.wire_version)
275
276 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700277 if not of_class.name in version_map_per_class:
278 version_map_per_class[of_class.name] = collections.OrderedDict()
279
Andreas Wundsam27303462013-07-16 12:52:35 -0700280 version_map_per_class[of_class.name][jversion] = of_class
281
282 interfaces = []
283 for class_name, version_map in version_map_per_class.items():
284 interfaces.append(JavaOFInterface(class_name, version_map))
285
Andreas Wundsambe168f72013-08-03 22:49:35 -0700286 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
287
Andreas Wundsam27303462013-07-16 12:52:35 -0700288 return interfaces
289
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700290 @memoize
291 def interface_by_name(self, name):
292 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
293
Andreas Wundsam27303462013-07-16 12:52:35 -0700294 @property
295 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700296 def all_classes(self):
297 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
298
299 @property
300 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700301 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700302 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700303
304 for version in self.versions:
305 of_protocol = of_g.ir[version.int_version]
306 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700307 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700308
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700309 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
310 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700311
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700312 # inelegant - need java name here
313 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700314 return enums
315
316 @memoize
317 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700318 res = find(lambda e: e.name == name, self.enums)
319 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700320 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700321 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700322
Andreas Wundsam5204de22013-07-30 11:34:45 -0700323 @property
324 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700325 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700326 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700327
328 factories = OrderedDict()
329
330 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
331 for base_class in sub_factory_classes:
332 package = base_class[2:].lower()
333 remove_prefix = base_class[2].lower() + base_class[3:]
334
335 # HACK need to have a better way to deal with parameterized base classes
336 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
337
338 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
339 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={})
340
341 factories[""] = OFFactory(
342 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700343 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700344 remove_prefix="",
345 members=[], base_class="OFMessage", sub_factories=OrderedDict(
346 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ))
347
348 for i in self.interfaces:
349 for n, factory in factories.items():
350 if n == "":
351 factory.members.append(i)
352 break
353 else:
354 super_class = self.interface_by_name(n)
355 if i.is_instance_of(super_class):
356 factory.members.append(i)
357 break
358 return factories.values()
Andreas Wundsam5204de22013-07-30 11:34:45 -0700359
360 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700361 """ return wether or not to generate implementation class clazz.
362 Now true for everything except OFTableModVer10.
363 @param clazz JavaOFClass instance
364 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700365 if clazz.interface.name.startswith("OFMatchV"):
366 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700367 elif clazz.name == "OFTableModVer10":
368 # tablemod ver 10 is a hack and has no oftype defined
369 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700370 if loxi_utils.class_is_message(clazz.interface.c_name):
371 return True
372 if loxi_utils.class_is_oxm(clazz.interface.c_name):
373 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700374 if loxi_utils.class_is_action(clazz.interface.c_name):
375 return True
376 if loxi_utils.class_is_instruction(clazz.interface.c_name):
377 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700378 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700379 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700380
381
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700382class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700383 @property
384 def factory_classes(self):
385 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700386 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700387 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700388 interface=self,
389 version=version
390 ) for version in model.versions ]
391
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700392 def method_name(self, member, builder=True):
393 n = member.variable_name
394 if n.startswith(self.remove_prefix):
395 n = n[len(self.remove_prefix):]
396 n = n[0].lower() + n[1:]
397 if builder:
398 return "build" + n[0].upper() + n[1:]
399 else:
400 return n
Andreas Wundsam5204de22013-07-30 11:34:45 -0700401
402OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700403class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
404 @property
405 def base_class(self):
406 return self.interface.base_class
407
408 @property
409 def versioned_base_class(self):
410 base_class_interface = model.interface_by_name(self.interface.base_class)
411 if base_class_interface and base_class_interface.has_version(self.version):
412 return base_class_interface.versioned_class(self.version)
413 else:
414 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700415
Andreas Wundsam27303462013-07-16 12:52:35 -0700416model = JavaModel()
417
418#######################################################################
419### OFVersion
420#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700421
422class JavaOFVersion(object):
423 """ Models a version of OpenFlow. contains methods to convert the internal
424 Loxi version to a java constant / a string """
425 def __init__(self, int_version):
426 self.int_version = int(int_version)
427
428 @property
429 def of_version(self):
430 return "1" + str(int(self.int_version) - 1)
431
432 @property
433 def constant_version(self):
434 return "OF_" + self.of_version
435
Andreas Wundsam27303462013-07-16 12:52:35 -0700436 def __repr__(self):
437 return "JavaOFVersion(%d)" % self.int_version
438
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700439 def __str__(self):
440 return of_g.param_version_names[self.int_version]
441
Andreas Wundsam27303462013-07-16 12:52:35 -0700442 def __hash__(self):
443 return hash(self.int_version)
444
445 def __eq__(self, other):
446 if other is None or type(self) != type(other):
447 return False
448 return (self.int_version,) == (other.int_version,)
449
450#######################################################################
451### Interface
452#######################################################################
453
454class JavaOFInterface(object):
455 """ Models an OpenFlow Message class for the purpose of the java class.
456 Version agnostic, in contrast to the loxi_ir python model.
457 """
458 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700459 """"
460 @param c_name: loxi style name (e.g., of_flow_add)
461 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
462 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700463 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700464 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700465 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700466 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 -0700467 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700468 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700469 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700470 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700471 self.constant_name = c_name.upper().replace("OF_", "")
472
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700473 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700474 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700475 if self.name != parent_interface:
476 self.parent_interface = parent_interface
477 else:
478 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700479
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700480 @property
481 @memoize
482 def all_parent_interfaces(self):
483 return [ "OFObject" ] + \
484 ([ self.parent_interface ] if self.parent_interface else [] )+ \
485 self.additional_parent_interfaces
486 @property
487 @memoize
488 def additional_parent_interfaces(self):
489 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
490 m = re.match(r'(.*)Request$', self.name)
491 if m:
492 reply_name = m.group(1) + "Reply"
493 if model.interface_by_name(reply_name):
494 return ["OFRequest<%s>" % reply_name ]
495 return []
496
497
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700498 def is_instance_of(self, other_class):
499 if self == other_class:
500 return True
501 parent = self.super_class
502 if parent is None:
503 return False
504 else:
505 return parent.is_instance_of(other_class)
506
507 @property
508 def super_class(self):
509 if not self.parent_interface:
510 return None
511 else:
512 return model.interface_by_name(self.parent_interface)
513
514
515 def inherited_declaration(self, type_spec="?"):
516 if self.type_annotation:
517 return "%s<%s>" % (self.name, type_spec)
518 else:
519 return "%s" % self.name
520
521 @property
522 def type_variable(self):
523 if self.type_annotation:
524 return "<T>"
525 else:
526 return "";
527
Andreas Wundsam27303462013-07-16 12:52:35 -0700528 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700529 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
530 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
531 # model (note, that the loxi model is on versioned classes). Should check/infer the
532 # inheritance information from the versioned lox_ir classes.
Andreas Wundsam001b1822013-08-02 22:25:55 -0700533 if re.match(r'OF.+StatsRequest$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700534 return ("", "OFStatsRequest", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700535 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700536 return ("", "OFStatsReply", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700537 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700538 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700539 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 -0700540 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700541 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 -0700542 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700543 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
544 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700545 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700546 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700547 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700548 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700549 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700550 if re.match(r'OFActionBsn.+', self.name):
551 return ("action", "OFActionBsn", None)
552 elif re.match(r'OFActionNicira.+', self.name):
553 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700554 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
555 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700556 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700557 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700558 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700559 return ("", "OFBsnVport", None)
560 elif self.name == "OFOxm":
561 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700562 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700563 if self.name in model.oxm_map:
564 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
565 else:
566 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700567 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700568 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700569 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700570 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700571 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700572 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700573 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700574 return ("", "OFHelloElem", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700575 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700576 return ("", None, None)
577
578 @property
579 @memoize
580 def writeable_members(self):
581 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700582
583 @property
584 @memoize
585 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700586 return self.ir_model_members + self.virtual_members
587
588 @property
589 @memoize
590 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700591 """return a list of all members to be exposed by this interface. Corresponds to
592 the union of the members of the vesioned classes without length, fieldlength
593 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700594 all_versions = []
595 member_map = collections.OrderedDict()
596
597 for (version, of_class) in self.version_map.items():
598 for of_member in of_class.members:
599 if isinstance(of_member, OFLengthMember) or \
600 isinstance(of_member, OFFieldLengthMember) or \
601 isinstance(of_member, OFPadMember):
602 continue
603 if of_member.name not in member_map:
604 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
605
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700606 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 -0700607
608 @property
609 def virtual_members(self):
610 if self.name == "OFOxm":
611 return (
612 JavaVirtualMember(self, "value", java_type.generic_t),
613 JavaVirtualMember(self, "mask", java_type.generic_t),
614 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
615 JavaVirtualMember(self, "masked", java_type.boolean),
616 )
617 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
618 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
619 if self.name in model.oxm_map \
620 else java_type.make_match_field_jtype()
621
622 return (
623 JavaVirtualMember(self, "matchField", field_type),
624 JavaVirtualMember(self, "masked", java_type.boolean),
625 ) \
626 + \
627 (
628 ( 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
629 ()
630 )
631 else:
632 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700633
634 @property
635 @memoize
636 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700637 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700638 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 -0700639
640 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700641 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700642 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700643 return len(self.all_versions) == len(model.versions)
644
645 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700646 @memoize
647 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700648 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700649 return self.version_map.keys()
650
Andreas Wundsam5204de22013-07-30 11:34:45 -0700651 def has_version(self, version):
652 return version in self.version_map
653
Andreas Wundsam27303462013-07-16 12:52:35 -0700654 def versioned_class(self, version):
655 return JavaOFClass(self, version, self.version_map[version])
656
657 @property
658 @memoize
659 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700660 return [ self.versioned_class(version) for version in self.all_versions ]
661
662#######################################################################
663### (Versioned) Classes
664#######################################################################
665
666class JavaOFClass(object):
667 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700668 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700669 """
670 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700671 """
672 @param interface JavaOFInterface instance of the parent interface
673 @param version JavaOFVersion
674 @param ir_class OFClass from loxi_ir
675 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700676 self.interface = interface
677 self.ir_class = ir_class
678 self.c_name = self.ir_class.name
679 self.version = version
680 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700681 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700682 self.generated = False
683
684 @property
685 @memoize
686 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700687 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700688
689 @property
690 def name(self):
691 return "%sVer%s" % (self.interface.name, self.version.of_version)
692
693 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700694 def variable_name(self):
695 return self.name[3:]
696
697 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700698 def length(self):
699 if self.is_fixed_length:
700 return self.min_length
701 else:
702 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
703
704 @property
705 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700706 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700707 id_tuple = (self.ir_class.name, self.version.int_version)
708 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
709
710 @property
711 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700712 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700713 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
714 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700715
716 def all_properties(self):
717 return self.interface.members
718
719 def get_member(self, name):
720 for m in self.members:
721 if m.name == name:
722 return m
723
724 @property
725 @memoize
726 def data_members(self):
727 return [ prop for prop in self.members if prop.is_data ]
728
729 @property
730 @memoize
731 def fixed_value_members(self):
732 return [ prop for prop in self.members if prop.is_fixed_value ]
733
734 @property
735 @memoize
736 def public_members(self):
737 return [ prop for prop in self.members if prop.is_public ]
738
739 @property
740 @memoize
741 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700742 return self.ir_model_members + self.virtual_members
743
744 @property
745 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700746 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700747 return tuple(members)
748
749 @property
750 def virtual_members(self):
751 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
752 if self.interface.name in model.oxm_map:
753 oxm_entry = model.oxm_map[self.interface.name]
754 return (
755 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
756 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
757 )
758 else:
759 return (
760 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
761 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
762 )
763 else:
764 return ()
Andreas Wundsam27303462013-07-16 12:52:35 -0700765
766 def all_versions(self):
767 return [ JavaOFVersion(int_version)
768 for int_version in of_g.unified[self.c_name]
769 if int_version != 'union' and int_version != 'object_id' ]
770
771 def version_is_inherited(self, version):
772 return 'use_version' in of_g.unified[self.ir_class.name][version.int_version]
773
774 def inherited_from(self, version):
775 return JavaOFVersion(of_g.unified[self.ir_class.name][version.int_version]['use_version'])
776
777 @property
778 def is_virtual(self):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700779 return self.ir_class.virtual # type_maps.class_is_virtual(self.c_name) or self.ir_class.virtual
780
781 @property
782 def discriminator(self):
783 return find(lambda m: isinstance(m, OFDiscriminatorMember), self.ir_class.members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700784
785 @property
786 def is_extension(self):
787 return type_maps.message_is_extension(self.c_name, -1)
788
Andreas Wundsam758c9cc2013-08-01 22:16:06 -0700789 @property
790 def align(self):
791 return int(self.ir_class.params['align']) if 'align' in self.ir_class.params else 0
792
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700793 @property
794 @memoize
795 def superclass(self):
796 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
797
798 @property
799 @memoize
800 def subclasses(self):
801 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
802
Andreas Wundsam27303462013-07-16 12:52:35 -0700803#######################################################################
804### Member
805#######################################################################
806
807
808class JavaMember(object):
809 """ Models a property (member) of an openflow class. """
810 def __init__(self, msg, name, java_type, member):
811 self.msg = msg
812 self.name = name
813 self.java_type = java_type
814 self.member = member
815 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700816
817 @property
818 def title_name(self):
819 return self.name[0].upper() + self.name[1:]
820
821 @property
822 def constant_name(self):
823 return self.c_name.upper()
824
825 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700826 def getter_name(self):
827 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
828
829 @property
830 def setter_name(self):
831 return "set" + self.title_name
832
833 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700834 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700835 if self.is_fixed_value:
836 return self.constant_name
837 else:
838 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700839
840 @property
841 def default_value(self):
842 java_type = self.java_type.public_type;
843
Andreas Wundsam27303462013-07-16 12:52:35 -0700844 if self.is_fixed_value:
845 return self.enum_value
Andreas Wundsam880a2a82013-08-22 07:55:14 -0700846 elif java_type == "OFOxmList":
847 return "OFOxmList.EMPTY"
Andreas Wundsam7cfeac32013-09-17 13:53:48 -0700848 elif re.match(r'Set.*', java_type):
849 return "Collections.emptySet()"
Andreas Wundsam27303462013-07-16 12:52:35 -0700850 elif re.match(r'List.*', java_type):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700851 return "Collections.emptyList()"
852 elif java_type == "boolean":
853 return "false";
Andreas Wundsam880a2a82013-08-22 07:55:14 -0700854 elif self.java_type.is_array:
855 return "new %s[0]" % java_type[:-2]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700856 elif java_type in ("byte", "char", "short", "int", "long"):
857 return "({0}) 0".format(java_type);
858 else:
859 return "null";
860
861 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700862 def enum_value(self):
863 if self.name == "version":
864 return "OFVersion.%s" % self.msg.version.constant_version
865
866 java_type = self.java_type.public_type;
867 try:
868 global model
869 enum = model.enum_by_name(java_type)
870 entry = enum.entry_by_version_value(self.msg.version, self.value)
871 return "%s.%s" % ( enum.name, entry.name)
872 except KeyError, e:
873 print e.message
874 return self.value
875
876 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700877 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700878 return isinstance(self.member, OFPadMember)
879
880 def is_type_value(self, version=None):
881 if(version==None):
882 return any(self.is_type_value(version) for version in self.msg.all_versions)
883 try:
884 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
885 except:
886 return False
887
888 @property
889 def is_field_length_value(self):
890 return isinstance(self.member, OFFieldLengthMember)
891
892 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700893 def is_discriminator(self):
894 return isinstance(self.member, OFDiscriminatorMember)
895
896 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700897 def is_length_value(self):
898 return isinstance(self.member, OFLengthMember)
899
900 @property
901 def is_public(self):
902 return not (self.is_pad or self.is_length_value)
903
904 @property
905 def is_data(self):
906 return isinstance(self.member, OFDataMember) and self.name != "version"
907
908 @property
909 def is_fixed_value(self):
910 return hasattr(self.member, "value") or self.name == "version" \
911 or ( self.name == "length" and self.msg.is_fixed_length) \
912 or ( self.name == "len" and self.msg.is_fixed_length)
913
914 @property
915 def value(self):
916 if self.name == "version":
917 return self.msg.version.int_version
918 elif self.name == "length" or self.name == "len":
919 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700920 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700921 return self.java_type.format_value(self.member.value)
922
923 @property
924 def priv_value(self):
925 if self.name == "version":
926 return self.msg.version.int_version
927 elif self.name == "length" or self.name == "len":
928 return self.msg.length
929 else:
930 return self.java_type.format_value(self.member.value, pub_type=False)
931
Andreas Wundsam27303462013-07-16 12:52:35 -0700932
933 @property
934 def is_writeable(self):
935 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
936
937 def get_type_value_info(self, version):
938 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700939
940 @property
941 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700942 if hasattr(self.member, "length"):
943 return self.member.length
944 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700945 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700946 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700947
948 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700949 def for_of_member(java_class, member):
950 if isinstance(member, OFPadMember):
951 return JavaMember(None, "", None, member)
952 else:
953 if member.name == 'len':
954 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700955 elif member.name == 'value_mask':
956 name = 'mask'
Andreas Wundsam27303462013-07-16 12:52:35 -0700957 else:
958 name = java_type.name_c_to_camel(member.name)
959 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
960 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700961
962 @property
963 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700964 if not self.msg.c_name in of_g.unified:
965 print("%s not self.unified" % self.msg.c_name)
966 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700967 for version in of_g.unified[self.msg.c_name]:
968 if version == 'union' or version =='object_id':
969 continue
970 if 'use_version' in of_g.unified[self.msg.c_name][version]:
971 continue
972
Andreas Wundsam27303462013-07-16 12:52:35 -0700973 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 -0700974 return False
975 return True
976
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700977 @property
978 def is_virtual(self):
979 return False
980
Andreas Wundsam27303462013-07-16 12:52:35 -0700981 def __hash__(self):
982 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700983
Andreas Wundsam27303462013-07-16 12:52:35 -0700984 def __eq__(self, other):
985 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700986 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700987 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700988
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700989class JavaVirtualMember(JavaMember):
990 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
991 def __init__(self, msg, name, java_type, value=None):
992 JavaMember.__init__(self, msg, name, java_type, member=None)
993 self._value = value
994
995 @property
996 def is_fixed_value(self):
997 return True
998
999 @property
1000 def value(self):
1001 return self._value
1002
1003 @property
1004 def priv_value(self):
1005 return self._value
1006
1007
1008 @property
1009 def is_universal(self):
1010 return True
1011
1012 @property
1013 def is_virtual(self):
1014 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001015
1016#######################################################################
1017### Unit Test
1018#######################################################################
1019
Yotam Harchol466b3212013-08-15 12:14:46 -07001020class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001021 def __init__(self, java_class):
1022 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001023 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001024 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001025 data_file_template = "of{version}/{name}.".format(version=java_class.version.of_version,
1026 name=java_class.c_name[3:]) + "{i}.data"
1027 test_class_name = self.java_class.name + "Test"
1028 self.test_units = []
1029 if test_data.exists(first_data_file_name):
1030 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
1031 i = 1
1032 while test_data.exists(data_file_template.format(i=i)):
1033 self.test_units.append(JavaUnitTest(java_class, data_file_template.format(i=i), test_class_name + str(i)))
1034 i = i + 1
1035
1036 @property
1037 def package(self):
1038 return self.java_class.package
1039
1040 @property
1041 def has_test_data(self):
1042 return len(self.test_units) > 0
1043
1044 @property
1045 def length(self):
1046 return len(self.test_units)
1047
1048 def get_test_unit(self, i):
1049 return self.test_units[i]
1050
1051
1052class JavaUnitTest(object):
1053 def __init__(self, java_class, file_name=None, test_class_name=None):
1054 self.java_class = java_class
1055 if file_name is None:
1056 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1057 name=java_class.c_name[3:])
1058 else:
1059 self.data_file_name = file_name
1060 if test_class_name is None:
1061 self.test_class_name = self.java_class.name + "Test"
1062 else:
1063 self.test_class_name = test_class_name
1064
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001065 @property
1066 def package(self):
1067 return self.java_class.package
1068
1069 @property
1070 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001071 return self.test_class_name
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001072
1073 @property
1074 def has_test_data(self):
1075 return test_data.exists(self.data_file_name)
1076
1077 @property
1078 @memoize
1079 def test_data(self):
1080 return test_data.read(self.data_file_name)
1081
1082
Andreas Wundsam27303462013-07-16 12:52:35 -07001083#######################################################################
1084### Enums
1085#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001086
Andreas Wundsam27303462013-07-16 12:52:35 -07001087class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001088 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001089 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001090
1091 if c_name == "of_stats_types":
1092 self.name = "OFStatsType"
1093 else:
1094 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001095
Andreas Wundsam27303462013-07-16 12:52:35 -07001096 # Port_features has constants that start with digits
1097 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001098
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001099 self.version_enums = version_enum_map
1100
1101 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1102 for version, ir_enum in version_enum_map.items():
1103 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001104 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1105
Andreas Wundsam27303462013-07-16 12:52:35 -07001106 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001107 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001108
1109 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 -07001110 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001111
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001112 self.metadata = model.enum_metadata_map[self.name]
1113
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001114 def wire_type(self, version):
1115 ir_enum = self.version_enums[version]
1116 if "wire_type" in ir_enum.params:
1117 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1118 else:
1119 return java_type.u8
1120
1121 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001122 @memoize
1123 def is_bitmask(self):
1124 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1125
1126 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001127 def versions(self):
1128 return self.version_enums.keys()
1129
Andreas Wundsam27303462013-07-16 12:52:35 -07001130 @memoize
1131 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001132 res = find(lambda e: e.name == name, self.entries)
1133 if res:
1134 return res
1135 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001136 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1137
1138 @memoize
1139 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001140 res = find(lambda e: e.c_name == name, self.entries)
1141 if res:
1142 return res
1143 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001144 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1145
1146 @memoize
1147 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001148 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1149 if res:
1150 return res
1151 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001152 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1153
1154# values: Map JavaVersion->Value
1155class JavaEnumEntry(object):
1156 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001157 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001158 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1159 self.values = values
1160
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001161 @property
1162 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001163 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001164
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001165 def has_value(self, version):
1166 return version in self.values
1167
Andreas Wundsam27303462013-07-16 12:52:35 -07001168 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001169 return self.values[version]
1170
1171 def format_value(self, version):
1172 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001173 return res
1174
Andreas Wundsam27303462013-07-16 12:52:35 -07001175 def all_values(self, versions, not_present=None):
1176 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001177
1178 @property
1179 @memoize
1180 def masked_enum_group(self):
1181 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1182 return group
1183
1184 @property
1185 @memoize
1186 def is_mask(self):
1187 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])