blob: 36b75bd8953cffeb5a502a4bb9372700456ad223 [file] [log] [blame]
Yi Tseng43ee7e82018-04-12 16:37:34 +08001#!/usr/bin/env python2.7
2# -*- utf-8 -*-
3import argparse
Yi Tseng43ee7e82018-04-12 16:37:34 +08004import google.protobuf.text_format as tf
Carmelo Casconeb5324e72018-11-25 02:26:32 -08005import re
Yi Tseng13c27f12018-06-23 01:08:55 +08006from p4.config.v1 import p4info_pb2
Yi Tseng43ee7e82018-04-12 16:37:34 +08007
Yi Tseng43ee7e82018-04-12 16:37:34 +08008copyright = '''/*
9 * Copyright 2017-present Open Networking Foundation
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23'''
24
Daniele Moro88e414c2021-04-08 23:41:51 +020025IMPORT_ACTION_ID = "import org.onosproject.net.pi.model.PiActionId;"
26IMPORT_ACTION_PARAM_ID = "import org.onosproject.net.pi.model.PiActionParamId;"
27IMPORT_ACTION_PROFILE_ID = "import org.onosproject.net.pi.model.PiActionProfileId;"
28IMPORT_METER_ID = "import org.onosproject.net.pi.model.PiMeterId;"
29IMPORT_PACKET_METADATA_ID = "import org.onosproject.net.pi.model.PiPacketMetadataId;"
30IMPORT_COUNTER_ID = "import org.onosproject.net.pi.model.PiCounterId;"
31IMPORT_MATCH_FIELD_ID = "import org.onosproject.net.pi.model.PiMatchFieldId;"
32IMPORT_TABLE_ID = "import org.onosproject.net.pi.model.PiTableId;"
33
Yi Tseng43ee7e82018-04-12 16:37:34 +080034imports = '''
Daniele Moro88e414c2021-04-08 23:41:51 +020035'''
Yi Tseng43ee7e82018-04-12 16:37:34 +080036
CyberHasH2713bb62019-11-12 21:03:54 +080037PKG_FMT = 'package %s;'
38DEFAULT_PKG_PATH = 'org.onosproject.pipelines.%s'
Yi Tseng43ee7e82018-04-12 16:37:34 +080039
40CLASS_OPEN = 'public final class %s {'
41CLASS_CLOSE = '}'
42
43DEFAULT_CONSTRUCTOR = '''
44 // hide default constructor
45 private %s() {
46 }
47'''
48
49CONST_FMT = ' public static final %s %s = %s;'
50SHORT_CONST_FMT =''' public static final %s %s =
51 %s;'''
52JAVA_STR = 'String'
53EMPTY_STR = ''
54JAVA_DOC_FMT = '''/**
55 * Constants for %s pipeline.
56 */'''
57
58
59PI_HF_FIELD_ID = 'PiMatchFieldId'
60PI_HF_FIELD_ID_CST = 'PiMatchFieldId.of("%s")'
61
62PI_TBL_ID = 'PiTableId'
63PI_TBL_ID_CST = 'PiTableId.of("%s")'
64
65PI_CTR_ID = 'PiCounterId'
66PI_CTR_ID_CST = 'PiCounterId.of("%s")'
67
68PI_ACT_ID = 'PiActionId'
69PI_ACT_ID_CST = 'PiActionId.of("%s")'
70
71PI_ACT_PRM_ID = 'PiActionParamId'
72PI_ACT_PRM_ID_CST = 'PiActionParamId.of("%s")'
73
74PI_ACT_PROF_ID = 'PiActionProfileId'
75PI_ACT_PROF_ID_CST = 'PiActionProfileId.of("%s")'
76
Carmelo Cascone4c289b72019-01-22 15:30:45 -080077PI_PKT_META_ID = 'PiPacketMetadataId'
78PI_PKT_META_ID_CST = 'PiPacketMetadataId.of("%s")'
Yi Tseng43ee7e82018-04-12 16:37:34 +080079
Daniele Moro1edc0bd2019-08-21 17:22:50 -070080PI_METER_ID = 'PiMeterId'
81PI_METER_ID_CST = 'PiMeterId.of("%s")'
82
Carmelo Casconeb5324e72018-11-25 02:26:32 -080083HF_VAR_PREFIX = 'HDR_'
84
85
Yi Tseng43ee7e82018-04-12 16:37:34 +080086class ConstantClassGenerator(object):
87 headers = set()
88 header_fields = set()
89 tables = set()
90 counters = set()
91 direct_counters = set()
92 actions = set()
93 action_params = set()
94 action_profiles = set()
95 packet_metadata = set()
Daniele Moro1edc0bd2019-08-21 17:22:50 -070096 meters = set()
Daniele Moro88e414c2021-04-08 23:41:51 +020097 direct_meters = set()
Yi Tseng43ee7e82018-04-12 16:37:34 +080098
99 # https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case
100 def convert_camel_to_all_caps(self, name):
101 s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
102 s1 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).upper()
103 return s1.replace('.', '_')
104
CyberHasH2713bb62019-11-12 21:03:54 +0800105 def __init__(self, base_name, pkg_path):
Yi Tseng43ee7e82018-04-12 16:37:34 +0800106
107 self.class_name = base_name.title() + 'Constants'
CyberHasH2713bb62019-11-12 21:03:54 +0800108 self.package_name = PKG_FMT % (pkg_path, )
Yi Tseng43ee7e82018-04-12 16:37:34 +0800109 self.java_doc = JAVA_DOC_FMT % (base_name, )
110
111 def parse(self, p4info):
Daniele Moro88e414c2021-04-08 23:41:51 +0200112 global imports
113
114 if len(p4info.tables) > 0:
115 imports += IMPORT_TABLE_ID + "\n"
116 imports += IMPORT_MATCH_FIELD_ID + "\n"
117
Yi Tseng43ee7e82018-04-12 16:37:34 +0800118 for tbl in p4info.tables:
119 for mf in tbl.match_fields:
120 self.header_fields.add(mf.name)
121
122 self.tables.add(tbl.preamble.name)
123
Daniele Moro88e414c2021-04-08 23:41:51 +0200124 if len(p4info.counters) > 0 or len(p4info.direct_counters) > 0:
125 imports += IMPORT_COUNTER_ID + "\n"
126
Yi Tseng43ee7e82018-04-12 16:37:34 +0800127 for ctr in p4info.counters:
128 self.counters.add(ctr.preamble.name)
129
130 for dir_ctr in p4info.direct_counters:
131 self.direct_counters.add(dir_ctr.preamble.name)
132
Daniele Moro88e414c2021-04-08 23:41:51 +0200133 if len(p4info.actions) > 0:
134 imports += IMPORT_ACTION_ID + "\n"
135 imports += IMPORT_ACTION_PARAM_ID + "\n"
136
Yi Tseng43ee7e82018-04-12 16:37:34 +0800137 for act in p4info.actions:
138 self.actions.add(act.preamble.name)
139
140 for param in act.params:
141 self.action_params.add(param.name)
142
Daniele Moro88e414c2021-04-08 23:41:51 +0200143 if len(p4info.action_profiles) > 0:
144 imports += IMPORT_ACTION_PROFILE_ID + "\n"
145
Yi Tseng43ee7e82018-04-12 16:37:34 +0800146 for act_prof in p4info.action_profiles:
147 self.action_profiles.add(act_prof.preamble.name)
148
Daniele Moro88e414c2021-04-08 23:41:51 +0200149 if len(p4info.controller_packet_metadata) > 0:
150 imports += IMPORT_PACKET_METADATA_ID + "\n"
151
Yi Tseng43ee7e82018-04-12 16:37:34 +0800152 for cpm in p4info.controller_packet_metadata:
153 for mta in cpm.metadata:
154 self.packet_metadata.add(mta.name)
Daniele Moro88e414c2021-04-08 23:41:51 +0200155
156 if len(p4info.meters) > 0 or len(p4info.direct_meters) > 0:
157 imports += IMPORT_METER_ID + "\n"
158
Daniele Moro1edc0bd2019-08-21 17:22:50 -0700159 for mtr in p4info.meters:
160 self.meters.add(mtr.preamble.name)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800161
Daniele Moro88e414c2021-04-08 23:41:51 +0200162 for dir_mtr in p4info.direct_meters:
163 self.direct_meters.add(dir_mtr.preamble.name)
164
Yi Tseng43ee7e82018-04-12 16:37:34 +0800165 def const_line(self, name, type, constructor):
166 var_name = self.convert_camel_to_all_caps(name)
Carmelo Casconeb5324e72018-11-25 02:26:32 -0800167 if type == PI_HF_FIELD_ID:
168 var_name = HF_VAR_PREFIX + var_name
Yi Tseng43ee7e82018-04-12 16:37:34 +0800169 val = constructor % (name, )
170
171 line = CONST_FMT % (type, var_name, val)
172 if len(line) > 80:
173 line = SHORT_CONST_FMT % (type, var_name, val)
174 return line
175
176 def generate_java(self):
177 lines = list()
178 lines.append(copyright)
179 lines.append(self.package_name)
180 lines.append(imports)
181 lines.append(self.java_doc)
182 # generate the class
183 lines.append(CLASS_OPEN % (self.class_name, ))
184 lines.append(DEFAULT_CONSTRUCTOR % (self.class_name, ))
185
186 if len(self.header_fields) is not 0:
187 lines.append(' // Header field IDs')
188 for hf in self.header_fields:
189 lines.append(self.const_line(hf, PI_HF_FIELD_ID, PI_HF_FIELD_ID_CST))
190
191 if len(self.tables) is not 0:
192 lines.append(' // Table IDs')
193 for tbl in self.tables:
194 lines.append(self.const_line(tbl, PI_TBL_ID, PI_TBL_ID_CST))
195
196 if len(self.counters) is not 0:
197 lines.append(' // Indirect Counter IDs')
198 for ctr in self.counters:
199 lines.append(self.const_line(ctr, PI_CTR_ID, PI_CTR_ID_CST))
200
201 if len(self.direct_counters) is not 0:
202 lines.append(' // Direct Counter IDs')
203 for dctr in self.direct_counters:
204 lines.append(self.const_line(dctr, PI_CTR_ID, PI_CTR_ID_CST))
205
206 if len(self.actions) is not 0:
207 lines.append(' // Action IDs')
208 for act in self.actions:
209 lines.append(self.const_line(act, PI_ACT_ID, PI_ACT_ID_CST))
210
211 if len(self.action_params) is not 0:
212 lines.append(' // Action Param IDs')
213 for act_prm in self.action_params:
214 lines.append(self.const_line(act_prm, PI_ACT_PRM_ID, PI_ACT_PRM_ID_CST))
215
216 if len(self.action_profiles) is not 0:
217 lines.append(' // Action Profile IDs')
218 for act_prof in self.action_profiles:
219 lines.append(self.const_line(act_prof, PI_ACT_PROF_ID, PI_ACT_PROF_ID_CST))
220
221 if len(self.packet_metadata) is not 0:
222 lines.append(' // Packet Metadata IDs')
223 for pmeta in self.packet_metadata:
Daniele Moro1edc0bd2019-08-21 17:22:50 -0700224 if not pmeta.startswith("_"):
225 lines.append(self.const_line(pmeta, PI_PKT_META_ID, PI_PKT_META_ID_CST))
226
227 if len(self.meters) is not 0:
228 lines.append(' // Meter IDs')
229 for mtr in self.meters:
230 lines.append(self.const_line(mtr, PI_METER_ID, PI_METER_ID_CST))
Daniele Moro88e414c2021-04-08 23:41:51 +0200231
232 if len(self.direct_meters) is not 0:
233 lines.append(' // Direct Meter IDs')
234 for mtr in self.direct_meters:
235 lines.append(self.const_line(mtr, PI_METER_ID, PI_METER_ID_CST))
236
Yi Tseng43ee7e82018-04-12 16:37:34 +0800237 lines.append(CLASS_CLOSE)
238 # end of class
239
240 return '\n'.join(lines)
241
CyberHasH2713bb62019-11-12 21:03:54 +0800242def gen_pkg_path(output, base_name):
243 if output is not None:
244 i = output.find('java/')
245 if i != -1:
246 pkg_path = output[i+5:]
247 last_slash = pkg_path.rfind('/')
248 pkg_path = pkg_path[:last_slash].replace('/','.')
249 return pkg_path
250 return DEFAULT_PKG_PATH % (base_name, )
Yi Tseng43ee7e82018-04-12 16:37:34 +0800251def main():
252 parser = argparse.ArgumentParser(prog='onos-gen-p4-constants',
253 description='ONOS P4Info to Java constant generator.')
254 parser.add_argument('name', help='Name of the constant, will be used as class name')
255 parser.add_argument('p4info', help='P4Info file')
256 parser.add_argument('-o', '--output', help='output path', default='-')
CyberHasH2713bb62019-11-12 21:03:54 +0800257 parser.add_argument('--with-package-path', help='Specify the java package path', dest='pkg_path')
Yi Tseng43ee7e82018-04-12 16:37:34 +0800258 args = parser.parse_args()
259
260 base_name = args.name
261 file_name = args.p4info
CyberHasH2713bb62019-11-12 21:03:54 +0800262 output = args.output
263 pkg_path = args.pkg_path
264 if pkg_path is None:
265 pkg_path = gen_pkg_path(output, base_name)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800266 p4info = p4info_pb2.P4Info()
267 with open(file_name, 'r') as intput_file:
268 s = intput_file.read()
269 tf.Merge(s, p4info)
270
CyberHasH2713bb62019-11-12 21:03:54 +0800271 gen = ConstantClassGenerator(base_name, pkg_path)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800272 gen.parse(p4info)
273
274 java_code = gen.generate_java()
275
CyberHasH2713bb62019-11-12 21:03:54 +0800276 if output == '-':
Yi Tseng43ee7e82018-04-12 16:37:34 +0800277 # std output
Daniele Moro88e414c2021-04-08 23:41:51 +0200278 print(java_code)
Yi Tseng43ee7e82018-04-12 16:37:34 +0800279 else:
CyberHasH2713bb62019-11-12 21:03:54 +0800280 with open(output, 'w') as output_file:
Yi Tseng43ee7e82018-04-12 16:37:34 +0800281 output_file.write(java_code)
282
283
284if __name__ == '__main__':
Daniele Moro88e414c2021-04-08 23:41:51 +0200285 main()