blob: b24bc701e285195b947a4fd142d2db4848e0cd08 [file] [log] [blame]
Carmelo Cascone98dfdb72017-07-14 12:01:37 -04001/*
2 * Copyright 2017-present Open Networking Laboratory
3 *
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
17package org.onosproject.p4runtime.ctl;
18
19
20import com.google.common.collect.Maps;
21import com.google.protobuf.Message;
22import p4.config.P4InfoOuterClass.Action;
23import p4.config.P4InfoOuterClass.ActionProfile;
24import p4.config.P4InfoOuterClass.ControllerPacketMetadata;
25import p4.config.P4InfoOuterClass.Counter;
26import p4.config.P4InfoOuterClass.DirectCounter;
27import p4.config.P4InfoOuterClass.DirectMeter;
28import p4.config.P4InfoOuterClass.MatchField;
29import p4.config.P4InfoOuterClass.Meter;
30import p4.config.P4InfoOuterClass.P4Info;
31import p4.config.P4InfoOuterClass.Preamble;
32import p4.config.P4InfoOuterClass.Table;
33
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 */
43final class P4InfoBrowser {
44
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();
56
57 /**
58 * Creates a new browser for the given P4Info.
59 *
60 * @param p4info P4Info protobuf message
61 */
62 P4InfoBrowser(P4Info p4info) {
63 parseP4Info(p4info);
64 }
65
66 private void parseP4Info(P4Info p4info) {
67 p4info.getTablesList().forEach(
68 entity -> {
69 tables.addWithPreamble(entity.getPreamble(), entity);
70 // Index match fields.
71 int tableId = entity.getPreamble().getId();
72 String tableName = entity.getPreamble().getName();
73 EntityBrowser<MatchField> matchFieldBrowser = new EntityBrowser<>(format(
74 "match field for table '%s'", tableName));
Carmelo Cascone8d99b172017-07-18 17:26:31 -040075 entity.getMatchFieldsList().forEach(m -> {
76 String alias = extractMatchFieldSimpleName(m.getName());
77 matchFieldBrowser.add(m.getName(), alias, m.getId(), m);
78 });
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040079 matchFields.put(tableId, matchFieldBrowser);
80 });
81
82 p4info.getActionsList().forEach(
83 entity -> {
84 actions.addWithPreamble(entity.getPreamble(), entity);
85 // Index action params.
86 int actionId = entity.getPreamble().getId();
87 String actionName = entity.getPreamble().getName();
88 EntityBrowser<Action.Param> paramBrowser = new EntityBrowser<>(format(
89 "param for action '%s'", actionName));
Carmelo Cascone8d99b172017-07-18 17:26:31 -040090 entity.getParamsList().forEach(p -> paramBrowser.add(p.getName(), null, p.getId(), p));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040091 actionParams.put(actionId, paramBrowser);
92 });
93
94 p4info.getActionProfilesList().forEach(
95 entity -> actionProfiles.addWithPreamble(entity.getPreamble(), entity));
96
97 p4info.getCountersList().forEach(
98 entity -> counters.addWithPreamble(entity.getPreamble(), entity));
99
100 p4info.getDirectCountersList().forEach(
101 entity -> directCounters.addWithPreamble(entity.getPreamble(), entity));
102
103 p4info.getMetersList().forEach(
104 entity -> meters.addWithPreamble(entity.getPreamble(), entity));
105
106 p4info.getDirectMetersList().forEach(
107 entity -> directMeters.addWithPreamble(entity.getPreamble(), entity));
108
109 p4info.getControllerPacketMetadataList().forEach(
110 entity -> ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity));
111 }
112
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400113 private String extractMatchFieldSimpleName(String name) {
114 // Removes the leading "hdr." or other scope identifier.
115 // E.g.: "hdr.ethernet.etherType" becomes "ethernet.etherType"
116 String[] pieces = name.split("\\.");
117 if (pieces.length == 3) {
118 return pieces[1] + "." + pieces[2];
119 } else if (pieces.length == 2) {
120 return name;
121 } else {
122 throw new UnsupportedOperationException("Invalid match field name: " + name);
123 }
124 }
125
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400126 /**
127 * Returns a browser for tables.
128 *
129 * @return table browser
130 */
131 EntityBrowser<Table> tables() {
132 return tables;
133 }
134
135 /**
136 * Returns a browser for actions.
137 *
138 * @return action browser
139 */
140 EntityBrowser<Action> actions() {
141 return actions;
142 }
143
144 /**
145 * Returns a browser for action profiles.
146 *
147 * @return action profile browser
148 */
149 EntityBrowser<ActionProfile> actionProfiles() {
150 return actionProfiles;
151 }
152
153 /**
154 * Returns a browser for counters.
155 *
156 * @return counter browser
157 */
158 EntityBrowser<Counter> counters() {
159 return counters;
160 }
161
162 /**
163 * Returns a browser for direct counters.
164 *
165 * @return direct counter browser
166 */
167 EntityBrowser<DirectCounter> directCounters() {
168 return directCounters;
169 }
170
171 /**
172 * Returns a browser for meters.
173 *
174 * @return meter browser
175 */
176 EntityBrowser<Meter> meters() {
177 return meters;
178 }
179
180 /**
181 * Returns a browser for direct meters.
182 *
183 * @return table browser
184 */
185 EntityBrowser<DirectMeter> directMeters() {
186 return directMeters;
187 }
188
189 /**
190 * Returns a browser for controller packet metadata.
191 *
192 * @return controller packet metadata browser
193 */
194 EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
195 return ctrlPktMetadatas;
196 }
197
198 /**
199 * Returns a browser for params of the given action.
200 *
201 * @param actionId action identifier
202 * @return action params browser
203 * @throws NotFoundException if the action cannot be found
204 */
205 EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
206 // Throws exception if action id is not found.
207 actions.getById(actionId);
208 return actionParams.get(actionId);
209 }
210
211 /**
212 * Returns a browser for match fields of the given table.
213 *
214 * @param tableId table identifier
215 * @return controller packet metadata browser
216 * @throws NotFoundException if the table cannot be found
217 */
218 EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
219 // Throws exception if action id is not found.
220 tables.getById(tableId);
221 return matchFields.get(tableId);
222 }
223
224 /**
225 * Browser of P4Info entities.
226 *
227 * @param <T> protobuf message type
228 */
229 static final class EntityBrowser<T extends Message> {
230
231 private String entityName;
232 private final Map<String, T> names = Maps.newHashMap();
233 private final Map<String, T> aliases = Maps.newHashMap();
234 private final Map<Integer, T> ids = Maps.newHashMap();
235
236 private EntityBrowser(String entityName) {
237 this.entityName = entityName;
238 }
239
240 /**
Carmelo Cascone04d09392017-07-19 08:49:08 -0400241 * Adds the given entity identified by the given name, alias (nullable) and id.
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400242 *
243 * @param name entity name
Carmelo Cascone04d09392017-07-19 08:49:08 -0400244 * @param alias entity alias or null
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400245 * @param id entity id
246 * @param entity entity message
247 */
Carmelo Cascone04d09392017-07-19 08:49:08 -0400248 void add(String name, String alias, int id, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400249 checkNotNull(name);
250 checkArgument(!name.isEmpty(), "Name cannot be empty");
251 checkNotNull(entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400252 names.put(name, entity);
253 ids.put(id, entity);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400254 if (alias != null && !alias.isEmpty()) {
255 aliases.put(alias, entity);
256 }
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400257 }
258
259 /**
260 * Adds the given entity identified by the given P4Info preamble.
261 *
262 * @param preamble P4Info preamble protobuf message
263 * @param entity entity message
264 */
265 void addWithPreamble(Preamble preamble, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400266 checkNotNull(preamble);
267 add(preamble.getName(), preamble.getAlias(), preamble.getId(), entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400268 }
269
270 /**
271 * Returns true if the P4Info defines an entity with such name, false otherwise.
272 *
273 * @param name entity name
274 * @return boolean
275 */
276 boolean hasName(String name) {
277 return names.containsKey(name);
278 }
279
280 /**
281 * Returns the entity identified by the given name, if present, otherwise, throws an exception.
282 *
283 * @param name entity name
284 * @return entity message
285 * @throws NotFoundException if the entity cannot be found
286 */
287 T getByName(String name) throws NotFoundException {
288 if (!hasName(name)) {
289 throw new NotFoundException(entityName, name);
290 }
291 return names.get(name);
292 }
293
294 /**
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400295 * Returns the entity identified by the given name or alias, if present, otherwise, throws an exception.
296 *
297 * @param name entity name or alias
298 * @return entity message
299 * @throws NotFoundException if the entity cannot be found
300 */
301 T getByNameOrAlias(String name) throws NotFoundException {
302 if (hasName(name)) {
303 return names.get(name);
304 } else if (hasAlias(name)) {
305 return aliases.get(name);
306 } else {
307 throw new NotFoundException(entityName, name);
308 }
309 }
310
311 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400312 * Returns true if the P4Info defines an entity with such alias, false otherwise.
313 *
314 * @param alias entity alias
315 * @return boolean
316 */
317 boolean hasAlias(String alias) {
318 return aliases.containsKey(alias);
319 }
320
321 /**
322 * Returns the entity identified by the given alias, if present, otherwise, throws an exception.
323 *
324 * @param alias entity alias
325 * @return entity message
326 * @throws NotFoundException if the entity cannot be found
327 */
328 T getByAlias(String alias) throws NotFoundException {
329 if (!hasName(alias)) {
330 throw new NotFoundException(entityName, alias);
331 }
332 return aliases.get(alias);
333 }
334
335 /**
336 * Returns true if the P4Info defines an entity with such id, false otherwise.
337 *
338 * @param id entity id
339 * @return boolean
340 */
341 boolean hasId(int id) {
342 return ids.containsKey(id);
343 }
344
345 /**
346 * Returns the entity identified by the given id, if present, otherwise, throws an exception.
347 *
348 * @param id entity id
349 * @return entity message
350 * @throws NotFoundException if the entity cannot be found
351 */
352 T getById(int id) throws NotFoundException {
353 if (!hasId(id)) {
354 throw new NotFoundException(entityName, id);
355 }
356 return ids.get(id);
357 }
358 }
359
360 /**
361 * Signals tha an entity cannot be found in the P4Info.
362 */
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400363 public static final class NotFoundException extends Exception {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400364
365 NotFoundException(String entityName, String key) {
366 super(format("No such %s in P4Info with name/alias '%s'", entityName, key));
367 }
368
369 NotFoundException(String entityName, int id) {
370 super(format("No such %s in P4Info with id '%d'", entityName, id));
371 }
372 }
373}