blob: 3c27468d8d1405c0d186d929615ca7c64569c662 [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
Carmelo Cascone8d99b172017-07-18 17:26:31 -040034import javax.annotation.Nullable;
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040035import 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 */
44final class P4InfoBrowser {
45
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();
57
58 /**
59 * Creates a new browser for the given P4Info.
60 *
61 * @param p4info P4Info protobuf message
62 */
63 P4InfoBrowser(P4Info p4info) {
64 parseP4Info(p4info);
65 }
66
67 private void parseP4Info(P4Info p4info) {
68 p4info.getTablesList().forEach(
69 entity -> {
70 tables.addWithPreamble(entity.getPreamble(), entity);
71 // Index match fields.
72 int tableId = entity.getPreamble().getId();
73 String tableName = entity.getPreamble().getName();
74 EntityBrowser<MatchField> matchFieldBrowser = new EntityBrowser<>(format(
75 "match field for table '%s'", tableName));
Carmelo Cascone8d99b172017-07-18 17:26:31 -040076 entity.getMatchFieldsList().forEach(m -> {
77 String alias = extractMatchFieldSimpleName(m.getName());
78 matchFieldBrowser.add(m.getName(), alias, m.getId(), m);
79 });
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(
111 entity -> ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity));
112 }
113
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400114 private String extractMatchFieldSimpleName(String name) {
115 // Removes the leading "hdr." or other scope identifier.
116 // E.g.: "hdr.ethernet.etherType" becomes "ethernet.etherType"
117 String[] pieces = name.split("\\.");
118 if (pieces.length == 3) {
119 return pieces[1] + "." + pieces[2];
120 } else if (pieces.length == 2) {
121 return name;
122 } else {
123 throw new UnsupportedOperationException("Invalid match field name: " + name);
124 }
125 }
126
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400127 /**
128 * Returns a browser for tables.
129 *
130 * @return table browser
131 */
132 EntityBrowser<Table> tables() {
133 return tables;
134 }
135
136 /**
137 * Returns a browser for actions.
138 *
139 * @return action browser
140 */
141 EntityBrowser<Action> actions() {
142 return actions;
143 }
144
145 /**
146 * Returns a browser for action profiles.
147 *
148 * @return action profile browser
149 */
150 EntityBrowser<ActionProfile> actionProfiles() {
151 return actionProfiles;
152 }
153
154 /**
155 * Returns a browser for counters.
156 *
157 * @return counter browser
158 */
159 EntityBrowser<Counter> counters() {
160 return counters;
161 }
162
163 /**
164 * Returns a browser for direct counters.
165 *
166 * @return direct counter browser
167 */
168 EntityBrowser<DirectCounter> directCounters() {
169 return directCounters;
170 }
171
172 /**
173 * Returns a browser for meters.
174 *
175 * @return meter browser
176 */
177 EntityBrowser<Meter> meters() {
178 return meters;
179 }
180
181 /**
182 * Returns a browser for direct meters.
183 *
184 * @return table browser
185 */
186 EntityBrowser<DirectMeter> directMeters() {
187 return directMeters;
188 }
189
190 /**
191 * Returns a browser for controller packet metadata.
192 *
193 * @return controller packet metadata browser
194 */
195 EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
196 return ctrlPktMetadatas;
197 }
198
199 /**
200 * Returns a browser for params of the given action.
201 *
202 * @param actionId action identifier
203 * @return action params browser
204 * @throws NotFoundException if the action cannot be found
205 */
206 EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
207 // Throws exception if action id is not found.
208 actions.getById(actionId);
209 return actionParams.get(actionId);
210 }
211
212 /**
213 * Returns a browser for match fields of the given table.
214 *
215 * @param tableId table identifier
216 * @return controller packet metadata browser
217 * @throws NotFoundException if the table cannot be found
218 */
219 EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
220 // Throws exception if action id is not found.
221 tables.getById(tableId);
222 return matchFields.get(tableId);
223 }
224
225 /**
226 * Browser of P4Info entities.
227 *
228 * @param <T> protobuf message type
229 */
230 static final class EntityBrowser<T extends Message> {
231
232 private String entityName;
233 private final Map<String, T> names = Maps.newHashMap();
234 private final Map<String, T> aliases = Maps.newHashMap();
235 private final Map<Integer, T> ids = Maps.newHashMap();
236
237 private EntityBrowser(String entityName) {
238 this.entityName = entityName;
239 }
240
241 /**
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400242 * Adds the given entity identified by the given name, alias and id.
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400243 *
244 * @param name entity name
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400245 * @param alias entity alias
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400246 * @param id entity id
247 * @param entity entity message
248 */
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400249 void add(String name, @Nullable String alias, int id, T entity) {
250 checkNotNull(name);
251 checkArgument(!name.isEmpty(), "Name cannot be empty");
252 checkNotNull(entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400253 names.put(name, entity);
254 ids.put(id, entity);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400255 if (alias != null && !alias.isEmpty()) {
256 aliases.put(alias, entity);
257 }
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400258 }
259
260 /**
261 * Adds the given entity identified by the given P4Info preamble.
262 *
263 * @param preamble P4Info preamble protobuf message
264 * @param entity entity message
265 */
266 void addWithPreamble(Preamble preamble, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400267 checkNotNull(preamble);
268 add(preamble.getName(), preamble.getAlias(), preamble.getId(), entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400269 }
270
271 /**
272 * Returns true if the P4Info defines an entity with such name, false otherwise.
273 *
274 * @param name entity name
275 * @return boolean
276 */
277 boolean hasName(String name) {
278 return names.containsKey(name);
279 }
280
281 /**
282 * Returns the entity identified by the given name, if present, otherwise, throws an exception.
283 *
284 * @param name entity name
285 * @return entity message
286 * @throws NotFoundException if the entity cannot be found
287 */
288 T getByName(String name) throws NotFoundException {
289 if (!hasName(name)) {
290 throw new NotFoundException(entityName, name);
291 }
292 return names.get(name);
293 }
294
295 /**
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400296 * Returns the entity identified by the given name or alias, if present, otherwise, throws an exception.
297 *
298 * @param name entity name or alias
299 * @return entity message
300 * @throws NotFoundException if the entity cannot be found
301 */
302 T getByNameOrAlias(String name) throws NotFoundException {
303 if (hasName(name)) {
304 return names.get(name);
305 } else if (hasAlias(name)) {
306 return aliases.get(name);
307 } else {
308 throw new NotFoundException(entityName, name);
309 }
310 }
311
312 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400313 * Returns true if the P4Info defines an entity with such alias, false otherwise.
314 *
315 * @param alias entity alias
316 * @return boolean
317 */
318 boolean hasAlias(String alias) {
319 return aliases.containsKey(alias);
320 }
321
322 /**
323 * Returns the entity identified by the given alias, if present, otherwise, throws an exception.
324 *
325 * @param alias entity alias
326 * @return entity message
327 * @throws NotFoundException if the entity cannot be found
328 */
329 T getByAlias(String alias) throws NotFoundException {
330 if (!hasName(alias)) {
331 throw new NotFoundException(entityName, alias);
332 }
333 return aliases.get(alias);
334 }
335
336 /**
337 * Returns true if the P4Info defines an entity with such id, false otherwise.
338 *
339 * @param id entity id
340 * @return boolean
341 */
342 boolean hasId(int id) {
343 return ids.containsKey(id);
344 }
345
346 /**
347 * Returns the entity identified by the given id, if present, otherwise, throws an exception.
348 *
349 * @param id entity id
350 * @return entity message
351 * @throws NotFoundException if the entity cannot be found
352 */
353 T getById(int id) throws NotFoundException {
354 if (!hasId(id)) {
355 throw new NotFoundException(entityName, id);
356 }
357 return ids.get(id);
358 }
359 }
360
361 /**
362 * Signals tha an entity cannot be found in the P4Info.
363 */
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400364 public static final class NotFoundException extends Exception {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400365
366 NotFoundException(String entityName, String key) {
367 super(format("No such %s in P4Info with name/alias '%s'", entityName, key));
368 }
369
370 NotFoundException(String entityName, int id) {
371 super(format("No such %s in P4Info with id '%d'", entityName, id));
372 }
373 }
374}