blob: 8504202b101eb8b6796b7ca42c2640fea1c5f1df [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 Cascone08be2552021-01-28 17:25:16 -080033import p4.config.v1.P4InfoOuterClass.Digest;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040034
35import java.util.Map;
36
Carmelo Cascone8d99b172017-07-18 17:26:31 -040037import static com.google.common.base.Preconditions.checkArgument;
38import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040039import static java.lang.String.format;
40
41/**
42 * Utility class to easily retrieve information from a P4Info protobuf message.
43 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -080044public final class P4InfoBrowser {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040045
46 private final EntityBrowser<Table> tables = new EntityBrowser<>("table");
47 private final EntityBrowser<Action> actions = new EntityBrowser<>("action");
48 private final EntityBrowser<ActionProfile> actionProfiles = new EntityBrowser<>("action profile");
49 private final EntityBrowser<Counter> counters = new EntityBrowser<>("counter");
50 private final EntityBrowser<DirectCounter> directCounters = new EntityBrowser<>("direct counter");
51 private final EntityBrowser<Meter> meters = new EntityBrowser<>("meter");
52 private final EntityBrowser<DirectMeter> directMeters = new EntityBrowser<>("direct meter");
53 private final EntityBrowser<ControllerPacketMetadata> ctrlPktMetadatas =
54 new EntityBrowser<>("controller packet metadata");
55 private final Map<Integer, EntityBrowser<Action.Param>> actionParams = Maps.newHashMap();
56 private final Map<Integer, EntityBrowser<MatchField>> matchFields = Maps.newHashMap();
Andrea Campanellafc1d34c2017-07-18 17:01:41 +020057 private final Map<Integer, EntityBrowser<ControllerPacketMetadata.Metadata>> ctrlPktMetadatasMetadata =
58 Maps.newHashMap();
Carmelo Cascone08be2552021-01-28 17:25:16 -080059 private final EntityBrowser<Digest> digests = new EntityBrowser<>("digest");
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040060
61 /**
62 * Creates a new browser for the given P4Info.
63 *
64 * @param p4info P4Info protobuf message
65 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -080066 public P4InfoBrowser(P4Info p4info) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040067 parseP4Info(p4info);
68 }
69
70 private void parseP4Info(P4Info p4info) {
71 p4info.getTablesList().forEach(
72 entity -> {
73 tables.addWithPreamble(entity.getPreamble(), entity);
74 // Index match fields.
75 int tableId = entity.getPreamble().getId();
76 String tableName = entity.getPreamble().getName();
77 EntityBrowser<MatchField> matchFieldBrowser = new EntityBrowser<>(format(
78 "match field for table '%s'", tableName));
Carmelo Cascone87892e22017-11-13 16:01:29 -080079 entity.getMatchFieldsList().forEach(m -> matchFieldBrowser.add(m.getName(), null, m.getId(), m));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040080 matchFields.put(tableId, matchFieldBrowser);
81 });
82
83 p4info.getActionsList().forEach(
84 entity -> {
85 actions.addWithPreamble(entity.getPreamble(), entity);
86 // Index action params.
87 int actionId = entity.getPreamble().getId();
88 String actionName = entity.getPreamble().getName();
89 EntityBrowser<Action.Param> paramBrowser = new EntityBrowser<>(format(
90 "param for action '%s'", actionName));
Carmelo Cascone8d99b172017-07-18 17:26:31 -040091 entity.getParamsList().forEach(p -> paramBrowser.add(p.getName(), null, p.getId(), p));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040092 actionParams.put(actionId, paramBrowser);
93 });
94
95 p4info.getActionProfilesList().forEach(
96 entity -> actionProfiles.addWithPreamble(entity.getPreamble(), entity));
97
98 p4info.getCountersList().forEach(
99 entity -> counters.addWithPreamble(entity.getPreamble(), entity));
100
101 p4info.getDirectCountersList().forEach(
102 entity -> directCounters.addWithPreamble(entity.getPreamble(), entity));
103
104 p4info.getMetersList().forEach(
105 entity -> meters.addWithPreamble(entity.getPreamble(), entity));
106
107 p4info.getDirectMetersList().forEach(
108 entity -> directMeters.addWithPreamble(entity.getPreamble(), entity));
109
110 p4info.getControllerPacketMetadataList().forEach(
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200111 entity -> {
112 ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity);
113 // Index control packet metadata metadata.
114 int ctrlPktMetadataId = entity.getPreamble().getId();
115 String ctrlPktMetadataName = entity.getPreamble().getName();
116 EntityBrowser<ControllerPacketMetadata.Metadata> metadataBrowser = new EntityBrowser<>(format(
117 "metadata field for controller packet metadata '%s'", ctrlPktMetadataName));
118 entity.getMetadataList().forEach(m -> metadataBrowser.add(m.getName(), null, m.getId(), m));
119 ctrlPktMetadatasMetadata.put(ctrlPktMetadataId, metadataBrowser);
120 });
Carmelo Cascone08be2552021-01-28 17:25:16 -0800121
122 p4info.getDigestsList().forEach(
123 entity -> digests.addWithPreamble(entity.getPreamble(), entity));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400124 }
125
126 /**
127 * Returns a browser for tables.
128 *
129 * @return table browser
130 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800131 public EntityBrowser<Table> tables() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400132 return tables;
133 }
134
135 /**
136 * Returns a browser for actions.
137 *
138 * @return action browser
139 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800140 public EntityBrowser<Action> actions() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400141 return actions;
142 }
143
144 /**
145 * Returns a browser for action profiles.
146 *
147 * @return action profile browser
148 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800149 public EntityBrowser<ActionProfile> actionProfiles() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400150 return actionProfiles;
151 }
152
153 /**
154 * Returns a browser for counters.
155 *
156 * @return counter browser
157 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800158 public EntityBrowser<Counter> counters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400159 return counters;
160 }
161
162 /**
163 * Returns a browser for direct counters.
164 *
165 * @return direct counter browser
166 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800167 public EntityBrowser<DirectCounter> directCounters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400168 return directCounters;
169 }
170
171 /**
172 * Returns a browser for meters.
173 *
174 * @return meter browser
175 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800176 public EntityBrowser<Meter> meters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400177 return meters;
178 }
179
180 /**
181 * Returns a browser for direct meters.
182 *
183 * @return table browser
184 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800185 public EntityBrowser<DirectMeter> directMeters() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400186 return directMeters;
187 }
188
189 /**
190 * Returns a browser for controller packet metadata.
191 *
192 * @return controller packet metadata browser
193 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800194 public EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400195 return ctrlPktMetadatas;
196 }
197
198 /**
Carmelo Cascone08be2552021-01-28 17:25:16 -0800199 * Returns a browser for digests.
200 *
201 * @return digest browser
202 */
203 public EntityBrowser<Digest> digests() {
204 return digests;
205 }
206
207 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400208 * Returns a browser for params of the given action.
209 *
210 * @param actionId action identifier
211 * @return action params browser
212 * @throws NotFoundException if the action cannot be found
213 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800214 public EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400215 // Throws exception if action id is not found.
216 actions.getById(actionId);
217 return actionParams.get(actionId);
218 }
219
220 /**
221 * Returns a browser for match fields of the given table.
222 *
223 * @param tableId table identifier
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200224 * @return match field browser
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400225 * @throws NotFoundException if the table cannot be found
226 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800227 public EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400228 // Throws exception if action id is not found.
229 tables.getById(tableId);
230 return matchFields.get(tableId);
231 }
232
233 /**
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200234 * Returns a browser for metadata fields of the controller packet metadata.
235 *
236 * @param controllerPacketMetadataId controller packet metadata identifier
237 * @return metadata browser
Frank Wangd8ab0962017-08-11 11:09:30 +0800238 * @throws NotFoundException controller packet metadata cannot be found
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200239 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800240 public EntityBrowser<ControllerPacketMetadata.Metadata> packetMetadatas(int controllerPacketMetadataId)
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200241 throws NotFoundException {
242 // Throws exception if controller packet metadata id is not found.
243 ctrlPktMetadatas.getById(controllerPacketMetadataId);
244 return ctrlPktMetadatasMetadata.get(controllerPacketMetadataId);
245 }
246
247 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400248 * Browser of P4Info entities.
249 *
250 * @param <T> protobuf message type
251 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800252 public static final class EntityBrowser<T extends Message> {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400253
254 private String entityName;
255 private final Map<String, T> names = Maps.newHashMap();
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200256 private final Map<String, String> aliasToNames = Maps.newHashMap();
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400257 private final Map<Integer, T> ids = Maps.newHashMap();
258
259 private EntityBrowser(String entityName) {
260 this.entityName = entityName;
261 }
262
263 /**
Carmelo Cascone04d09392017-07-19 08:49:08 -0400264 * Adds the given entity identified by the given name, alias (nullable) and id.
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400265 *
266 * @param name entity name
Carmelo Cascone04d09392017-07-19 08:49:08 -0400267 * @param alias entity alias or null
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400268 * @param id entity id
269 * @param entity entity message
270 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800271 private void add(String name, String alias, int id, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400272 checkNotNull(name);
273 checkArgument(!name.isEmpty(), "Name cannot be empty");
274 checkNotNull(entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400275 names.put(name, entity);
276 ids.put(id, entity);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400277 if (alias != null && !alias.isEmpty()) {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200278 aliasToNames.put(alias, name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400279 }
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400280 }
281
282 /**
283 * Adds the given entity identified by the given P4Info preamble.
284 *
285 * @param preamble P4Info preamble protobuf message
286 * @param entity entity message
287 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800288 private void addWithPreamble(Preamble preamble, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400289 checkNotNull(preamble);
290 add(preamble.getName(), preamble.getAlias(), preamble.getId(), entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400291 }
292
293 /**
294 * Returns true if the P4Info defines an entity with such name, false otherwise.
295 *
296 * @param name entity name
297 * @return boolean
298 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800299 public boolean hasName(String name) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400300 return names.containsKey(name);
301 }
302
303 /**
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200304 * Returns the entity identified by the given name, if present, otherwise, throws an exception.
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400305 *
306 * @param name entity name or alias
307 * @return entity message
308 * @throws NotFoundException if the entity cannot be found
309 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800310 public T getByName(String name) throws NotFoundException {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400311 if (hasName(name)) {
312 return names.get(name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400313 } else {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200314 final String hint = aliasToNames.containsKey(name)
315 ? format("Did you mean '%s'? Make sure to use entity names in PI IDs, not aliases",
316 aliasToNames.get(name))
317 : "";
318 throw new NotFoundException(entityName, name, hint);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400319 }
320 }
321
322 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400323 * Returns true if the P4Info defines an entity with such id, false otherwise.
324 *
325 * @param id entity id
326 * @return boolean
327 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800328 public boolean hasId(int id) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400329 return ids.containsKey(id);
330 }
331
332 /**
333 * Returns the entity identified by the given id, if present, otherwise, throws an exception.
334 *
335 * @param id entity id
336 * @return entity message
337 * @throws NotFoundException if the entity cannot be found
338 */
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800339 public T getById(int id) throws NotFoundException {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400340 if (!hasId(id)) {
341 throw new NotFoundException(entityName, id);
342 }
343 return ids.get(id);
344 }
345 }
346
347 /**
348 * Signals tha an entity cannot be found in the P4Info.
349 */
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400350 public static final class NotFoundException extends Exception {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400351
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800352 public NotFoundException(String entityName, String key, String hint) {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200353 super(format(
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800354 "No such %s in P4Info with name '%s'%s",
355 entityName, key, hint.isEmpty() ? "" : " (" + hint + ")"));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400356 }
357
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800358 public NotFoundException(String entityName, int id) {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400359 super(format("No such %s in P4Info with id '%d'", entityName, id));
360 }
361 }
362}