Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 1 | #!/usr/bin/env python2.7 |
| 2 | # -*- utf-8 -*- |
| 3 | import argparse |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 4 | import google.protobuf.text_format as tf |
Carmelo Cascone | b5324e7 | 2018-11-25 02:26:32 -0800 | [diff] [blame] | 5 | import re |
Yi Tseng | 13c27f1 | 2018-06-23 01:08:55 +0800 | [diff] [blame] | 6 | from p4.config.v1 import p4info_pb2 |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 7 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 8 | copyright = '''/* |
| 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 Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 25 | IMPORT_ACTION_ID = "import org.onosproject.net.pi.model.PiActionId;" |
| 26 | IMPORT_ACTION_PARAM_ID = "import org.onosproject.net.pi.model.PiActionParamId;" |
| 27 | IMPORT_ACTION_PROFILE_ID = "import org.onosproject.net.pi.model.PiActionProfileId;" |
| 28 | IMPORT_METER_ID = "import org.onosproject.net.pi.model.PiMeterId;" |
| 29 | IMPORT_PACKET_METADATA_ID = "import org.onosproject.net.pi.model.PiPacketMetadataId;" |
| 30 | IMPORT_COUNTER_ID = "import org.onosproject.net.pi.model.PiCounterId;" |
| 31 | IMPORT_MATCH_FIELD_ID = "import org.onosproject.net.pi.model.PiMatchFieldId;" |
| 32 | IMPORT_TABLE_ID = "import org.onosproject.net.pi.model.PiTableId;" |
| 33 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 34 | imports = ''' |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 35 | ''' |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 36 | |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 37 | PKG_FMT = 'package %s;' |
| 38 | DEFAULT_PKG_PATH = 'org.onosproject.pipelines.%s' |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 39 | |
| 40 | CLASS_OPEN = 'public final class %s {' |
| 41 | CLASS_CLOSE = '}' |
| 42 | |
| 43 | DEFAULT_CONSTRUCTOR = ''' |
| 44 | // hide default constructor |
| 45 | private %s() { |
| 46 | } |
| 47 | ''' |
| 48 | |
| 49 | CONST_FMT = ' public static final %s %s = %s;' |
| 50 | SHORT_CONST_FMT =''' public static final %s %s = |
| 51 | %s;''' |
| 52 | JAVA_STR = 'String' |
| 53 | EMPTY_STR = '' |
| 54 | JAVA_DOC_FMT = '''/** |
| 55 | * Constants for %s pipeline. |
| 56 | */''' |
| 57 | |
| 58 | |
| 59 | PI_HF_FIELD_ID = 'PiMatchFieldId' |
| 60 | PI_HF_FIELD_ID_CST = 'PiMatchFieldId.of("%s")' |
| 61 | |
| 62 | PI_TBL_ID = 'PiTableId' |
| 63 | PI_TBL_ID_CST = 'PiTableId.of("%s")' |
| 64 | |
| 65 | PI_CTR_ID = 'PiCounterId' |
| 66 | PI_CTR_ID_CST = 'PiCounterId.of("%s")' |
| 67 | |
| 68 | PI_ACT_ID = 'PiActionId' |
| 69 | PI_ACT_ID_CST = 'PiActionId.of("%s")' |
| 70 | |
| 71 | PI_ACT_PRM_ID = 'PiActionParamId' |
| 72 | PI_ACT_PRM_ID_CST = 'PiActionParamId.of("%s")' |
| 73 | |
| 74 | PI_ACT_PROF_ID = 'PiActionProfileId' |
| 75 | PI_ACT_PROF_ID_CST = 'PiActionProfileId.of("%s")' |
| 76 | |
Carmelo Cascone | 4c289b7 | 2019-01-22 15:30:45 -0800 | [diff] [blame] | 77 | PI_PKT_META_ID = 'PiPacketMetadataId' |
| 78 | PI_PKT_META_ID_CST = 'PiPacketMetadataId.of("%s")' |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 79 | |
Daniele Moro | 1edc0bd | 2019-08-21 17:22:50 -0700 | [diff] [blame] | 80 | PI_METER_ID = 'PiMeterId' |
| 81 | PI_METER_ID_CST = 'PiMeterId.of("%s")' |
| 82 | |
Carmelo Cascone | b5324e7 | 2018-11-25 02:26:32 -0800 | [diff] [blame] | 83 | HF_VAR_PREFIX = 'HDR_' |
| 84 | |
| 85 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 86 | class 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 Moro | 1edc0bd | 2019-08-21 17:22:50 -0700 | [diff] [blame] | 96 | meters = set() |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 97 | direct_meters = set() |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 98 | |
| 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 | |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 105 | def __init__(self, base_name, pkg_path): |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 106 | |
| 107 | self.class_name = base_name.title() + 'Constants' |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 108 | self.package_name = PKG_FMT % (pkg_path, ) |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 109 | self.java_doc = JAVA_DOC_FMT % (base_name, ) |
| 110 | |
| 111 | def parse(self, p4info): |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 112 | global imports |
| 113 | |
| 114 | if len(p4info.tables) > 0: |
| 115 | imports += IMPORT_TABLE_ID + "\n" |
| 116 | imports += IMPORT_MATCH_FIELD_ID + "\n" |
| 117 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 118 | 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 Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 124 | if len(p4info.counters) > 0 or len(p4info.direct_counters) > 0: |
| 125 | imports += IMPORT_COUNTER_ID + "\n" |
| 126 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 127 | 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 Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 133 | if len(p4info.actions) > 0: |
| 134 | imports += IMPORT_ACTION_ID + "\n" |
| 135 | imports += IMPORT_ACTION_PARAM_ID + "\n" |
| 136 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 137 | 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 Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 143 | if len(p4info.action_profiles) > 0: |
| 144 | imports += IMPORT_ACTION_PROFILE_ID + "\n" |
| 145 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 146 | for act_prof in p4info.action_profiles: |
| 147 | self.action_profiles.add(act_prof.preamble.name) |
| 148 | |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 149 | if len(p4info.controller_packet_metadata) > 0: |
| 150 | imports += IMPORT_PACKET_METADATA_ID + "\n" |
| 151 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 152 | for cpm in p4info.controller_packet_metadata: |
| 153 | for mta in cpm.metadata: |
| 154 | self.packet_metadata.add(mta.name) |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 155 | |
| 156 | if len(p4info.meters) > 0 or len(p4info.direct_meters) > 0: |
| 157 | imports += IMPORT_METER_ID + "\n" |
| 158 | |
Daniele Moro | 1edc0bd | 2019-08-21 17:22:50 -0700 | [diff] [blame] | 159 | for mtr in p4info.meters: |
| 160 | self.meters.add(mtr.preamble.name) |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 161 | |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 162 | for dir_mtr in p4info.direct_meters: |
| 163 | self.direct_meters.add(dir_mtr.preamble.name) |
| 164 | |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 165 | def const_line(self, name, type, constructor): |
| 166 | var_name = self.convert_camel_to_all_caps(name) |
Carmelo Cascone | b5324e7 | 2018-11-25 02:26:32 -0800 | [diff] [blame] | 167 | if type == PI_HF_FIELD_ID: |
| 168 | var_name = HF_VAR_PREFIX + var_name |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 169 | 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 Moro | 1edc0bd | 2019-08-21 17:22:50 -0700 | [diff] [blame] | 224 | 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 Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 231 | |
| 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 Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 237 | lines.append(CLASS_CLOSE) |
| 238 | # end of class |
| 239 | |
| 240 | return '\n'.join(lines) |
| 241 | |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 242 | def 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 Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 251 | def 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='-') |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 257 | parser.add_argument('--with-package-path', help='Specify the java package path', dest='pkg_path') |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 258 | args = parser.parse_args() |
| 259 | |
| 260 | base_name = args.name |
| 261 | file_name = args.p4info |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 262 | 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 Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 266 | p4info = p4info_pb2.P4Info() |
| 267 | with open(file_name, 'r') as intput_file: |
| 268 | s = intput_file.read() |
| 269 | tf.Merge(s, p4info) |
| 270 | |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 271 | gen = ConstantClassGenerator(base_name, pkg_path) |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 272 | gen.parse(p4info) |
| 273 | |
| 274 | java_code = gen.generate_java() |
| 275 | |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 276 | if output == '-': |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 277 | # std output |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 278 | print(java_code) |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 279 | else: |
CyberHasH | 2713bb6 | 2019-11-12 21:03:54 +0800 | [diff] [blame] | 280 | with open(output, 'w') as output_file: |
Yi Tseng | 43ee7e8 | 2018-04-12 16:37:34 +0800 | [diff] [blame] | 281 | output_file.write(java_code) |
| 282 | |
| 283 | |
| 284 | if __name__ == '__main__': |
Daniele Moro | 88e414c | 2021-04-08 23:41:51 +0200 | [diff] [blame^] | 285 | main() |