blob: 0d1cc878144b543e5649c747506fc8a03bbfd0c7 [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
Andreas Wundsam27303462013-07-16 12:52:35 -070049class JavaModel(object):
Andreas Wundsamf1949682013-09-23 14:48:31 -070050 # registry for enums that should not be generated
51 # set(${java_enum_name})
Andreas Wundsamacd57d52013-10-18 17:35:01 -070052 enum_blacklist = set(("OFDefinitions", "OFPortNo", "OFVlanId", "OFGroup"))
Andreas Wundsamf1949682013-09-23 14:48:31 -070053 # registry for enum *entry* that should not be generated
54 # map: ${java_enum_name} -> set(${java_entry_entry_name})
Andreas Wundsam43526532013-08-01 22:03:50 -070055 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 -070056
57 # registry of interfaces that should not be generated
58 # set(java_names)
Andreas Wundsambe168f72013-08-03 22:49:35 -070059 # OFUint structs are there for god-knows what in loci. We certainly don't need them.
60 interface_blacklist = set( ("OFUint8", "OFUint32",))
Andreas Wundsamf1949682013-09-23 14:48:31 -070061 # registry of interface properties that should not be generated
62 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -070063 read_blacklist = defaultdict(lambda: set(), OFExperimenter=set(('data','subtype')), OFActionExperimenter=set(('data',)))
Andreas Wundsamf1949682013-09-23 14:48:31 -070064 # map: $java_type -> set(java_name_property)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -070065 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 -070066 # interfaces that are virtual
Andreas Wundsam001b1822013-08-02 22:25:55 -070067 virtual_interfaces = set(['OFOxm', 'OFInstruction', 'OFFlowMod', 'OFBsnVport' ])
Andreas Wundsam27303462013-07-16 12:52:35 -070068
Andreas Wundsam2be7da52013-08-22 07:34:25 -070069 OxmMapEntry = namedtuple("OxmMapEntry", ["type_name", "value", "masked" ])
Yotam Harchole5d92972013-08-22 14:18:36 -070070 oxm_map = { "OFOxmInPort": OxmMapEntry("OFPort", "IN_PORT", False),
71 "OFOxmInPortMasked": OxmMapEntry("OFPort", "IN_PORT", True),
72 "OFOxmInPhyPort": OxmMapEntry("OFPort", "IN_PHY_PORT", False),
73 "OFOxmInPhyPortMasked": OxmMapEntry("OFPort", "IN_PHY_PORT", True),
74 "OFOxmMetadata": OxmMapEntry("OFMetadata", "METADATA", False),
75 "OFOxmMetadataMasked": OxmMapEntry("OFMetadata", "METADATA", True),
76 "OFOxmEthDst": OxmMapEntry("MacAddress", "ETH_DST", False),
77 "OFOxmEthDstMasked": OxmMapEntry("MacAddress", "ETH_DST", True),
78 "OFOxmEthSrc": OxmMapEntry("MacAddress", "ETH_SRC", False),
79 "OFOxmEthSrcMasked": OxmMapEntry("MacAddress", "ETH_SRC", True),
80 "OFOxmEthType": OxmMapEntry("EthType", "ETH_TYPE", False),
81 "OFOxmEthTypeMasked": OxmMapEntry("EthType", "ETH_TYPE", True),
82 "OFOxmVlanVid": OxmMapEntry("VlanVid", "VLAN_VID", False),
83 "OFOxmVlanVidMasked": OxmMapEntry("VlanVid", "VLAN_VID", True),
84 "OFOxmVlanPcp": OxmMapEntry("VlanPcp", "VLAN_PCP", False),
85 "OFOxmVlanPcpMasked": OxmMapEntry("VlanPcp", "VLAN_PCP", True),
86 "OFOxmIpDscp": OxmMapEntry("IpDscp", "IP_DSCP", False),
87 "OFOxmIpDscpMasked": OxmMapEntry("IpDscp", "IP_DSCP", True),
88 "OFOxmIpEcn": OxmMapEntry("IpEcn", "IP_ECN", False),
89 "OFOxmIpEcnMasked": OxmMapEntry("IpEcn", "IP_ECN", True),
90 "OFOxmIpProto": OxmMapEntry("IpProtocol", "IP_PROTO", False),
91 "OFOxmIpProtoMasked": OxmMapEntry("IpProtocol", "IP_PROTO", True),
Yotam Harchola289d552013-09-16 10:10:40 -070092 "OFOxmIpv4Src": OxmMapEntry("IPv4Address", "IPV4_SRC", False),
93 "OFOxmIpv4SrcMasked": OxmMapEntry("IPv4Address", "IPV4_SRC", True),
94 "OFOxmIpv4Dst": OxmMapEntry("IPv4Address", "IPV4_DST", False),
95 "OFOxmIpv4DstMasked": OxmMapEntry("IPv4Address", "IPV4_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -070096 "OFOxmTcpSrc": OxmMapEntry("TransportPort", "TCP_SRC", False),
97 "OFOxmTcpSrcMasked": OxmMapEntry("TransportPort", "TCP_SRC", True),
98 "OFOxmTcpDst": OxmMapEntry("TransportPort", "TCP_DST", False),
99 "OFOxmTcpDstMasked": OxmMapEntry("TransportPort", "TCP_DST", True),
100 "OFOxmUdpSrc": OxmMapEntry("TransportPort", "UDP_SRC", False),
101 "OFOxmUdpSrcMasked": OxmMapEntry("TransportPort", "UDP_SRC", True),
102 "OFOxmUdpDst": OxmMapEntry("TransportPort", "UDP_DST", False),
103 "OFOxmUdpDstMasked": OxmMapEntry("TransportPort", "UDP_DST", True),
104 "OFOxmSctpSrc": OxmMapEntry("TransportPort", "SCTP_SRC", False),
105 "OFOxmSctpSrcMasked": OxmMapEntry("TransportPort", "SCTP_SRC", True),
106 "OFOxmSctpDst": OxmMapEntry("TransportPort", "SCTP_DST", False),
107 "OFOxmSctpDstMasked": OxmMapEntry("TransportPort", "SCTP_DST", True),
108 "OFOxmIcmpv4Type": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", False),
109 "OFOxmIcmpv4TypeMasked": OxmMapEntry("ICMPv4Type", "ICMPV4_TYPE", True),
110 "OFOxmIcmpv4Code": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", False),
111 "OFOxmIcmpv4CodeMasked": OxmMapEntry("ICMPv4Code", "ICMPV4_CODE", True),
112 "OFOxmArpOp": OxmMapEntry("ArpOpcode", "ARP_OP", False),
113 "OFOxmArpOpMasked": OxmMapEntry("ArpOpcode", "ARP_OP", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700114 "OFOxmArpSpa": OxmMapEntry("IPv4Address", "ARP_SPA", False),
115 "OFOxmArpSpaMasked": OxmMapEntry("IPv4Address", "ARP_SPA", True),
116 "OFOxmArpTpa": OxmMapEntry("IPv4Address", "ARP_TPA", False),
117 "OFOxmArpTpaMasked": OxmMapEntry("IPv4Address", "ARP_TPA", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700118 "OFOxmArpSha": OxmMapEntry("MacAddress", "ARP_SHA", False),
119 "OFOxmArpShaMasked": OxmMapEntry("MacAddress", "ARP_SHA", True),
120 "OFOxmArpTha": OxmMapEntry("MacAddress", "ARP_THA", False),
121 "OFOxmArpThaMasked": OxmMapEntry("MacAddress", "ARP_THA", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700122 "OFOxmIpv6Src": OxmMapEntry("IPv6Address", "IPV6_SRC", False),
123 "OFOxmIpv6SrcMasked": OxmMapEntry("IPv6Address", "IPV6_SRC", True),
124 "OFOxmIpv6Dst": OxmMapEntry("IPv6Address", "IPV6_DST", False),
125 "OFOxmIpv6DstMasked": OxmMapEntry("IPv6Address", "IPV6_DST", True),
Yotam Harchole5d92972013-08-22 14:18:36 -0700126 "OFOxmIpv6Flabel": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", False),
Yotam Harchola86e4252013-09-06 15:36:28 -0700127 "OFOxmIpv6FlabelMasked": OxmMapEntry("IPv6FlowLabel", "IPV6_FLABEL", True),
128 "OFOxmIcmpv6Type": OxmMapEntry("U8", "ICMPV6_TYPE", False),
129 "OFOxmIcmpv6TypeMasked": OxmMapEntry("U8", "ICMPV6_TYPE", True),
130 "OFOxmIcmpv6Code": OxmMapEntry("U8", "ICMPV6_CODE", False),
131 "OFOxmIcmpv6CodeMasked": OxmMapEntry("U8", "ICMPV6_CODE", True),
Yotam Harchola289d552013-09-16 10:10:40 -0700132 "OFOxmIpv6NdTarget": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", False),
133 "OFOxmIpv6NdTargetMasked": OxmMapEntry("IPv6Address", "IPV6_ND_TARGET", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700134 "OFOxmIpv6NdSll": OxmMapEntry("MacAddress", "IPV6_ND_SLL", False),
135 "OFOxmIpv6NdSllMasked": OxmMapEntry("MacAddress", "IPV6_ND_SLL", True),
136 "OFOxmIpv6NdTll": OxmMapEntry("MacAddress", "IPV6_ND_TLL", False),
137 "OFOxmIpv6NdTllMasked": OxmMapEntry("MacAddress", "IPV6_ND_TLL", True),
138 "OFOxmMplsLabel": OxmMapEntry("U32", "MPLS_LABEL", False),
139 "OFOxmMplsLabelMasked": OxmMapEntry("U32", "MPLS_LABEL", True),
140 "OFOxmMplsTc": OxmMapEntry("U8", "MPLS_TC", False),
Yotam Harchola11f38b2013-09-26 15:38:17 -0700141 "OFOxmMplsTcMasked": OxmMapEntry("U8", "MPLS_TC", True),
Yotam Harchol2c535582013-10-01 15:50:20 -0700142 "OFOxmBsnInPorts128": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", False),
Rich Lane1424d0b2013-10-24 17:16:24 -0700143 "OFOxmBsnInPorts128Masked": OxmMapEntry("OFBitMask128", "BSN_IN_PORTS_128", True),
Rich Lane376cafe2013-10-27 21:49:14 -0700144 "OFOxmBsnLagId": OxmMapEntry("LagId", "BSN_LAG_ID", False),
145 "OFOxmBsnLagIdMasked": OxmMapEntry("LagId", "BSN_LAG_ID", True),
Rich Laneeb21c4f2013-10-28 17:34:41 -0700146 "OFOxmBsnVrf": OxmMapEntry("VRF", "BSN_VRF", False),
147 "OFOxmBsnVrfMasked": OxmMapEntry("VRF", "BSN_VRF", True),
Rich Lane9c27b5d2013-10-30 17:14:32 -0700148 "OFOxmBsnGlobalVrfAllowed": OxmMapEntry("OFBooleanValue", "BSN_GLOBAL_VRF_ALLOWED", False),
149 "OFOxmBsnGlobalVrfAllowedMasked": OxmMapEntry("OFBooleanValue", "BSN_GLOBAL_VRF_ALLOWED", True),
Rich Laneeb21c4f2013-10-28 17:34:41 -0700150 "OFOxmBsnL3InterfaceClassId": OxmMapEntry("ClassId", "BSN_L3_INTERFACE_CLASS_ID", False),
151 "OFOxmBsnL3InterfaceClassIdMasked": OxmMapEntry("ClassId", "BSN_L3_INTERFACE_CLASS_ID", True),
152 "OFOxmBsnL3SrcClassId": OxmMapEntry("ClassId", "BSN_L3_SRC_CLASS_ID", False),
153 "OFOxmBsnL3SrcClassIdMasked": OxmMapEntry("ClassId", "BSN_L3_SRC_CLASS_ID", True),
154 "OFOxmBsnL3DstClassId": OxmMapEntry("ClassId", "BSN_L3_DST_CLASS_ID", False),
155 "OFOxmBsnL3DstClassIdMasked": OxmMapEntry("ClassId", "BSN_L3_DST_CLASS_ID", True),
Yotam Harchola86e4252013-09-06 15:36:28 -0700156 }
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700157
Andreas Wundsamf1949682013-09-23 14:48:31 -0700158 # Registry of nullable properties:
159 # ${java_class_name} -> set(${java_property_name})
160 nullable_map = defaultdict(lambda: set(),
161 )
162
163 # represents a subgroup of a bitmask enum that is actualy a normal enumerable within a masked part of the enum
164 # e.g., the flags STP.* in OF1.0 port state are bit mask entries, but instead enumerables according to the mask "STP_MASK"
165 # name: a name for the group
166 # mask: java name of the enum entry that defines the mask
167 # members: set of names of the members of the group
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700168 MaskedEnumGroup = namedtuple("MaskedEnumGroup", ("name", "mask", "members"))
169
Andreas Wundsamf1949682013-09-23 14:48:31 -0700170 # registry of MaskedEnumGroups (see above).
171 # map: ${java_enum_name}: tuple(MaskedEnumGroup)
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700172 masked_enum_groups = defaultdict(lambda: (),
Andreas Wundsamaebe5182013-09-24 13:01:07 -0700173 OFPortState = (MaskedEnumGroup("stp_flags", mask="STP_MASK", members=set(("STP_LISTEN", "STP_LEARN", "STP_FORWARD", "STP_BLOCK"))), ),
174 OFConfigFlags = (
175 MaskedEnumGroup("frag_flags", mask="FRAG_MASK", members=set(("FRAG_NORMAL", "FRAG_DROP", "FRAG_REASM"))),
Andreas Wundsam376cef52013-09-24 13:52:18 -0700176 ),
177 OFTableConfig = (
178 MaskedEnumGroup("table_miss_flags", mask="TABLE_MISS_MASK", members=set(("TABLE_MISS_CONTROLLER", "TABLE_MISS_CONTINUE", "TABLE_MISS_DROP"))),
179 ),
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -0700180 )
181
Andreas Wundsamf1949682013-09-23 14:48:31 -0700182 # represents a metadata property associated with an EnumClass
183 # name:
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700184 class OFEnumPropertyMetadata(namedtuple("OFEnumPropertyMetadata", ("name", "type", "value"))):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700185 """
186 represents a metadata property associated with an Enum Class
187 @param name name of metadata property
188 @param type java_type instance describing the type
189 @value: Generator function f(entry) that generates the value
190 """
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700191 @property
192 def variable_name(self):
193 return self.name[0].lower() + self.name[1:]
194
195 @property
196 def getter_name(self):
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700197 prefix = "is" if self.type == java_type.boolean else "get"
198 return prefix+self.name
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700199
Andreas Wundsamf1949682013-09-23 14:48:31 -0700200 """ Metadata container. """
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700201 OFEnumMetadata = namedtuple("OFEnumMetadata", ("properties", "to_string"))
202
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700203 def gen_port_speed(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700204 """ Generator function for OFortFeatures.PortSpeed"""
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700205 splits = enum_entry.name.split("_")
206 if len(splits)>=2:
207 m = re.match(r'\d+[MGTP]B', splits[1])
208 if m:
209 return "PortSpeed.SPEED_{}".format(splits[1])
210 return "PortSpeed.SPEED_NONE";
211
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700212 def gen_stp_state(enum_entry):
Andreas Wundsamf1949682013-09-23 14:48:31 -0700213 """ Generator function for OFPortState.StpState"""
Rob Vaterlausbda9ce62013-09-17 14:45:48 -0700214 splits = enum_entry.name.split("_")
215 if len(splits)>=1:
216 if splits[0] == "STP":
217 return "true"
218 return "false"
219
Andreas Wundsamf1949682013-09-23 14:48:31 -0700220 # registry for metadata properties for enums
221 # map: ${java_enum_name}: OFEnumMetadata
Rob Vaterlaus6035bf52013-09-17 19:32:38 -0700222 enum_metadata_map = defaultdict(lambda: JavaModel.OFEnumMetadata((), None),
Rob Vaterlausa049dee2013-09-18 16:18:37 -0700223 OFPortFeatures = OFEnumMetadata((OFEnumPropertyMetadata("PortSpeed", java_type.port_speed, gen_port_speed),), None),
224 OFPortState = OFEnumMetadata((OFEnumPropertyMetadata("StpState", java_type.boolean, gen_stp_state),), None),
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -0700225 )
226
Andreas Wundsam27303462013-07-16 12:52:35 -0700227 @property
228 @memoize
229 def versions(self):
230 return OrderedSet( JavaOFVersion(raw_version) for raw_version in of_g.target_version_list )
231
232 @property
233 @memoize
234 def interfaces(self):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700235 version_map_per_class = collections.OrderedDict()
Andreas Wundsam27303462013-07-16 12:52:35 -0700236
237 for raw_version, of_protocol in of_g.ir.items():
238 jversion = JavaOFVersion(of_protocol.wire_version)
239
240 for of_class in of_protocol.classes:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700241 if not of_class.name in version_map_per_class:
242 version_map_per_class[of_class.name] = collections.OrderedDict()
243
Andreas Wundsam27303462013-07-16 12:52:35 -0700244 version_map_per_class[of_class.name][jversion] = of_class
245
246 interfaces = []
247 for class_name, version_map in version_map_per_class.items():
248 interfaces.append(JavaOFInterface(class_name, version_map))
249
Andreas Wundsambe168f72013-08-03 22:49:35 -0700250 interfaces = [ i for i in interfaces if i.name not in self.interface_blacklist ]
251
Andreas Wundsam27303462013-07-16 12:52:35 -0700252 return interfaces
253
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700254 @memoize
255 def interface_by_name(self, name):
256 return find(lambda i: erase_type_annotation(i.name) == erase_type_annotation(name), self.interfaces)
257
Andreas Wundsam27303462013-07-16 12:52:35 -0700258 @property
259 @memoize
Andreas Wundsam001b1822013-08-02 22:25:55 -0700260 def all_classes(self):
261 return [clazz for interface in self.interfaces for clazz in interface.versioned_classes]
262
263 @property
264 @memoize
Andreas Wundsam27303462013-07-16 12:52:35 -0700265 def enums(self):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700266 name_version_enum_map = OrderedDefaultDict(lambda: OrderedDict())
Andreas Wundsam27303462013-07-16 12:52:35 -0700267
268 for version in self.versions:
269 of_protocol = of_g.ir[version.int_version]
270 for enum in of_protocol.enums:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700271 name_version_enum_map[enum.name][version] = enum
Andreas Wundsam27303462013-07-16 12:52:35 -0700272
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700273 enums = [ JavaEnum(name, version_enum_map) for name, version_enum_map,
274 in name_version_enum_map.items() ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700275
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -0700276 # inelegant - need java name here
277 enums = [ enum for enum in enums if enum.name not in self.enum_blacklist ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700278 return enums
279
280 @memoize
281 def enum_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700282 res = find(lambda e: e.name == name, self.enums)
283 if not res:
Andreas Wundsam27303462013-07-16 12:52:35 -0700284 raise KeyError("Could not find enum with name %s" % name)
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700285 return res
Andreas Wundsam27303462013-07-16 12:52:35 -0700286
Andreas Wundsam5204de22013-07-30 11:34:45 -0700287 @property
288 @memoize
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700289 def of_factories(self):
Yotam Harchol791e4882013-09-05 16:32:56 -0700290 prefix = "org.projectfloodlight.openflow.protocol"
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700291
292 factories = OrderedDict()
293
294 sub_factory_classes = ("OFAction", "OFInstruction", "OFMeterBand", "OFOxm", "OFQueueProp")
295 for base_class in sub_factory_classes:
296 package = base_class[2:].lower()
297 remove_prefix = base_class[2].lower() + base_class[3:]
298
299 # HACK need to have a better way to deal with parameterized base classes
300 annotated_base_class = base_class + "<?>" if base_class == "OFOxm" else base_class
301
302 factories[base_class] = OFFactory(package="%s.%s" % (prefix, package),
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700303 name=base_class + "s", members=[], remove_prefix=remove_prefix, base_class=annotated_base_class, sub_factories={}, xid_generator=False)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700304
305 factories[""] = OFFactory(
306 package=prefix,
Andreas Wundsam5204de22013-07-30 11:34:45 -0700307 name="OFFactory",
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700308 remove_prefix="",
309 members=[], base_class="OFMessage", sub_factories=OrderedDict(
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700310 ("{}{}s".format(n[2].lower(), n[3:]), "{}s".format(n)) for n in sub_factory_classes ),
311 xid_generator=True)
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700312
313 for i in self.interfaces:
314 for n, factory in factories.items():
315 if n == "":
316 factory.members.append(i)
317 break
318 else:
319 super_class = self.interface_by_name(n)
320 if i.is_instance_of(super_class):
321 factory.members.append(i)
322 break
323 return factories.values()
Yotam Harchol595c6442013-09-27 16:29:08 -0700324
325 @memoize
326 def factory_of(self, interface):
327 for factory in self.of_factories:
328 if interface in factory.members:
329 return factory
330 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700331
332 def generate_class(self, clazz):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700333 """ return wether or not to generate implementation class clazz.
334 Now true for everything except OFTableModVer10.
335 @param clazz JavaOFClass instance
336 """
Andreas Wundsam43526532013-08-01 22:03:50 -0700337 if clazz.interface.name.startswith("OFMatchV"):
338 return True
Andreas Wundsam001b1822013-08-02 22:25:55 -0700339 elif clazz.name == "OFTableModVer10":
340 # tablemod ver 10 is a hack and has no oftype defined
341 return False
Andreas Wundsam5204de22013-07-30 11:34:45 -0700342 if loxi_utils.class_is_message(clazz.interface.c_name):
343 return True
344 if loxi_utils.class_is_oxm(clazz.interface.c_name):
345 return True
Andreas Wundsam43526532013-08-01 22:03:50 -0700346 if loxi_utils.class_is_action(clazz.interface.c_name):
347 return True
348 if loxi_utils.class_is_instruction(clazz.interface.c_name):
349 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700350 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700351 return True
Andreas Wundsam5204de22013-07-30 11:34:45 -0700352
353
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700354class OFFactory(namedtuple("OFFactory", ("package", "name", "members", "remove_prefix", "base_class", "sub_factories", "xid_generator"))):
Andreas Wundsam5204de22013-07-30 11:34:45 -0700355 @property
356 def factory_classes(self):
357 return [ OFFactoryClass(
Yotam Harchol791e4882013-09-05 16:32:56 -0700358 package="org.projectfloodlight.openflow.protocol.ver{}".format(version.of_version),
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700359 name="{}Ver{}".format(self.name, version.of_version),
Andreas Wundsam5204de22013-07-30 11:34:45 -0700360 interface=self,
361 version=version
362 ) for version in model.versions ]
363
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700364 def method_name(self, member, builder=True):
365 n = member.variable_name
366 if n.startswith(self.remove_prefix):
367 n = n[len(self.remove_prefix):]
368 n = n[0].lower() + n[1:]
369 if builder:
370 return "build" + n[0].upper() + n[1:]
371 else:
372 return n
Yotam Harchol595c6442013-09-27 16:29:08 -0700373
374 def of_version(self, version):
375 for fc in self.factory_classes:
376 if fc.version == version:
377 return fc
378 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700379
380OFGenericClass = namedtuple("OFGenericClass", ("package", "name"))
Andreas Wundsame0d52be2013-08-22 07:52:13 -0700381class OFFactoryClass(namedtuple("OFFactoryClass", ("package", "name", "interface", "version"))):
382 @property
383 def base_class(self):
384 return self.interface.base_class
385
386 @property
387 def versioned_base_class(self):
388 base_class_interface = model.interface_by_name(self.interface.base_class)
389 if base_class_interface and base_class_interface.has_version(self.version):
390 return base_class_interface.versioned_class(self.version)
391 else:
392 return None
Andreas Wundsam5204de22013-07-30 11:34:45 -0700393
Andreas Wundsam27303462013-07-16 12:52:35 -0700394model = JavaModel()
395
396#######################################################################
397### OFVersion
398#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700399
400class JavaOFVersion(object):
401 """ Models a version of OpenFlow. contains methods to convert the internal
402 Loxi version to a java constant / a string """
403 def __init__(self, int_version):
404 self.int_version = int(int_version)
405
406 @property
407 def of_version(self):
408 return "1" + str(int(self.int_version) - 1)
409
410 @property
411 def constant_version(self):
412 return "OF_" + self.of_version
413
Andreas Wundsam27303462013-07-16 12:52:35 -0700414 def __repr__(self):
415 return "JavaOFVersion(%d)" % self.int_version
416
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700417 def __str__(self):
418 return of_g.param_version_names[self.int_version]
419
Andreas Wundsam27303462013-07-16 12:52:35 -0700420 def __hash__(self):
421 return hash(self.int_version)
422
423 def __eq__(self, other):
424 if other is None or type(self) != type(other):
425 return False
426 return (self.int_version,) == (other.int_version,)
427
428#######################################################################
429### Interface
430#######################################################################
431
432class JavaOFInterface(object):
433 """ Models an OpenFlow Message class for the purpose of the java class.
434 Version agnostic, in contrast to the loxi_ir python model.
435 """
436 def __init__(self, c_name, version_map):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700437 """"
438 @param c_name: loxi style name (e.g., of_flow_add)
439 @param version_map map of { JavaOFVersion: OFClass (from loxi_ir) }
440 """
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700441 self.c_name = c_name
Andreas Wundsam27303462013-07-16 12:52:35 -0700442 self.version_map = version_map
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700443 # name: the Java Type name, e.g., OFFlowAdd
Andreas Wundsam001b1822013-08-02 22:25:55 -0700444 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 -0700445 # variable_name name to use for variables of this type. i.e., flowAdd
Andreas Wundsam5204de22013-07-30 11:34:45 -0700446 self.variable_name = self.name[2].lower() + self.name[3:]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700447 self.title_name = self.variable_name[0].upper() + self.variable_name[1:]
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700448 # name for use in constants: FLOW_ADD
Andreas Wundsam27303462013-07-16 12:52:35 -0700449 self.constant_name = c_name.upper().replace("OF_", "")
450
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700451 pck_suffix, parent_interface, self.type_annotation = self.class_info()
Yotam Harchol791e4882013-09-05 16:32:56 -0700452 self.package = "org.projectfloodlight.openflow.protocol.%s" % pck_suffix if pck_suffix else "org.projectfloodlight.openflow.protocol"
Andreas Wundsam27303462013-07-16 12:52:35 -0700453 if self.name != parent_interface:
454 self.parent_interface = parent_interface
455 else:
456 self.parent_interface = None
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700457
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700458 @property
459 @memoize
460 def all_parent_interfaces(self):
461 return [ "OFObject" ] + \
462 ([ self.parent_interface ] if self.parent_interface else [] )+ \
463 self.additional_parent_interfaces
464 @property
465 @memoize
466 def additional_parent_interfaces(self):
467 if loxi_utils.class_is_message(self.c_name) and not self.is_virtual:
468 m = re.match(r'(.*)Request$', self.name)
469 if m:
470 reply_name = m.group(1) + "Reply"
471 if model.interface_by_name(reply_name):
472 return ["OFRequest<%s>" % reply_name ]
473 return []
474
475
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700476 def is_instance_of(self, other_class):
477 if self == other_class:
478 return True
479 parent = self.super_class
480 if parent is None:
481 return False
482 else:
483 return parent.is_instance_of(other_class)
484
485 @property
486 def super_class(self):
487 if not self.parent_interface:
488 return None
489 else:
490 return model.interface_by_name(self.parent_interface)
491
492
493 def inherited_declaration(self, type_spec="?"):
494 if self.type_annotation:
495 return "%s<%s>" % (self.name, type_spec)
496 else:
497 return "%s" % self.name
498
499 @property
500 def type_variable(self):
501 if self.type_annotation:
502 return "<T>"
503 else:
504 return "";
505
Andreas Wundsam27303462013-07-16 12:52:35 -0700506 def class_info(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700507 """ return tuple of (package_prefix, parent_class) for the current JavaOFInterface"""
508 # FIXME: This duplicates inheritance information that is now available in the loxi_ir
509 # model (note, that the loxi model is on versioned classes). Should check/infer the
510 # inheritance information from the versioned lox_ir classes.
Andreas Wundsamd6b0cb02013-09-28 18:55:56 -0700511 if re.match(r'OFStatsRequest$', self.name):
512 return ("", "OFMessage", "T extends OFStatsReply")
513 elif re.match(r'OF.+StatsRequest$', self.name):
514 return ("", "OFStatsRequest<{}>".format(re.sub(r'Request$', 'Reply', self.name)), None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700515 elif re.match(r'OF.+StatsReply$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700516 return ("", "OFStatsReply", None)
Rob Vaterlausfeee3712013-09-30 11:24:19 -0700517 elif re.match(r'OF.+ErrorMsg$', self.name):
518 return ("", "OFErrorMsg", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700519 elif re.match(r'OFFlow(Add|Modify(Strict)?|Delete(Strict)?)$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700520 return ("", "OFFlowMod", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700521 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 -0700522 return ("", "OFBsnHeader", None)
Andreas Wundsama705c6b2013-09-04 11:21:09 -0700523 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 -0700524 return ("", "OFNiciraHeader", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700525 elif self.name == "OFBsnHeader" or self.name =="OFNiciraHeader":
526 return ("", "OFExperimenter", None)
Andreas Wundsam43526532013-08-01 22:03:50 -0700527 elif re.match(r'OFMatch.*', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700528 return ("", "Match", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700529 elif loxi_utils.class_is_message(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700530 return ("", "OFMessage", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700531 elif loxi_utils.class_is_action(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700532 if re.match(r'OFActionBsn.+', self.name):
533 return ("action", "OFActionBsn", None)
534 elif re.match(r'OFActionNicira.+', self.name):
535 return ("action", "OFActionNicira", None)
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700536 elif self.name == "OFActionBsn" or self.name == "OFActionNicira":
537 return ("action", "OFActionExperimenter", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700538 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700539 return ("action", "OFAction", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700540 elif re.match(r'OFBsnVport.+$', self.name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700541 return ("", "OFBsnVport", None)
542 elif self.name == "OFOxm":
543 return ("oxm", None, "T extends OFValueType<T>")
Andreas Wundsam5204de22013-07-30 11:34:45 -0700544 elif loxi_utils.class_is_oxm(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700545 if self.name in model.oxm_map:
546 return ("oxm", "OFOxm<%s>" % model.oxm_map[self.name].type_name, None)
547 else:
548 return ("oxm", "OFOxm", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700549 elif loxi_utils.class_is_instruction(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700550 return ("instruction", "OFInstruction", None)
Andreas Wundsam5204de22013-07-30 11:34:45 -0700551 elif loxi_utils.class_is_meter_band(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700552 return ("meterband", "OFMeterBand", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700553 elif loxi_utils.class_is_queue_prop(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700554 return ("queueprop", "OFQueueProp", None)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700555 elif loxi_utils.class_is_hello_elem(self.c_name):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700556 return ("", "OFHelloElem", None)
Rich Lane26847562013-10-08 13:44:47 -0700557 elif loxi_utils.class_is_table_feature_prop(self.c_name):
558 return ("", "OFTableFeatureProp", None)
Andreas Wundsam27303462013-07-16 12:52:35 -0700559 else:
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700560 return ("", None, None)
561
562 @property
563 @memoize
564 def writeable_members(self):
565 return [ m for m in self.members if m.is_writeable ]
Andreas Wundsam27303462013-07-16 12:52:35 -0700566
Andreas Wundsamcb17b232013-09-28 19:05:36 -0700567 @memoize
568 def member_by_name(self, name):
569 return find(lambda m: m.name == name, self.members)
570
Andreas Wundsam27303462013-07-16 12:52:35 -0700571 @property
572 @memoize
573 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700574 return self.ir_model_members + self.virtual_members
575
576 @property
577 @memoize
578 def ir_model_members(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700579 """return a list of all members to be exposed by this interface. Corresponds to
580 the union of the members of the vesioned classes without length, fieldlength
581 and pads (those are handled automatically during (de)serialization and not exposed"""
Andreas Wundsam27303462013-07-16 12:52:35 -0700582 all_versions = []
583 member_map = collections.OrderedDict()
584
585 for (version, of_class) in self.version_map.items():
586 for of_member in of_class.members:
587 if isinstance(of_member, OFLengthMember) or \
588 isinstance(of_member, OFFieldLengthMember) or \
589 isinstance(of_member, OFPadMember):
590 continue
591 if of_member.name not in member_map:
592 member_map[of_member.name] = JavaMember.for_of_member(self, of_member)
593
Andreas Wundsamb1da3f62013-09-04 18:46:03 -0700594 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 -0700595
596 @property
597 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700598 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700599 if self.name == "OFOxm":
Andreas Wundsama0981022013-10-02 18:15:06 -0700600 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700601 JavaVirtualMember(self, "value", java_type.generic_t),
602 JavaVirtualMember(self, "mask", java_type.generic_t),
603 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype("T")),
604 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800605 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype("T"))
Andreas Wundsama0981022013-10-02 18:15:06 -0700606 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700607 elif self.parent_interface and self.parent_interface.startswith("OFOxm"):
608 field_type = java_type.make_match_field_jtype(model.oxm_map[self.name].type_name) \
609 if self.name in model.oxm_map \
610 else java_type.make_match_field_jtype()
611
Andreas Wundsama0981022013-10-02 18:15:06 -0700612 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700613 JavaVirtualMember(self, "matchField", field_type),
614 JavaVirtualMember(self, "masked", java_type.boolean),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800615 JavaVirtualMember(self, "canonical", java_type.make_oxm_jtype(model.oxm_map[self.name].type_name),
616 custom_template=lambda builder: "OFOxm{}_getCanonical.java".format(".Builder" if builder else "")),
Andreas Wundsama0981022013-10-02 18:15:06 -0700617 ]
618 if not find(lambda x: x.name == "mask", self.ir_model_members):
619 virtual_members.append(JavaVirtualMember(self, "mask", find(lambda x: x.name == "value", self.ir_model_members).java_type))
620
621 if not find(lambda m: m.name == "version", self.ir_model_members):
622 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version))
623
624 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700625
626 @property
627 @memoize
628 def is_virtual(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700629 """ Is this interface virtual. If so, do not generate a builder interface """
Andreas Wundsam001b1822013-08-02 22:25:55 -0700630 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 -0700631
632 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700633 def is_universal(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700634 """ Is this interface universal, i.e., does it exist in all OF versions? """
Andreas Wundsam5204de22013-07-30 11:34:45 -0700635 return len(self.all_versions) == len(model.versions)
636
637 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700638 @memoize
639 def all_versions(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700640 """ return list of all versions that this interface exists in """
Andreas Wundsam27303462013-07-16 12:52:35 -0700641 return self.version_map.keys()
642
Andreas Wundsam5204de22013-07-30 11:34:45 -0700643 def has_version(self, version):
644 return version in self.version_map
645
Andreas Wundsam27303462013-07-16 12:52:35 -0700646 def versioned_class(self, version):
647 return JavaOFClass(self, version, self.version_map[version])
648
649 @property
650 @memoize
651 def versioned_classes(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700652 return [ self.versioned_class(version) for version in self.all_versions ]
653
654#######################################################################
655### (Versioned) Classes
656#######################################################################
657
658class JavaOFClass(object):
659 """ Models an OpenFlow Message class for the purpose of the java class.
Andreas Wundsamd40ddbf2013-07-25 15:40:47 -0700660 Version specific child of a JavaOFInterface
Andreas Wundsam27303462013-07-16 12:52:35 -0700661 """
662 def __init__(self, interface, version, ir_class):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700663 """
664 @param interface JavaOFInterface instance of the parent interface
665 @param version JavaOFVersion
666 @param ir_class OFClass from loxi_ir
667 """
Andreas Wundsam27303462013-07-16 12:52:35 -0700668 self.interface = interface
669 self.ir_class = ir_class
670 self.c_name = self.ir_class.name
671 self.version = version
672 self.constant_name = self.c_name.upper().replace("OF_", "")
Yotam Harchol791e4882013-09-05 16:32:56 -0700673 self.package = "org.projectfloodlight.openflow.protocol.ver%s" % version.of_version
Andreas Wundsame916d6f2013-07-30 11:33:58 -0700674 self.generated = False
675
676 @property
677 @memoize
678 def unit_test(self):
Yotam Harchol466b3212013-08-15 12:14:46 -0700679 return JavaUnitTestSet(self)
Andreas Wundsam27303462013-07-16 12:52:35 -0700680
681 @property
682 def name(self):
683 return "%sVer%s" % (self.interface.name, self.version.of_version)
684
685 @property
Andreas Wundsam5204de22013-07-30 11:34:45 -0700686 def variable_name(self):
Andreas Wundsam781d99f2013-10-23 15:25:56 -0700687 return self.name[2].lower() + self.name[3:]
Andreas Wundsam5204de22013-07-30 11:34:45 -0700688
689 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700690 def length(self):
691 if self.is_fixed_length:
692 return self.min_length
693 else:
694 raise Exception("No fixed length for class %s, version %s" % (self.name, self.version))
695
696 @property
697 def min_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700698 """ @return the minimum wire length of an instance of this class in bytes """
Andreas Wundsam27303462013-07-16 12:52:35 -0700699 id_tuple = (self.ir_class.name, self.version.int_version)
700 return of_g.base_length[id_tuple] if id_tuple in of_g.base_length else -1
701
702 @property
703 def is_fixed_length(self):
Andreas Wundsamd8bcedf2013-08-03 21:23:37 -0700704 """ true iff this class serializes to a fixed length on the wire """
Andreas Wundsambbf85e12013-09-13 14:18:01 -0700705 return (self.ir_class.name, self.version.int_version) in of_g.is_fixed_length and \
706 not self.is_virtual
Andreas Wundsam27303462013-07-16 12:52:35 -0700707
708 def all_properties(self):
709 return self.interface.members
710
711 def get_member(self, name):
712 for m in self.members:
713 if m.name == name:
714 return m
715
716 @property
717 @memoize
718 def data_members(self):
719 return [ prop for prop in self.members if prop.is_data ]
720
721 @property
722 @memoize
723 def fixed_value_members(self):
724 return [ prop for prop in self.members if prop.is_fixed_value ]
725
726 @property
727 @memoize
728 def public_members(self):
729 return [ prop for prop in self.members if prop.is_public ]
730
731 @property
732 @memoize
733 def members(self):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700734 return self.ir_model_members + self.virtual_members
735
736 @property
Andreas Wundsama0981022013-10-02 18:15:06 -0700737 @memoize
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700738 def ir_model_members(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700739 members = [ JavaMember.for_of_member(self, of_member) for of_member in self.ir_class.members ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700740 return tuple(members)
741
742 @property
743 def virtual_members(self):
Andreas Wundsama0981022013-10-02 18:15:06 -0700744 virtual_members = []
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700745 if self.interface.parent_interface and self.interface.parent_interface.startswith("OFOxm"):
746 if self.interface.name in model.oxm_map:
747 oxm_entry = model.oxm_map[self.interface.name]
Andreas Wundsama0981022013-10-02 18:15:06 -0700748 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700749 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(oxm_entry.type_name), "MatchField.%s" % oxm_entry.value),
750 JavaVirtualMember(self, "masked", java_type.boolean, "true" if oxm_entry.masked else "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800751 ]
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700752 else:
Andreas Wundsama0981022013-10-02 18:15:06 -0700753 virtual_members += [
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700754 JavaVirtualMember(self, "matchField", java_type.make_match_field_jtype(), "null"),
755 JavaVirtualMember(self, "masked", java_type.boolean, "false"),
Andreas Wundsam661a2222013-11-05 17:18:59 -0800756 ]
Andreas Wundsama0981022013-10-02 18:15:06 -0700757 if not find(lambda m: m.name == "version", self.ir_model_members):
758 virtual_members.append(JavaVirtualMember(self, "version", java_type.of_version, "OFVersion.%s" % self.version.constant_version))
759
760 return tuple(virtual_members)
Andreas Wundsam27303462013-07-16 12:52:35 -0700761
Andreas Wundsam661a2222013-11-05 17:18:59 -0800762 @memoize
763 def member_by_name(self, name):
764 return find(lambda m: m.name == name, self.members)
765
Andreas Wundsam27303462013-07-16 12:52:35 -0700766 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
Andreas Wundsam5da68512013-10-22 22:18:00 -0700794 def length_includes_align(self):
795 return self.ir_class.params['length_includes_align'] == "True" if 'length_includes_align' in self.ir_class.params else False
796
797 @property
Andreas Wundsam1f0d8802013-08-02 22:20:14 -0700798 @memoize
799 def superclass(self):
800 return find(lambda c: c.version == self.version and c.c_name == self.ir_class.superclass, model.all_classes)
801
802 @property
803 @memoize
804 def subclasses(self):
805 return [ c for c in model.all_classes if c.version == self.version and c.ir_class.superclass == self.c_name ]
806
Andreas Wundsam27303462013-07-16 12:52:35 -0700807#######################################################################
808### Member
809#######################################################################
810
811
812class JavaMember(object):
813 """ Models a property (member) of an openflow class. """
814 def __init__(self, msg, name, java_type, member):
815 self.msg = msg
816 self.name = name
817 self.java_type = java_type
818 self.member = member
819 self.c_name = self.member.name if(hasattr(self.member, "name")) else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700820
821 @property
822 def title_name(self):
823 return self.name[0].upper() + self.name[1:]
824
825 @property
826 def constant_name(self):
827 return self.c_name.upper()
828
829 @property
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700830 def getter_name(self):
831 return ("is" if self.java_type.public_type == "boolean" else "get") + self.title_name
832
833 @property
834 def setter_name(self):
835 return "set" + self.title_name
836
837 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700838 def default_name(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700839 if self.is_fixed_value:
840 return self.constant_name
841 else:
842 return "DEFAULT_"+self.constant_name
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700843
844 @property
845 def default_value(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700846 if self.is_fixed_value:
847 return self.enum_value
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700848 else:
Andreas Wundsam142dd2a2013-09-23 14:47:37 -0700849 default = self.java_type.default_op(self.msg.version)
850 if default == "null" and not self.is_nullable:
851 return None
852 else:
853 return default
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700854
855 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700856 def enum_value(self):
857 if self.name == "version":
858 return "OFVersion.%s" % self.msg.version.constant_version
859
860 java_type = self.java_type.public_type;
861 try:
862 global model
863 enum = model.enum_by_name(java_type)
864 entry = enum.entry_by_version_value(self.msg.version, self.value)
865 return "%s.%s" % ( enum.name, entry.name)
866 except KeyError, e:
867 print e.message
868 return self.value
869
870 @property
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700871 def is_pad(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700872 return isinstance(self.member, OFPadMember)
873
874 def is_type_value(self, version=None):
875 if(version==None):
876 return any(self.is_type_value(version) for version in self.msg.all_versions)
877 try:
878 return self.c_name in get_type_values(self.msg.c_name, version.int_version)
879 except:
880 return False
881
882 @property
883 def is_field_length_value(self):
884 return isinstance(self.member, OFFieldLengthMember)
885
886 @property
Andreas Wundsam001b1822013-08-02 22:25:55 -0700887 def is_discriminator(self):
888 return isinstance(self.member, OFDiscriminatorMember)
889
890 @property
Andreas Wundsam27303462013-07-16 12:52:35 -0700891 def is_length_value(self):
892 return isinstance(self.member, OFLengthMember)
893
894 @property
895 def is_public(self):
896 return not (self.is_pad or self.is_length_value)
897
898 @property
899 def is_data(self):
900 return isinstance(self.member, OFDataMember) and self.name != "version"
901
902 @property
903 def is_fixed_value(self):
904 return hasattr(self.member, "value") or self.name == "version" \
905 or ( self.name == "length" and self.msg.is_fixed_length) \
906 or ( self.name == "len" and self.msg.is_fixed_length)
907
908 @property
909 def value(self):
910 if self.name == "version":
911 return self.msg.version.int_version
912 elif self.name == "length" or self.name == "len":
913 return self.msg.length
Andreas Wundsam27303462013-07-16 12:52:35 -0700914 else:
Andreas Wundsam001b1822013-08-02 22:25:55 -0700915 return self.java_type.format_value(self.member.value)
916
917 @property
918 def priv_value(self):
919 if self.name == "version":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700920 return self.java_type.format_value(self.msg.version.int_version, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700921 elif self.name == "length" or self.name == "len":
Andreas Wundsam22ba3af2013-10-04 16:00:30 -0700922 return self.java_type.format_value(self.msg.length, pub_type=False)
Andreas Wundsam001b1822013-08-02 22:25:55 -0700923 else:
924 return self.java_type.format_value(self.member.value, pub_type=False)
925
Andreas Wundsam27303462013-07-16 12:52:35 -0700926
927 @property
928 def is_writeable(self):
929 return self.is_data and not self.name in model.write_blacklist[self.msg.name]
930
931 def get_type_value_info(self, version):
932 return get_type_values(msg.c_name, version.int_version)[self.c_name]
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700933
934 @property
935 def length(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700936 if hasattr(self.member, "length"):
937 return self.member.length
938 else:
Andreas Wundsam5204de22013-07-30 11:34:45 -0700939 count, base = loxi_utils.type_dec_to_count_base(self.member.type)
Andreas Wundsam27303462013-07-16 12:52:35 -0700940 return of_g.of_base_types[base]['bytes'] * count
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700941
942 @staticmethod
Andreas Wundsam27303462013-07-16 12:52:35 -0700943 def for_of_member(java_class, member):
944 if isinstance(member, OFPadMember):
945 return JavaMember(None, "", None, member)
946 else:
947 if member.name == 'len':
948 name = 'length'
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700949 elif member.name == 'value_mask':
950 name = 'mask'
Andreas Wundsamacd57d52013-10-18 17:35:01 -0700951 elif member.name == 'group_id':
952 name = 'group'
Andreas Wundsam27303462013-07-16 12:52:35 -0700953 else:
954 name = java_type.name_c_to_camel(member.name)
955 j_type = java_type.convert_to_jtype(java_class.c_name, member.name, member.oftype)
956 return JavaMember(java_class, name, j_type, member)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700957
958 @property
959 def is_universal(self):
Andreas Wundsam27303462013-07-16 12:52:35 -0700960 if not self.msg.c_name in of_g.unified:
961 print("%s not self.unified" % self.msg.c_name)
962 return False
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700963 for version in of_g.unified[self.msg.c_name]:
964 if version == 'union' or version =='object_id':
965 continue
966 if 'use_version' in of_g.unified[self.msg.c_name][version]:
967 continue
968
Andreas Wundsam27303462013-07-16 12:52:35 -0700969 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 -0700970 return False
971 return True
972
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700973 @property
974 def is_virtual(self):
975 return False
976
Andreas Wundsam27303462013-07-16 12:52:35 -0700977 def __hash__(self):
978 return hash(self.name)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700979
Andreas Wundsam27303462013-07-16 12:52:35 -0700980 def __eq__(self, other):
981 if other is None or type(self) != type(other):
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700982 return False
Andreas Wundsam27303462013-07-16 12:52:35 -0700983 return (self.name,) == (other.name,)
Andreas Wundsam40e14f72013-05-06 14:49:08 -0700984
Andreas Wundsamf1949682013-09-23 14:48:31 -0700985 @property
986 def is_nullable(self):
987 return self.name in model.nullable_map[self.msg.name]
988
989
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700990class JavaVirtualMember(JavaMember):
991 """ Models a virtual property (member) of an openflow class that is not backed by a loxi ir member """
Andreas Wundsam4b8661f2013-11-05 17:17:28 -0800992 def __init__(self, msg, name, java_type, value=None, custom_template=None):
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700993 JavaMember.__init__(self, msg, name, java_type, member=None)
994 self._value = value
Andreas Wundsam4b8661f2013-11-05 17:17:28 -0800995 self.custom_template = custom_template
Andreas Wundsam2be7da52013-08-22 07:34:25 -0700996
997 @property
998 def is_fixed_value(self):
999 return True
1000
1001 @property
1002 def value(self):
1003 return self._value
1004
1005 @property
1006 def priv_value(self):
1007 return self._value
1008
1009
1010 @property
1011 def is_universal(self):
1012 return True
1013
1014 @property
1015 def is_virtual(self):
1016 return True
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001017
1018#######################################################################
1019### Unit Test
1020#######################################################################
1021
Yotam Harchol466b3212013-08-15 12:14:46 -07001022class JavaUnitTestSet(object):
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001023 def __init__(self, java_class):
1024 self.java_class = java_class
Yotam Harchol466b3212013-08-15 12:14:46 -07001025 first_data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001026 name=java_class.c_name[3:])
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001027 glob_file_name = "of{version}/{name}__*.data".format(version=java_class.version.of_version,
1028 name=java_class.c_name[3:])
Yotam Harchol466b3212013-08-15 12:14:46 -07001029 test_class_name = self.java_class.name + "Test"
1030 self.test_units = []
1031 if test_data.exists(first_data_file_name):
1032 self.test_units.append(JavaUnitTest(java_class, first_data_file_name, test_class_name))
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001033
Yotam Harchol466b3212013-08-15 12:14:46 -07001034 i = 1
Andreas Wundsamaa4f3cd2013-10-22 22:19:08 -07001035 for f in test_data.glob(glob_file_name):
1036 m = re.match(".*__(.*).data", f)
1037 if m:
1038 suffix = java_type.name_c_to_caps_camel(m.group(1))
1039 else:
1040 suffix = str(i)
1041 i += 1
1042 test_class_name = self.java_class.name + suffix + "Test"
1043 self.test_units.append(JavaUnitTest(java_class, f, test_class_name))
Andreas Wundsamf1949682013-09-23 14:48:31 -07001044
Yotam Harchol466b3212013-08-15 12:14:46 -07001045 @property
1046 def package(self):
1047 return self.java_class.package
1048
1049 @property
1050 def has_test_data(self):
1051 return len(self.test_units) > 0
1052
1053 @property
1054 def length(self):
1055 return len(self.test_units)
Andreas Wundsamf1949682013-09-23 14:48:31 -07001056
Yotam Harchol466b3212013-08-15 12:14:46 -07001057 def get_test_unit(self, i):
1058 return self.test_units[i]
1059
1060
1061class JavaUnitTest(object):
1062 def __init__(self, java_class, file_name=None, test_class_name=None):
1063 self.java_class = java_class
1064 if file_name is None:
1065 self.data_file_name = "of{version}/{name}.data".format(version=java_class.version.of_version,
1066 name=java_class.c_name[3:])
1067 else:
1068 self.data_file_name = file_name
1069 if test_class_name is None:
1070 self.test_class_name = self.java_class.name + "Test"
1071 else:
1072 self.test_class_name = test_class_name
Andreas Wundsamf1949682013-09-23 14:48:31 -07001073
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001074 @property
1075 def package(self):
1076 return self.java_class.package
1077
1078 @property
1079 def name(self):
Yotam Harchol466b3212013-08-15 12:14:46 -07001080 return self.test_class_name
Yotam Harchol595c6442013-09-27 16:29:08 -07001081
1082 @property
1083 def interface(self):
1084 return self.java_class.interface
1085
Andreas Wundsame916d6f2013-07-30 11:33:58 -07001086 @property
1087 def has_test_data(self):
1088 return test_data.exists(self.data_file_name)
1089
1090 @property
1091 @memoize
1092 def test_data(self):
1093 return test_data.read(self.data_file_name)
1094
1095
Andreas Wundsam27303462013-07-16 12:52:35 -07001096#######################################################################
1097### Enums
1098#######################################################################
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001099
Andreas Wundsam27303462013-07-16 12:52:35 -07001100class JavaEnum(object):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001101 def __init__(self, c_name, version_enum_map):
Andreas Wundsam27303462013-07-16 12:52:35 -07001102 self.c_name = c_name
Andreas Wundsambe168f72013-08-03 22:49:35 -07001103
Rob Vaterlausfeee3712013-09-30 11:24:19 -07001104 self.name = "OF" + java_type.name_c_to_caps_camel("_".join(c_name.split("_")[1:]))
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001105
Andreas Wundsam27303462013-07-16 12:52:35 -07001106 # Port_features has constants that start with digits
1107 self.name_prefix = "PF_" if self.name == "OFPortFeatures" else ""
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001108
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001109 self.version_enums = version_enum_map
1110
1111 entry_name_version_value_map = OrderedDefaultDict(lambda: OrderedDict())
1112 for version, ir_enum in version_enum_map.items():
1113 for ir_entry in ir_enum.entries:
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001114 entry_name_version_value_map[ir_entry.name][version] = ir_entry.value
1115
Andreas Wundsam27303462013-07-16 12:52:35 -07001116 self.entries = [ JavaEnumEntry(self, name, version_value_map)
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001117 for (name, version_value_map) in entry_name_version_value_map.items() ]
Andreas Wundsam43526532013-08-01 22:03:50 -07001118
1119 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 -07001120 self.package = "org.projectfloodlight.openflow.protocol"
Andreas Wundsam40e14f72013-05-06 14:49:08 -07001121
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001122 self.metadata = model.enum_metadata_map[self.name]
1123
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001124 def wire_type(self, version):
1125 ir_enum = self.version_enums[version]
1126 if "wire_type" in ir_enum.params:
1127 return java_type.convert_enum_wire_type_to_jtype(ir_enum.params["wire_type"])
1128 else:
1129 return java_type.u8
1130
1131 @property
Andreas Wundsam7cfeac32013-09-17 13:53:48 -07001132 @memoize
1133 def is_bitmask(self):
1134 return any(ir_enum.is_bitmask for ir_enum in self.version_enums.values())
1135
1136 @property
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001137 def versions(self):
1138 return self.version_enums.keys()
1139
Andreas Wundsam27303462013-07-16 12:52:35 -07001140 @memoize
1141 def entry_by_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001142 res = find(lambda e: e.name == name, self.entries)
1143 if res:
1144 return res
1145 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001146 raise KeyError("Enum %s: no entry with name %s" % (self.name, name))
1147
1148 @memoize
1149 def entry_by_c_name(self, name):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001150 res = find(lambda e: e.c_name == name, self.entries)
1151 if res:
1152 return res
1153 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001154 raise KeyError("Enum %s: no entry with c_name %s" % (self.name, name))
1155
1156 @memoize
1157 def entry_by_version_value(self, version, value):
Andreas Wundsam1f0d8802013-08-02 22:20:14 -07001158 res = find(lambda e: e.values[version] == value if version in e.values else False, self.entries)
1159 if res:
1160 return res
1161 else:
Andreas Wundsam27303462013-07-16 12:52:35 -07001162 raise KeyError("Enum %s: no entry with version %s, value %s" % (self.name, version, value))
1163
1164# values: Map JavaVersion->Value
1165class JavaEnumEntry(object):
1166 def __init__(self, enum, name, values):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001167 self.enum = enum
Andreas Wundsam27303462013-07-16 12:52:35 -07001168 self.name = enum.name_prefix + "_".join(name.split("_")[1:]).upper()
1169 self.values = values
1170
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001171 @property
1172 def constructor_params(self):
Rob Vaterlaus6035bf52013-09-17 19:32:38 -07001173 return [ m.value(self) for m in self.enum.metadata.properties ]
Andreas Wundsam8ec3bcc2013-09-16 19:44:00 -07001174
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001175 def has_value(self, version):
1176 return version in self.values
1177
Andreas Wundsam27303462013-07-16 12:52:35 -07001178 def value(self, version):
Andreas Wundsambf1dbbd2013-07-30 11:07:59 -07001179 return self.values[version]
1180
1181 def format_value(self, version):
1182 res = self.enum.wire_type(version).format_value(self.values[version])
Andreas Wundsam27303462013-07-16 12:52:35 -07001183 return res
1184
Andreas Wundsam27303462013-07-16 12:52:35 -07001185 def all_values(self, versions, not_present=None):
1186 return [ self.values[version] if version in self.values else not_present for version in versions ]
Andreas Wundsamc1acfcc2013-09-20 13:10:20 -07001187
1188 @property
1189 @memoize
1190 def masked_enum_group(self):
1191 group = find(lambda g: self.name in g.members, model.masked_enum_groups[self.enum.name])
1192 return group
1193
1194 @property
1195 @memoize
1196 def is_mask(self):
1197 return any(self.name == g.mask for g in model.masked_enum_groups[self.enum.name])