Implemented P4Info browser
Change-Id: Iebff15ac60cf1121b29afce7f67b4c31c5a60f0e
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4InfoBrowser.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4InfoBrowser.java
new file mode 100644
index 0000000..d8edaff
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4InfoBrowser.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.p4runtime.ctl;
+
+
+import com.google.common.collect.Maps;
+import com.google.protobuf.Message;
+import p4.config.P4InfoOuterClass.Action;
+import p4.config.P4InfoOuterClass.ActionProfile;
+import p4.config.P4InfoOuterClass.ControllerPacketMetadata;
+import p4.config.P4InfoOuterClass.Counter;
+import p4.config.P4InfoOuterClass.DirectCounter;
+import p4.config.P4InfoOuterClass.DirectMeter;
+import p4.config.P4InfoOuterClass.MatchField;
+import p4.config.P4InfoOuterClass.Meter;
+import p4.config.P4InfoOuterClass.P4Info;
+import p4.config.P4InfoOuterClass.Preamble;
+import p4.config.P4InfoOuterClass.Table;
+
+import java.util.Map;
+
+import static java.lang.String.format;
+
+/**
+ * Utility class to easily retrieve information from a P4Info protobuf message.
+ */
+final class P4InfoBrowser {
+
+ private final EntityBrowser<Table> tables = new EntityBrowser<>("table");
+ private final EntityBrowser<Action> actions = new EntityBrowser<>("action");
+ private final EntityBrowser<ActionProfile> actionProfiles = new EntityBrowser<>("action profile");
+ private final EntityBrowser<Counter> counters = new EntityBrowser<>("counter");
+ private final EntityBrowser<DirectCounter> directCounters = new EntityBrowser<>("direct counter");
+ private final EntityBrowser<Meter> meters = new EntityBrowser<>("meter");
+ private final EntityBrowser<DirectMeter> directMeters = new EntityBrowser<>("direct meter");
+ private final EntityBrowser<ControllerPacketMetadata> ctrlPktMetadatas =
+ new EntityBrowser<>("controller packet metadata");
+ private final Map<Integer, EntityBrowser<Action.Param>> actionParams = Maps.newHashMap();
+ private final Map<Integer, EntityBrowser<MatchField>> matchFields = Maps.newHashMap();
+
+ /**
+ * Creates a new browser for the given P4Info.
+ *
+ * @param p4info P4Info protobuf message
+ */
+ P4InfoBrowser(P4Info p4info) {
+ parseP4Info(p4info);
+ }
+
+ private void parseP4Info(P4Info p4info) {
+ p4info.getTablesList().forEach(
+ entity -> {
+ tables.addWithPreamble(entity.getPreamble(), entity);
+ // Index match fields.
+ int tableId = entity.getPreamble().getId();
+ String tableName = entity.getPreamble().getName();
+ EntityBrowser<MatchField> matchFieldBrowser = new EntityBrowser<>(format(
+ "match field for table '%s'", tableName));
+ entity.getMatchFieldsList().forEach(m -> matchFieldBrowser.add(m.getName(), m.getId(), m));
+ matchFields.put(tableId, matchFieldBrowser);
+ });
+
+ p4info.getActionsList().forEach(
+ entity -> {
+ actions.addWithPreamble(entity.getPreamble(), entity);
+ // Index action params.
+ int actionId = entity.getPreamble().getId();
+ String actionName = entity.getPreamble().getName();
+ EntityBrowser<Action.Param> paramBrowser = new EntityBrowser<>(format(
+ "param for action '%s'", actionName));
+ entity.getParamsList().forEach(p -> paramBrowser.add(p.getName(), p.getId(), p));
+ actionParams.put(actionId, paramBrowser);
+ });
+
+ p4info.getActionProfilesList().forEach(
+ entity -> actionProfiles.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getCountersList().forEach(
+ entity -> counters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getDirectCountersList().forEach(
+ entity -> directCounters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getMetersList().forEach(
+ entity -> meters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getDirectMetersList().forEach(
+ entity -> directMeters.addWithPreamble(entity.getPreamble(), entity));
+
+ p4info.getControllerPacketMetadataList().forEach(
+ entity -> ctrlPktMetadatas.addWithPreamble(entity.getPreamble(), entity));
+ }
+
+ /**
+ * Returns a browser for tables.
+ *
+ * @return table browser
+ */
+ EntityBrowser<Table> tables() {
+ return tables;
+ }
+
+ /**
+ * Returns a browser for actions.
+ *
+ * @return action browser
+ */
+ EntityBrowser<Action> actions() {
+ return actions;
+ }
+
+ /**
+ * Returns a browser for action profiles.
+ *
+ * @return action profile browser
+ */
+ EntityBrowser<ActionProfile> actionProfiles() {
+ return actionProfiles;
+ }
+
+ /**
+ * Returns a browser for counters.
+ *
+ * @return counter browser
+ */
+ EntityBrowser<Counter> counters() {
+ return counters;
+ }
+
+ /**
+ * Returns a browser for direct counters.
+ *
+ * @return direct counter browser
+ */
+ EntityBrowser<DirectCounter> directCounters() {
+ return directCounters;
+ }
+
+ /**
+ * Returns a browser for meters.
+ *
+ * @return meter browser
+ */
+ EntityBrowser<Meter> meters() {
+ return meters;
+ }
+
+ /**
+ * Returns a browser for direct meters.
+ *
+ * @return table browser
+ */
+ EntityBrowser<DirectMeter> directMeters() {
+ return directMeters;
+ }
+
+ /**
+ * Returns a browser for controller packet metadata.
+ *
+ * @return controller packet metadata browser
+ */
+ EntityBrowser<ControllerPacketMetadata> controllerPacketMetadatas() {
+ return ctrlPktMetadatas;
+ }
+
+ /**
+ * Returns a browser for params of the given action.
+ *
+ * @param actionId action identifier
+ * @return action params browser
+ * @throws NotFoundException if the action cannot be found
+ */
+ EntityBrowser<Action.Param> actionParams(int actionId) throws NotFoundException {
+ // Throws exception if action id is not found.
+ actions.getById(actionId);
+ return actionParams.get(actionId);
+ }
+
+ /**
+ * Returns a browser for match fields of the given table.
+ *
+ * @param tableId table identifier
+ * @return controller packet metadata browser
+ * @throws NotFoundException if the table cannot be found
+ */
+ EntityBrowser<MatchField> matchFields(int tableId) throws NotFoundException {
+ // Throws exception if action id is not found.
+ tables.getById(tableId);
+ return matchFields.get(tableId);
+ }
+
+ /**
+ * Browser of P4Info entities.
+ *
+ * @param <T> protobuf message type
+ */
+ static final class EntityBrowser<T extends Message> {
+
+ private String entityName;
+ private final Map<String, T> names = Maps.newHashMap();
+ private final Map<String, T> aliases = Maps.newHashMap();
+ private final Map<Integer, T> ids = Maps.newHashMap();
+
+ private EntityBrowser(String entityName) {
+ this.entityName = entityName;
+ }
+
+ /**
+ * Adds the given entity identified by the given name and id.
+ *
+ * @param name entity name
+ * @param id entity id
+ * @param entity entity message
+ */
+ void add(String name, int id, T entity) {
+ names.put(name, entity);
+ ids.put(id, entity);
+ }
+
+ /**
+ * Adds the given entity identified by the given P4Info preamble.
+ *
+ * @param preamble P4Info preamble protobuf message
+ * @param entity entity message
+ */
+ void addWithPreamble(Preamble preamble, T entity) {
+ names.put(preamble.getName(), entity);
+ aliases.put(preamble.getName(), entity);
+ ids.put(preamble.getId(), entity);
+ }
+
+ /**
+ * Returns true if the P4Info defines an entity with such name, false otherwise.
+ *
+ * @param name entity name
+ * @return boolean
+ */
+ boolean hasName(String name) {
+ return names.containsKey(name);
+ }
+
+ /**
+ * Returns the entity identified by the given name, if present, otherwise, throws an exception.
+ *
+ * @param name entity name
+ * @return entity message
+ * @throws NotFoundException if the entity cannot be found
+ */
+ T getByName(String name) throws NotFoundException {
+ if (!hasName(name)) {
+ throw new NotFoundException(entityName, name);
+ }
+ return names.get(name);
+ }
+
+ /**
+ * Returns true if the P4Info defines an entity with such alias, false otherwise.
+ *
+ * @param alias entity alias
+ * @return boolean
+ */
+ boolean hasAlias(String alias) {
+ return aliases.containsKey(alias);
+ }
+
+ /**
+ * Returns the entity identified by the given alias, if present, otherwise, throws an exception.
+ *
+ * @param alias entity alias
+ * @return entity message
+ * @throws NotFoundException if the entity cannot be found
+ */
+ T getByAlias(String alias) throws NotFoundException {
+ if (!hasName(alias)) {
+ throw new NotFoundException(entityName, alias);
+ }
+ return aliases.get(alias);
+ }
+
+ /**
+ * Returns true if the P4Info defines an entity with such id, false otherwise.
+ *
+ * @param id entity id
+ * @return boolean
+ */
+ boolean hasId(int id) {
+ return ids.containsKey(id);
+ }
+
+ /**
+ * Returns the entity identified by the given id, if present, otherwise, throws an exception.
+ *
+ * @param id entity id
+ * @return entity message
+ * @throws NotFoundException if the entity cannot be found
+ */
+ T getById(int id) throws NotFoundException {
+ if (!hasId(id)) {
+ throw new NotFoundException(entityName, id);
+ }
+ return ids.get(id);
+ }
+ }
+
+ /**
+ * Signals tha an entity cannot be found in the P4Info.
+ */
+ public static class NotFoundException extends Exception {
+
+ NotFoundException(String entityName, String key) {
+ super(format("No such %s in P4Info with name/alias '%s'", entityName, key));
+ }
+
+ NotFoundException(String entityName, int id) {
+ super(format("No such %s in P4Info with id '%d'", entityName, id));
+ }
+ }
+}