blob: 29cddffb9130a7a9e9e2ac485e1402c1fb882e5c [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;
26import p4.config.v1.P4InfoOuterClass.DirectCounter;
27import p4.config.v1.P4InfoOuterClass.DirectMeter;
28import p4.config.v1.P4InfoOuterClass.MatchField;
29import p4.config.v1.P4InfoOuterClass.Meter;
30import p4.config.v1.P4InfoOuterClass.P4Info;
31import p4.config.v1.P4InfoOuterClass.Preamble;
32import p4.config.v1.P4InfoOuterClass.Table;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040033
34import java.util.Map;
35
Carmelo Cascone8d99b172017-07-18 17:26:31 -040036import static com.google.common.base.Preconditions.checkArgument;
37import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040038import static java.lang.String.format;
39
40/**
41 * Utility class to easily retrieve information from a P4Info protobuf message.
42 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -080043public final class P4InfoBrowser {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040044
45 private final EntityBrowser<Table> tables = new EntityBrowser<>("table");
46 private final EntityBrowser<Action> actions = new EntityBrowser<>("action");
47 private final EntityBrowser<ActionProfile> actionProfiles = new EntityBrowser<>("action profile");
48 private final EntityBrowser<Counter> counters = new EntityBrowser<>("counter");
49 private final EntityBrowser<DirectCounter> directCounters = new EntityBrowser<>("direct counter");
50 private final EntityBrowser<Meter> meters = new EntityBrowser<>("meter");
51 private final EntityBrowser<DirectMeter> directMeters = new EntityBrowser<>("direct meter");
52 private final EntityBrowser<ControllerPacketMetadata> ctrlPktMetadatas =
53 new EntityBrowser<>("controller packet metadata");
54 private final Map<Integer, EntityBrowser<Action.Param>> actionParams = Maps.newHashMap();
55 private final Map<Integer, EntityBrowser<MatchField>> matchFields = Maps.newHashMap();
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020056 private final Map<Integer, EntityBrowser<ControllerPacketMetadata.Metadata>> ctrlPktMetadatasMetadata =
57 Maps.newHashMap();
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040058
59 /**
60 * Creates a new browser for the given P4Info.
61 *
62 * @param p4info P4Info protobuf message
63 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -080064 public P4InfoBrowser(P4Info p4info) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040065 parseP4Info(p4info);
66 }
67
68 private void parseP4Info(P4Info p4info) {
69 p4info.getTablesList().forEach(
70 entity -> {
71 tables.addWithPreamble(entity.getPreamble(), entity);
72 // Index match fields.
73 int tableId = entity.getPreamble().getId();
74 String tableName = entity.getPreamble().getName();
75 EntityBrowser<MatchField> matchFieldBrowser = new EntityBrowser<>(format(
76 "match field for table '%s'", tableName));
Carmelo Cascone87892e22017-11-13 16:01:29 -080077 entity.getMatchFieldsList().forEach(m -> matchFieldBrowser.add(m.getName(), null, m.getId(), m));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040078 matchFields.put(tableId, matchFieldBrowser);
79 });
80
81 p4info.getActionsList().forEach(
82 entity -> {
83 actions.addWithPreamble(entity.getPreamble(), entity);
84 // Index action params.
85 int actionId = entity.getPreamble().getId();
86 String actionName = entity.getPreamble().getName();
87 EntityBrowser<Action.Param> paramBrowser = new EntityBrowser<>(format(
88 "param for action '%s'", actionName));
Carmelo Cascone8d99b172017-07-18 17:26:31 -040089 entity.getParamsList().forEach(p -> paramBrowser.add(p.getName(), null, p.getId(), p));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040090 actionParams.put(actionId, paramBrowser);
91 });
92
93 p4info.getActionProfilesList().forEach(
94 entity -> actionProfiles.addWithPreamble(entity.getPreamble(), entity));
95
96 p4info.getCountersList().forEach(
97 entity -> counters.addWithPreamble(entity.getPreamble(), entity));
98
99 p4info.getDirectCountersList().forEach(
100 entity -> directCounters.addWithPreamble(entity.getPreamble(), entity));
101
102 p4info.getMetersList().forEach(
103 entity -> meters.addWithPreamble(entity.getPreamble(), entity));
104
105 p4info.getDirectMetersList().forEach(
106 entity -> directMeters.addWithPreamble(entity.getPreamble(), entity));
107
108 p4info.getControllerPacketMetadataList().forEach(
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200109 entity -> {
110 ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity);
111 // Index control packet metadata metadata.
112 int ctrlPktMetadataId = entity.getPreamble().getId();
113 String ctrlPktMetadataName = entity.getPreamble().getName();
114 EntityBrowser<ControllerPacketMetadata.Metadata> metadataBrowser = new EntityBrowser<>(format(
115 "metadata field for controller packet metadata '%s'", ctrlPktMetadataName));
116 entity.getMetadataList().forEach(m -> metadataBrowser.add(m.getName(), null, m.getId(), m));
117 ctrlPktMetadatasMetadata.put(ctrlPktMetadataId, metadataBrowser);
118 });
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400119 }
120
121 /**
122 * Returns a browser for tables.
123 *
124 * @return table browser
125 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800126 public EntityBrowser<Table> tables() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400127 return tables;
128 }
129
130 /**
131 * Returns a browser for actions.
132 *
133 * @return action browser
134 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800135 public EntityBrowser<Action> actions() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400136 return actions;
137 }
138
139 /**
140 * Returns a browser for action profiles.
141 *
142 * @return action profile browser
143 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800144 public EntityBrowser<ActionProfile> actionProfiles() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400145 return actionProfiles;
146 }
147
148 /**
149 * Returns a browser for counters.
150 *
151 * @return counter browser
152 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800153 public EntityBrowser<Counter> counters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400154 return counters;
155 }
156
157 /**
158 * Returns a browser for direct counters.
159 *
160 * @return direct counter browser
161 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800162 public EntityBrowser<DirectCounter> directCounters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400163 return directCounters;
164 }
165
166 /**
167 * Returns a browser for meters.
168 *
169 * @return meter browser
170 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800171 public EntityBrowser<Meter> meters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400172 return meters;
173 }
174
175 /**
176 * Returns a browser for direct meters.
177 *
178 * @return table browser
179 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800180 public EntityBrowser<DirectMeter> directMeters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400181 return directMeters;
182 }
183
184 /**
185 * Returns a browser for controller packet metadata.
186 *
187 * @return controller packet metadata browser
188 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800189 public EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400190 return ctrlPktMetadatas;
191 }
192
193 /**
194 * Returns a browser for params of the given action.
195 *
196 * @param actionId action identifier
197 * @return action params browser
198 * @throws NotFoundException if the action cannot be found
199 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800200 public EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400201 // Throws exception if action id is not found.
202 actions.getById(actionId);
203 return actionParams.get(actionId);
204 }
205
206 /**
207 * Returns a browser for match fields of the given table.
208 *
209 * @param tableId table identifier
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200210 * @return match field browser
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400211 * @throws NotFoundException if the table cannot be found
212 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800213 public EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400214 // Throws exception if action id is not found.
215 tables.getById(tableId);
216 return matchFields.get(tableId);
217 }
218
219 /**
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200220 * Returns a browser for metadata fields of the controller packet metadata.
221 *
222 * @param controllerPacketMetadataId controller packet metadata identifier
223 * @return metadata browser
Frank Wangd8ab0962017-08-11 11:09:30 +0800224 * @throws NotFoundException controller packet metadata cannot be found
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200225 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800226 public EntityBrowser<ControllerPacketMetadata.Metadata> packetMetadatas(int controllerPacketMetadataId)
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200227 throws NotFoundException {
228 // Throws exception if controller packet metadata id is not found.
229 ctrlPktMetadatas.getById(controllerPacketMetadataId);
230 return ctrlPktMetadatasMetadata.get(controllerPacketMetadataId);
231 }
232
233 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400234 * Browser of P4Info entities.
235 *
236 * @param <T> protobuf message type
237 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800238 public static final class EntityBrowser<T extends Message> {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400239
240 private String entityName;
241 private final Map<String, T> names = Maps.newHashMap();
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200242 private final Map<String, String> aliasToNames = Maps.newHashMap();
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400243 private final Map<Integer, T> ids = Maps.newHashMap();
244
245 private EntityBrowser(String entityName) {
246 this.entityName = entityName;
247 }
248
249 /**
Carmelo Cascone04d09392017-07-19 08:49:08 -0400250 * Adds the given entity identified by the given name, alias (nullable) and id.
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400251 *
252 * @param name entity name
Carmelo Cascone04d09392017-07-19 08:49:08 -0400253 * @param alias entity alias or null
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400254 * @param id entity id
255 * @param entity entity message
256 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800257 private void add(String name, String alias, int id, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400258 checkNotNull(name);
259 checkArgument(!name.isEmpty(), "Name cannot be empty");
260 checkNotNull(entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400261 names.put(name, entity);
262 ids.put(id, entity);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400263 if (alias != null && !alias.isEmpty()) {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200264 aliasToNames.put(alias, name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400265 }
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400266 }
267
268 /**
269 * Adds the given entity identified by the given P4Info preamble.
270 *
271 * @param preamble P4Info preamble protobuf message
272 * @param entity entity message
273 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800274 private void addWithPreamble(Preamble preamble, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400275 checkNotNull(preamble);
276 add(preamble.getName(), preamble.getAlias(), preamble.getId(), entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400277 }
278
279 /**
280 * Returns true if the P4Info defines an entity with such name, false otherwise.
281 *
282 * @param name entity name
283 * @return boolean
284 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800285 public boolean hasName(String name) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400286 return names.containsKey(name);
287 }
288
289 /**
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200290 * Returns the entity identified by the given name, if present, otherwise, throws an exception.
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400291 *
292 * @param name entity name or alias
293 * @return entity message
294 * @throws NotFoundException if the entity cannot be found
295 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800296 public T getByName(String name) throws NotFoundException {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400297 if (hasName(name)) {
298 return names.get(name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400299 } else {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200300 final String hint = aliasToNames.containsKey(name)
301 ? format("Did you mean '%s'? Make sure to use entity names in PI IDs, not aliases",
302 aliasToNames.get(name))
303 : "";
304 throw new NotFoundException(entityName, name, hint);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400305 }
306 }
307
308 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400309 * Returns true if the P4Info defines an entity with such id, false otherwise.
310 *
311 * @param id entity id
312 * @return boolean
313 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800314 public boolean hasId(int id) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400315 return ids.containsKey(id);
316 }
317
318 /**
319 * Returns the entity identified by the given id, if present, otherwise, throws an exception.
320 *
321 * @param id entity id
322 * @return entity message
323 * @throws NotFoundException if the entity cannot be found
324 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800325 public T getById(int id) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400326 if (!hasId(id)) {
327 throw new NotFoundException(entityName, id);
328 }
329 return ids.get(id);
330 }
331 }
332
333 /**
334 * Signals tha an entity cannot be found in the P4Info.
335 */
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400336 public static final class NotFoundException extends Exception {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400337
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800338 public NotFoundException(String entityName, String key, String hint) {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200339 super(format(
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800340 "No such %s in P4Info with name '%s'%s",
341 entityName, key, hint.isEmpty() ? "" : " (" + hint + ")"));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400342 }
343
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800344 public NotFoundException(String entityName, int id) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400345 super(format("No such %s in P4Info with id '%d'", entityName, id));
346 }
347 }
348}