blob: 48b14769326d806c9c39c7842e82da68625a85c9 [file] [log] [blame]
Carmelo Cascone98dfdb72017-07-14 12:01:37 -04001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-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
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();
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 */
64 P4InfoBrowser(P4Info p4info) {
65 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 Cascone8d99b172017-07-18 17:26:31 -040077 entity.getMatchFieldsList().forEach(m -> {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +020078 // FIXME: nasty hack needed to provide compatibility with BMv2-based pipeline models.
79 // Indeed in the BMv2 JSON header fields have format like "ethernet.srd_addr", while in P4Info
80 // the same will be "hdr.ethernet.srd_addr".
81 // To be removed when ONOS-7066 will be implemented.
82 String simpleName = extractMatchFieldSimpleName(m.getName());
83 matchFieldBrowser.add(simpleName, null, m.getId(), m);
Carmelo Cascone8d99b172017-07-18 17:26:31 -040084 });
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040085 matchFields.put(tableId, matchFieldBrowser);
86 });
87
88 p4info.getActionsList().forEach(
89 entity -> {
90 actions.addWithPreamble(entity.getPreamble(), entity);
91 // Index action params.
92 int actionId = entity.getPreamble().getId();
93 String actionName = entity.getPreamble().getName();
94 EntityBrowser<Action.Param> paramBrowser = new EntityBrowser<>(format(
95 "param for action '%s'", actionName));
Carmelo Cascone8d99b172017-07-18 17:26:31 -040096 entity.getParamsList().forEach(p -> paramBrowser.add(p.getName(), null, p.getId(), p));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -040097 actionParams.put(actionId, paramBrowser);
98 });
99
100 p4info.getActionProfilesList().forEach(
101 entity -> actionProfiles.addWithPreamble(entity.getPreamble(), entity));
102
103 p4info.getCountersList().forEach(
104 entity -> counters.addWithPreamble(entity.getPreamble(), entity));
105
106 p4info.getDirectCountersList().forEach(
107 entity -> directCounters.addWithPreamble(entity.getPreamble(), entity));
108
109 p4info.getMetersList().forEach(
110 entity -> meters.addWithPreamble(entity.getPreamble(), entity));
111
112 p4info.getDirectMetersList().forEach(
113 entity -> directMeters.addWithPreamble(entity.getPreamble(), entity));
114
115 p4info.getControllerPacketMetadataList().forEach(
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200116 entity -> {
117 ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity);
118 // Index control packet metadata metadata.
119 int ctrlPktMetadataId = entity.getPreamble().getId();
120 String ctrlPktMetadataName = entity.getPreamble().getName();
121 EntityBrowser<ControllerPacketMetadata.Metadata> metadataBrowser = new EntityBrowser<>(format(
122 "metadata field for controller packet metadata '%s'", ctrlPktMetadataName));
123 entity.getMetadataList().forEach(m -> metadataBrowser.add(m.getName(), null, m.getId(), m));
124 ctrlPktMetadatasMetadata.put(ctrlPktMetadataId, metadataBrowser);
125 });
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400126 }
127
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200128 static String extractMatchFieldSimpleName(String name) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400129 // Removes the leading "hdr." or other scope identifier.
130 // E.g.: "hdr.ethernet.etherType" becomes "ethernet.etherType"
131 String[] pieces = name.split("\\.");
132 if (pieces.length == 3) {
133 return pieces[1] + "." + pieces[2];
134 } else if (pieces.length == 2) {
135 return name;
136 } else {
137 throw new UnsupportedOperationException("Invalid match field name: " + name);
138 }
139 }
140
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400141 /**
142 * Returns a browser for tables.
143 *
144 * @return table browser
145 */
146 EntityBrowser<Table> tables() {
147 return tables;
148 }
149
150 /**
151 * Returns a browser for actions.
152 *
153 * @return action browser
154 */
155 EntityBrowser<Action> actions() {
156 return actions;
157 }
158
159 /**
160 * Returns a browser for action profiles.
161 *
162 * @return action profile browser
163 */
164 EntityBrowser<ActionProfile> actionProfiles() {
165 return actionProfiles;
166 }
167
168 /**
169 * Returns a browser for counters.
170 *
171 * @return counter browser
172 */
173 EntityBrowser<Counter> counters() {
174 return counters;
175 }
176
177 /**
178 * Returns a browser for direct counters.
179 *
180 * @return direct counter browser
181 */
182 EntityBrowser<DirectCounter> directCounters() {
183 return directCounters;
184 }
185
186 /**
187 * Returns a browser for meters.
188 *
189 * @return meter browser
190 */
191 EntityBrowser<Meter> meters() {
192 return meters;
193 }
194
195 /**
196 * Returns a browser for direct meters.
197 *
198 * @return table browser
199 */
200 EntityBrowser<DirectMeter> directMeters() {
201 return directMeters;
202 }
203
204 /**
205 * Returns a browser for controller packet metadata.
206 *
207 * @return controller packet metadata browser
208 */
209 EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
210 return ctrlPktMetadatas;
211 }
212
213 /**
214 * Returns a browser for params of the given action.
215 *
216 * @param actionId action identifier
217 * @return action params browser
218 * @throws NotFoundException if the action cannot be found
219 */
220 EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
221 // Throws exception if action id is not found.
222 actions.getById(actionId);
223 return actionParams.get(actionId);
224 }
225
226 /**
227 * Returns a browser for match fields of the given table.
228 *
229 * @param tableId table identifier
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200230 * @return match field browser
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400231 * @throws NotFoundException if the table cannot be found
232 */
233 EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
234 // Throws exception if action id is not found.
235 tables.getById(tableId);
236 return matchFields.get(tableId);
237 }
238
239 /**
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200240 * Returns a browser for metadata fields of the controller packet metadata.
241 *
242 * @param controllerPacketMetadataId controller packet metadata identifier
243 * @return metadata browser
Frank Wangd8ab0962017-08-11 11:09:30 +0800244 * @throws NotFoundException controller packet metadata cannot be found
Andrea Campanellafc1d34c2017-07-18 17:01:41 +0200245 */
246 EntityBrowser<ControllerPacketMetadata.Metadata> packetMetadatas(int controllerPacketMetadataId)
247 throws NotFoundException {
248 // Throws exception if controller packet metadata id is not found.
249 ctrlPktMetadatas.getById(controllerPacketMetadataId);
250 return ctrlPktMetadatasMetadata.get(controllerPacketMetadataId);
251 }
252
253 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400254 * Browser of P4Info entities.
255 *
256 * @param <T> protobuf message type
257 */
258 static final class EntityBrowser<T extends Message> {
259
260 private String entityName;
261 private final Map<String, T> names = Maps.newHashMap();
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200262 private final Map<String, String> aliasToNames = Maps.newHashMap();
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400263 private final Map<Integer, T> ids = Maps.newHashMap();
264
265 private EntityBrowser(String entityName) {
266 this.entityName = entityName;
267 }
268
269 /**
Carmelo Cascone04d09392017-07-19 08:49:08 -0400270 * Adds the given entity identified by the given name, alias (nullable) and id.
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400271 *
272 * @param name entity name
Carmelo Cascone04d09392017-07-19 08:49:08 -0400273 * @param alias entity alias or null
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400274 * @param id entity id
275 * @param entity entity message
276 */
Carmelo Cascone04d09392017-07-19 08:49:08 -0400277 void add(String name, String alias, int id, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400278 checkNotNull(name);
279 checkArgument(!name.isEmpty(), "Name cannot be empty");
280 checkNotNull(entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400281 names.put(name, entity);
282 ids.put(id, entity);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400283 if (alias != null && !alias.isEmpty()) {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200284 aliasToNames.put(alias, name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400285 }
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400286 }
287
288 /**
289 * Adds the given entity identified by the given P4Info preamble.
290 *
291 * @param preamble P4Info preamble protobuf message
292 * @param entity entity message
293 */
294 void addWithPreamble(Preamble preamble, T entity) {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400295 checkNotNull(preamble);
296 add(preamble.getName(), preamble.getAlias(), preamble.getId(), entity);
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400297 }
298
299 /**
300 * Returns true if the P4Info defines an entity with such name, false otherwise.
301 *
302 * @param name entity name
303 * @return boolean
304 */
305 boolean hasName(String name) {
306 return names.containsKey(name);
307 }
308
309 /**
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200310 * Returns the entity identified by the given name, if present, otherwise, throws an exception.
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400311 *
312 * @param name entity name or alias
313 * @return entity message
314 * @throws NotFoundException if the entity cannot be found
315 */
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200316 T getByName(String name) throws NotFoundException {
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400317 if (hasName(name)) {
318 return names.get(name);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400319 } else {
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200320 final String hint = aliasToNames.containsKey(name)
321 ? format("Did you mean '%s'? Make sure to use entity names in PI IDs, not aliases",
322 aliasToNames.get(name))
323 : "";
324 throw new NotFoundException(entityName, name, hint);
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400325 }
326 }
327
328 /**
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400329 * Returns true if the P4Info defines an entity with such id, false otherwise.
330 *
331 * @param id entity id
332 * @return boolean
333 */
334 boolean hasId(int id) {
335 return ids.containsKey(id);
336 }
337
338 /**
339 * Returns the entity identified by the given id, if present, otherwise, throws an exception.
340 *
341 * @param id entity id
342 * @return entity message
343 * @throws NotFoundException if the entity cannot be found
344 */
345 T getById(int id) throws NotFoundException {
346 if (!hasId(id)) {
347 throw new NotFoundException(entityName, id);
348 }
349 return ids.get(id);
350 }
351 }
352
353 /**
354 * Signals tha an entity cannot be found in the P4Info.
355 */
Carmelo Cascone8d99b172017-07-18 17:26:31 -0400356 public static final class NotFoundException extends Exception {
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400357
Carmelo Casconecb0a49c2017-10-03 14:32:23 +0200358 NotFoundException(String entityName, String key, String hint) {
359 super(format(
360 "No such %s in P4Info with name '%s'%s", entityName, key, hint.isEmpty() ? "" : " (" + hint + ")"));
Carmelo Cascone98dfdb72017-07-14 12:01:37 -0400361 }
362
363 NotFoundException(String entityName, int id) {
364 super(format("No such %s in P4Info with id '%d'", entityName, id));
365 }
366 }
367}