blob: c2200507e6a023e50781cd55daf5cf63ca35e2a5 [file] [log] [blame]
Carmelo Cascone98dfdb72017-07-14 12:01:37 -04001/*
Carmelo Cascone4c289b72019-01-22 15:30:45 -08002 * Copyright 2019-present Open Networking Foundation
Carmelo Cascone98dfdb72017-07-14 12:01:37 -04003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Carmelo Cascone4c289b72019-01-22 15:30:45 -080017package org.onosproject.p4runtime.ctl.utils;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040018
19
20import com.google.common.collect.Maps;
21import com.google.protobuf.Message;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020022import p4.config.v1.P4InfoOuterClass.Action;
23import p4.config.v1.P4InfoOuterClass.ActionProfile;
24import p4.config.v1.P4InfoOuterClass.ControllerPacketMetadata;
25import p4.config.v1.P4InfoOuterClass.Counter;
Carmelo Cascone304b6642021-01-28 17:25:16 -080026import p4.config.v1.P4InfoOuterClass.Digest;
Carmelo Cascone6af4e172018-06-15 16:01:30 +020027import p4.config.v1.P4InfoOuterClass.DirectCounter;
28import p4.config.v1.P4InfoOuterClass.DirectMeter;
29import p4.config.v1.P4InfoOuterClass.MatchField;
30import p4.config.v1.P4InfoOuterClass.Meter;
31import p4.config.v1.P4InfoOuterClass.P4Info;
32import p4.config.v1.P4InfoOuterClass.Preamble;
33import p4.config.v1.P4InfoOuterClass.Table;
Daniele Moro5c82b0f2020-12-07 20:56:30 +010034import p4.config.v1.P4Types;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040035
36import java.util.Map;
37
Carmelo Cascone8d99b172017-07-18 17:26:31 -040038import static com.google.common.base.Preconditions.checkArgument;
39import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040040import static java.lang.String.format;
41
42/**
43 * Utility class to easily retrieve information from a P4Info protobuf message.
44 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -080045public final class P4InfoBrowser {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040046
47 private final EntityBrowser<Table> tables = new EntityBrowser<>("table");
48 private final EntityBrowser<Action> actions = new EntityBrowser<>("action");
49 private final EntityBrowser<ActionProfile> actionProfiles = new EntityBrowser<>("action profile");
50 private final EntityBrowser<Counter> counters = new EntityBrowser<>("counter");
51 private final EntityBrowser<DirectCounter> directCounters = new EntityBrowser<>("direct counter");
52 private final EntityBrowser<Meter> meters = new EntityBrowser<>("meter");
53 private final EntityBrowser<DirectMeter> directMeters = new EntityBrowser<>("direct meter");
54 private final EntityBrowser<ControllerPacketMetadata> ctrlPktMetadatas =
55 new EntityBrowser<>("controller packet metadata");
56 private final Map<Integer, EntityBrowser<Action.Param>> actionParams = Maps.newHashMap();
57 private final Map<Integer, EntityBrowser<MatchField>> matchFields = Maps.newHashMap();
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020058 private final Map<Integer, EntityBrowser<ControllerPacketMetadata.Metadata>> ctrlPktMetadatasMetadata =
59 Maps.newHashMap();
Carmelo Cascone304b6642021-01-28 17:25:16 -080060 private final EntityBrowser<Digest> digests = new EntityBrowser<>("digest");
Daniele Moro5c82b0f2020-12-07 20:56:30 +010061 private final Map<String, Boolean> isTypeString = Maps.newHashMap();
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040062
63 /**
64 * Creates a new browser for the given P4Info.
65 *
66 * @param p4info P4Info protobuf message
67 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -080068 public P4InfoBrowser(P4Info p4info) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040069 parseP4Info(p4info);
70 }
71
72 private void parseP4Info(P4Info p4info) {
73 p4info.getTablesList().forEach(
74 entity -> {
75 tables.addWithPreamble(entity.getPreamble(), entity);
76 // Index match fields.
77 int tableId = entity.getPreamble().getId();
78 String tableName = entity.getPreamble().getName();
79 EntityBrowser<MatchField> matchFieldBrowser = new EntityBrowser<>(format(
80 "match field for table '%s'", tableName));
Carmelo Cascone87892e22017-11-13 16:01:29 -080081 entity.getMatchFieldsList().forEach(m -> matchFieldBrowser.add(m.getName(), null, m.getId(), m));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040082 matchFields.put(tableId, matchFieldBrowser);
83 });
84
85 p4info.getActionsList().forEach(
86 entity -> {
87 actions.addWithPreamble(entity.getPreamble(), entity);
88 // Index action params.
89 int actionId = entity.getPreamble().getId();
90 String actionName = entity.getPreamble().getName();
91 EntityBrowser<Action.Param> paramBrowser = new EntityBrowser<>(format(
92 "param for action '%s'", actionName));
Carmelo Cascone8d99b172017-07-18 17:26:31 -040093 entity.getParamsList().forEach(p -> paramBrowser.add(p.getName(), null, p.getId(), p));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040094 actionParams.put(actionId, paramBrowser);
95 });
96
97 p4info.getActionProfilesList().forEach(
98 entity -> actionProfiles.addWithPreamble(entity.getPreamble(), entity));
99
100 p4info.getCountersList().forEach(
101 entity -> counters.addWithPreamble(entity.getPreamble(), entity));
102
103 p4info.getDirectCountersList().forEach(
104 entity -> directCounters.addWithPreamble(entity.getPreamble(), entity));
105
106 p4info.getMetersList().forEach(
107 entity -> meters.addWithPreamble(entity.getPreamble(), entity));
108
109 p4info.getDirectMetersList().forEach(
110 entity -> directMeters.addWithPreamble(entity.getPreamble(), entity));
111
112 p4info.getControllerPacketMetadataList().forEach(
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200113 entity -> {
114 ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity);
115 // Index control packet metadata metadata.
116 int ctrlPktMetadataId = entity.getPreamble().getId();
117 String ctrlPktMetadataName = entity.getPreamble().getName();
118 EntityBrowser<ControllerPacketMetadata.Metadata> metadataBrowser = new EntityBrowser<>(format(
119 "metadata field for controller packet metadata '%s'", ctrlPktMetadataName));
120 entity.getMetadataList().forEach(m -> metadataBrowser.add(m.getName(), null, m.getId(), m));
121 ctrlPktMetadatasMetadata.put(ctrlPktMetadataId, metadataBrowser);
122 });
Carmelo Cascone304b6642021-01-28 17:25:16 -0800123
124 p4info.getDigestsList().forEach(
125 entity -> digests.addWithPreamble(entity.getPreamble(), entity));
Daniele Moro5c82b0f2020-12-07 20:56:30 +0100126 p4info.getTypeInfo().getNewTypesMap().forEach(
127 (s, p4NewTypeSpec) ->
128 isTypeString.put(s,
129 p4NewTypeSpec.hasTranslatedType()
130 && p4NewTypeSpec.getTranslatedType().hasSdnString()
131 ));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400132 }
133
134 /**
135 * Returns a browser for tables.
136 *
137 * @return table browser
138 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800139 public EntityBrowser<Table> tables() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400140 return tables;
141 }
142
143 /**
144 * Returns a browser for actions.
145 *
146 * @return action browser
147 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800148 public EntityBrowser<Action> actions() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400149 return actions;
150 }
151
152 /**
153 * Returns a browser for action profiles.
154 *
155 * @return action profile browser
156 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800157 public EntityBrowser<ActionProfile> actionProfiles() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400158 return actionProfiles;
159 }
160
161 /**
162 * Returns a browser for counters.
163 *
164 * @return counter browser
165 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800166 public EntityBrowser<Counter> counters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400167 return counters;
168 }
169
170 /**
171 * Returns a browser for direct counters.
172 *
173 * @return direct counter browser
174 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800175 public EntityBrowser<DirectCounter> directCounters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400176 return directCounters;
177 }
178
179 /**
180 * Returns a browser for meters.
181 *
182 * @return meter browser
183 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800184 public EntityBrowser<Meter> meters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400185 return meters;
186 }
187
188 /**
189 * Returns a browser for direct meters.
190 *
191 * @return table browser
192 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800193 public EntityBrowser<DirectMeter> directMeters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400194 return directMeters;
195 }
196
197 /**
198 * Returns a browser for controller packet metadata.
199 *
200 * @return controller packet metadata browser
201 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800202 public EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400203 return ctrlPktMetadatas;
204 }
205
206 /**
Carmelo Cascone304b6642021-01-28 17:25:16 -0800207 * Returns a browser for digests.
208 *
209 * @return digest browser
210 */
211 public EntityBrowser<Digest> digests() {
212 return digests;
213 }
214
215 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400216 * Returns a browser for params of the given action.
217 *
218 * @param actionId action identifier
219 * @return action params browser
220 * @throws NotFoundException if the action cannot be found
221 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800222 public EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400223 // Throws exception if action id is not found.
224 actions.getById(actionId);
225 return actionParams.get(actionId);
226 }
227
228 /**
229 * Returns a browser for match fields of the given table.
230 *
231 * @param tableId table identifier
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200232 * @return match field browser
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400233 * @throws NotFoundException if the table cannot be found
234 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800235 public EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400236 // Throws exception if action id is not found.
237 tables.getById(tableId);
238 return matchFields.get(tableId);
239 }
240
241 /**
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200242 * Returns a browser for metadata fields of the controller packet metadata.
243 *
244 * @param controllerPacketMetadataId controller packet metadata identifier
245 * @return metadata browser
Frank Wangd8ab0962017-08-11 11:09:30 +0800246 * @throws NotFoundException controller packet metadata cannot be found
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200247 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800248 public EntityBrowser<ControllerPacketMetadata.Metadata> packetMetadatas(int controllerPacketMetadataId)
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200249 throws NotFoundException {
250 // Throws exception if controller packet metadata id is not found.
251 ctrlPktMetadatas.getById(controllerPacketMetadataId);
252 return ctrlPktMetadatasMetadata.get(controllerPacketMetadataId);
253 }
254
255 /**
Daniele Moro5c82b0f2020-12-07 20:56:30 +0100256 * Checks if the given type name is a sdn_string.
257 *
258 * @param typeName Type name to check
259 * @return True if the given type name is a sdn_string, false otherwise
260 */
261 public boolean isTypeString(P4Types.P4NamedType typeName) {
262 return isTypeString.containsKey(typeName.getName())
263 && isTypeString.get(typeName.getName());
264 }
265
266 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400267 * Browser of P4Info entities.
268 *
269 * @param <T> protobuf message type
270 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800271 public static final class EntityBrowser<T extends Message> {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400272
273 private String entityName;
274 private final Map<String, T> names = Maps.newHashMap();
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200275 private final Map<String, String> aliasToNames = Maps.newHashMap();
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400276 private final Map<Integer, T> ids = Maps.newHashMap();
277
278 private EntityBrowser(String entityName) {
279 this.entityName = entityName;
280 }
281
282 /**
Carmelo Cascone04d09392017-07-19 08:49:08 -0400283 * Adds the given entity identified by the given name, alias (nullable) and id.
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400284 *
285 * @param name entity name
Carmelo Cascone04d09392017-07-19 08:49:08 -0400286 * @param alias entity alias or null
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400287 * @param id entity id
288 * @param entity entity message
289 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800290 private void add(String name, String alias, int id, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400291 checkNotNull(name);
292 checkArgument(!name.isEmpty(), "Name cannot be empty");
293 checkNotNull(entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400294 names.put(name, entity);
295 ids.put(id, entity);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400296 if (alias != null && !alias.isEmpty()) {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200297 aliasToNames.put(alias, name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400298 }
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400299 }
300
301 /**
302 * Adds the given entity identified by the given P4Info preamble.
303 *
304 * @param preamble P4Info preamble protobuf message
305 * @param entity entity message
306 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800307 private void addWithPreamble(Preamble preamble, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400308 checkNotNull(preamble);
309 add(preamble.getName(), preamble.getAlias(), preamble.getId(), entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400310 }
311
312 /**
313 * Returns true if the P4Info defines an entity with such name, false otherwise.
314 *
315 * @param name entity name
316 * @return boolean
317 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800318 public boolean hasName(String name) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400319 return names.containsKey(name);
320 }
321
322 /**
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200323 * Returns the entity identified by the given name, if present, otherwise, throws an exception.
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400324 *
325 * @param name entity name or alias
326 * @return entity message
327 * @throws NotFoundException if the entity cannot be found
328 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800329 public T getByName(String name) throws NotFoundException {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400330 if (hasName(name)) {
331 return names.get(name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400332 } else {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200333 final String hint = aliasToNames.containsKey(name)
334 ? format("Did you mean '%s'? Make sure to use entity names in PI IDs, not aliases",
335 aliasToNames.get(name))
336 : "";
337 throw new NotFoundException(entityName, name, hint);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400338 }
339 }
340
341 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400342 * Returns true if the P4Info defines an entity with such id, false otherwise.
343 *
344 * @param id entity id
345 * @return boolean
346 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800347 public boolean hasId(int id) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400348 return ids.containsKey(id);
349 }
350
351 /**
352 * Returns the entity identified by the given id, if present, otherwise, throws an exception.
353 *
354 * @param id entity id
355 * @return entity message
356 * @throws NotFoundException if the entity cannot be found
357 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800358 public T getById(int id) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400359 if (!hasId(id)) {
360 throw new NotFoundException(entityName, id);
361 }
362 return ids.get(id);
363 }
364 }
365
366 /**
367 * Signals tha an entity cannot be found in the P4Info.
368 */
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400369 public static final class NotFoundException extends Exception {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400370
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800371 public NotFoundException(String entityName, String key, String hint) {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200372 super(format(
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800373 "No such %s in P4Info with name '%s'%s",
374 entityName, key, hint.isEmpty() ? "" : " (" + hint + ")"));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400375 }
376
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800377 public NotFoundException(String entityName, int id) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400378 super(format("No such %s in P4Info with id '%d'", entityName, id));
379 }
380 }
381}