ONOS-7001 Support for direct counters
Currently Bmv2 returns UNKNOWN error when reading direct counters.
Change-Id: I834d7b5a8627181c6888500545e1bdbfe9af8dc1
diff --git a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
index 6b93f5d..738996f 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/model/PiPipelineInterpreter.java
@@ -24,6 +24,7 @@
import org.onosproject.net.packet.InboundPacket;
import org.onosproject.net.packet.OutboundPacket;
import org.onosproject.net.pi.runtime.PiAction;
+import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableId;
@@ -88,6 +89,15 @@
throws PiInterpreterException;
/**
+ * Returns a protocol-independent direct counter identifier for the given table, if present. If not present, it
+ * means that the given table does not support direct counters.
+ *
+ * @param piTableId table identifier
+ * @return optional direct counter identifier
+ */
+ Optional<PiCounterId> mapTableCounter(PiTableId piTableId);
+
+ /**
* Returns a collection of packet operations equivalent to the given OutboundPacket.
*
* @param packet a ONOS outbound packet
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java
index d762f23..cd88d2a 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterCellId.java
@@ -16,77 +16,22 @@
package org.onosproject.net.pi.runtime;
-import com.google.common.annotations.Beta;
-import com.google.common.base.Objects;
-import org.onlab.util.Identifier;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
/**
- * Identifier of a counter cell of a protocol-independent pipeline.
+ * Identifier of a counter cell in a protocol-independent pipeline.
*/
-@Beta
-public final class PiCounterCellId extends Identifier<String> {
-
- private final PiCounterId counterId;
- private final long index;
-
- private PiCounterCellId(PiCounterId counterId, long index) {
- super(counterId.id() + "[" + index + "]");
- this.counterId = counterId;
- this.index = index;
- }
+public interface PiCounterCellId {
/**
- * Returns a counter cell identifier for the given counter identifier and index.
- *
- * @param counterId counter identifier
- * @param index index
- * @return counter cell identifier
- */
- public static PiCounterCellId of(PiCounterId counterId, long index) {
- checkNotNull(counterId);
- checkArgument(index >= 0, "Index must be a positive integer");
- return new PiCounterCellId(counterId, index);
- }
-
- /**
- * Returns the counter identifier of this cell.
+ * Returns the identifier of the counter instance where this cell is contained.
*
* @return counter identifier
*/
- public PiCounterId counterId() {
- return counterId;
- }
+ PiCounterId counterId();
/**
- * Returns the index of this cell.
+ * Returns the type of counter identified.
*
- * @return cell index
+ * @return counter type
*/
- public long index() {
- return index;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof PiCounterCellId)) {
- return false;
- }
- if (!super.equals(o)) {
- return false;
- }
- PiCounterCellId that = (PiCounterCellId) o;
- return index == that.index &&
- Objects.equal(counterId, that.counterId);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(super.hashCode(), counterId, index);
- }
+ PiCounterType type();
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterId.java
index 6fcd55e..dcc7c02 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterId.java
@@ -17,7 +17,7 @@
package org.onosproject.net.pi.runtime;
import com.google.common.annotations.Beta;
-import org.onlab.util.Identifier;
+import com.google.common.base.Objects;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
@@ -26,22 +26,28 @@
* Identifier of a counter of a protocol-independent pipeline.
*/
@Beta
-public final class PiCounterId extends Identifier<String> {
+public final class PiCounterId {
- private PiCounterId(String name) {
- super(name);
+ private final String name;
+ private final PiCounterType type;
+
+ private PiCounterId(String name, PiCounterType type) {
+ this.name = name;
+ this.type = type;
}
/**
- * Returns a counter identifier for the given name.
+ * Returns a counter identifier for the given name and type.
*
* @param name counter name
+ * @param type counter type
* @return counter identifier
*/
- public static PiCounterId of(String name) {
+ public static PiCounterId of(String name, PiCounterType type) {
checkNotNull(name);
- checkArgument(!name.isEmpty(), "Name name can't be empty");
- return new PiCounterId(name);
+ checkNotNull(type);
+ checkArgument(!name.isEmpty(), "Name can't be empty");
+ return new PiCounterId(name, type);
}
/**
@@ -50,6 +56,38 @@
* @return counter name
*/
public String name() {
- return this.identifier;
+ return this.name;
+ }
+
+ /**
+ * Returns the type of the counter.
+ *
+ * @return counter type
+ */
+ public PiCounterType type() {
+ return this.type;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof PiCounterId)) {
+ return false;
+ }
+ PiCounterId that = (PiCounterId) o;
+ return Objects.equal(name, that.name) &&
+ type == that.type;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(name, type);
+ }
+
+ @Override
+ public String toString() {
+ return type.name().toLowerCase() + ":" + name;
}
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterType.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterType.java
new file mode 100644
index 0000000..b4a709a
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterType.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.net.pi.runtime;
+
+/**
+ * Type of counter in a protocol-independent pipeline.
+ */
+public enum PiCounterType {
+ /**
+ * Identifies a counter associated to a match-action table, where cells are directly associated to table entries.
+ */
+ DIRECT,
+
+ /**
+ * Identifies a counter not associated with any other resource.
+ */
+ INDIRECT
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiDirectCounterCellId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiDirectCounterCellId.java
new file mode 100644
index 0000000..26ae363
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiDirectCounterCellId.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.lang.String.format;
+import static org.onosproject.net.pi.runtime.PiCounterType.DIRECT;
+
+/**
+ * Identifier of a direct counter cell of a protocol-independent pipeline.
+ */
+@Beta
+public final class PiDirectCounterCellId implements PiCounterCellId {
+
+ private final PiCounterId counterId;
+ private final PiTableEntry tableEntry;
+
+ private PiDirectCounterCellId(PiCounterId counterId, PiTableEntry tableEntry) {
+ this.counterId = counterId;
+ this.tableEntry = tableEntry;
+ }
+
+ /**
+ * Returns a direct counter cell identifier for the given counter identifier and table entry.
+ *
+ * @param counterId counter identifier
+ * @param tableEntry table entry
+ * @return direct counter cell identifier
+ */
+ public static PiDirectCounterCellId of(PiCounterId counterId, PiTableEntry tableEntry) {
+ checkNotNull(counterId);
+ checkNotNull(tableEntry);
+ checkArgument(counterId.type() == DIRECT, "Counter ID must be of type DIRECT");
+ return new PiDirectCounterCellId(counterId, tableEntry);
+ }
+
+ /**
+ * Returns the table entry associated with this cell identifier.
+ *
+ * @return cell table entry
+ */
+ public PiTableEntry tableEntry() {
+ return tableEntry;
+ }
+
+ @Override
+ public PiCounterId counterId() {
+ return counterId;
+ }
+
+ @Override
+ public PiCounterType type() {
+ return DIRECT;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof PiDirectCounterCellId)) {
+ return false;
+ }
+ PiDirectCounterCellId that = (PiDirectCounterCellId) o;
+ return Objects.equal(counterId, that.counterId) &&
+ Objects.equal(tableEntry, that.tableEntry);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(counterId, tableEntry);
+ }
+
+ @Override
+ public String toString() {
+ return format("%s[{%s}]", counterId, tableEntry);
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiIndirectCounterCellId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiIndirectCounterCellId.java
new file mode 100644
index 0000000..6f3f73a
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiIndirectCounterCellId.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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.net.pi.runtime;
+
+import com.google.common.annotations.Beta;
+import org.onlab.util.Identifier;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.pi.runtime.PiCounterType.INDIRECT;
+
+/**
+ * Identifier of an indirect counter cell in a protocol-independent pipeline.
+ */
+@Beta
+public final class PiIndirectCounterCellId extends Identifier<String> implements PiCounterCellId {
+
+ private final PiCounterId counterId;
+ private final long index;
+
+ private PiIndirectCounterCellId(PiCounterId counterId, long index) {
+ super(counterId.toString() + "[" + index + "]");
+ this.counterId = counterId;
+ this.index = index;
+ }
+
+ /**
+ * Returns a counter cell identifier for the given counter identifier and index.
+ *
+ * @param counterId counter identifier
+ * @param index index
+ * @return counter cell identifier
+ */
+ public static PiIndirectCounterCellId of(PiCounterId counterId, long index) {
+ checkNotNull(counterId);
+ checkArgument(counterId.type() == INDIRECT, "Counter ID must be of type INDIRECT");
+ checkArgument(index >= 0, "Index must be a positive integer");
+ return new PiIndirectCounterCellId(counterId, index);
+ }
+
+ /**
+ * Returns the index of this cell.
+ *
+ * @return cell index
+ */
+ public long index() {
+ return index;
+ }
+
+ @Override
+ public PiCounterId counterId() {
+ return counterId;
+ }
+
+ @Override
+ public PiCounterType type() {
+ return INDIRECT;
+ }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMatchKey.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMatchKey.java
index 91cabfd..e2e8c3d 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMatchKey.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiMatchKey.java
@@ -24,14 +24,14 @@
import java.util.Optional;
import java.util.StringJoiner;
-import static com.google.common.base.Preconditions.checkArgument;
-
/**
* Representation of all field matches of an entry of a match+action table of a protocol-independent pipeline.
*/
@Beta
public final class PiMatchKey {
+ public static final PiMatchKey EMPTY = builder().build();
+
private final ImmutableMap<PiHeaderFieldId, PiFieldMatch> fieldMatches;
private PiMatchKey(ImmutableMap<PiHeaderFieldId, PiFieldMatch> fieldMatches) {
@@ -130,7 +130,6 @@
*/
public PiMatchKey build() {
ImmutableMap<PiHeaderFieldId, PiFieldMatch> fieldMatches = fieldMatchesBuilder.build();
- checkArgument(fieldMatches.size() > 0, "Field matches cannot be empty");
return new PiMatchKey(fieldMatches);
}
}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
index b0192fd..6a5f75b 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableEntry.java
@@ -31,6 +31,8 @@
@Beta
public final class PiTableEntry {
+ public static final PiTableEntry EMTPY = new PiTableEntry();
+
private static final int NO_PRIORITY = -1;
private static final double NO_TIMEOUT = -1;
@@ -41,6 +43,15 @@
private final int priority;
private final double timeout;
+ private PiTableEntry() {
+ this.tableId = null;
+ this.matchKey = null;
+ this.tableAction = null;
+ this.cookie = 0;
+ this.priority = NO_PRIORITY;
+ this.timeout = NO_TIMEOUT;
+ }
+
private PiTableEntry(PiTableId tableId, PiMatchKey matchKey,
PiTableAction tableAction, long cookie, int priority, double timeout) {
this.tableId = tableId;
diff --git a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
index 505dba3..6caa63c 100644
--- a/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
+++ b/core/api/src/test/java/org/onosproject/net/pi/runtime/PiTableEntryTest.java
@@ -27,29 +27,27 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
-import static org.onosproject.net.pi.runtime.PiConstantsTest.DROP;
-import static org.onosproject.net.pi.runtime.PiConstantsTest.DST_ADDR;
-import static org.onosproject.net.pi.runtime.PiConstantsTest.IPV4_HEADER_NAME;
+import static org.onosproject.net.pi.runtime.PiConstantsTest.*;
/**
* Unit tests for PiTableEntry class.
*/
public class PiTableEntryTest {
- final PiTableEntry piTableEntry1 = PiTableEntry.builder()
+ private final PiTableEntry piTableEntry1 = PiTableEntry.builder()
.forTable(PiTableId.of("Table10"))
.withCookie(0xac)
.withPriority(10)
.withAction(PiAction.builder().withId(PiActionId.of(DROP)).build())
.withTimeout(100)
.build();
- final PiTableEntry sameAsPiTableEntry1 = PiTableEntry.builder()
+ private final PiTableEntry sameAsPiTableEntry1 = PiTableEntry.builder()
.forTable(PiTableId.of("Table10"))
.withCookie(0xac)
.withPriority(10)
.withAction(PiAction.builder().withId(PiActionId.of(DROP)).build())
.withTimeout(100)
.build();
- final PiTableEntry piTableEntry2 = PiTableEntry.builder()
+ private final PiTableEntry piTableEntry2 = PiTableEntry.builder()
.forTable(PiTableId.of("Table20"))
.withCookie(0xac)
.withPriority(10)
@@ -77,6 +75,16 @@
}
/**
+ * Tests equality of the empty table entry.
+ */
+ @Test
+ public void testEmptyEquals() {
+ new EqualsTester()
+ .addEqualityGroup(PiTableEntry.EMTPY, PiTableEntry.EMTPY)
+ .testEquals();
+ }
+
+ /**
* Tests creation of a DefaultFlowRule using a FlowRule constructor.
*/
@Test
diff --git a/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java b/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
index 794e7f8..a9178ca 100644
--- a/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
+++ b/core/net/src/test/java/org/onosproject/net/pi/impl/MockInterpreter.java
@@ -32,6 +32,7 @@
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
+import org.onosproject.net.pi.runtime.PiCounterId;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiTableId;
@@ -103,6 +104,11 @@
}
@Override
+ public Optional<PiCounterId> mapTableCounter(PiTableId piTableId) {
+ return Optional.empty();
+ }
+
+ @Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
return ImmutableList.of();
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java
index 6029590..da56bfc 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4Interpreter.java
@@ -43,6 +43,8 @@
import org.onosproject.net.pi.runtime.PiActionId;
import org.onosproject.net.pi.runtime.PiActionParam;
import org.onosproject.net.pi.runtime.PiActionParamId;
+import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiCounterType;
import org.onosproject.net.pi.runtime.PiHeaderFieldId;
import org.onosproject.net.pi.runtime.PiPacketMetadata;
import org.onosproject.net.pi.runtime.PiPacketMetadataId;
@@ -74,6 +76,7 @@
// e.g. in a dedicated onos/pipeconf directory, along with any related P4 source code.
public static final String TABLE0 = "table0";
+ public static final String TABLE0_COUNTER = "table0_counter";
public static final String SEND_TO_CPU = "send_to_cpu";
public static final String PORT = "port";
public static final String DROP = "_drop";
@@ -81,12 +84,17 @@
public static final String EGRESS_PORT = "egress_port";
public static final String INGRESS_PORT = "ingress_port";
+ private static final PiTableId TABLE0_ID = PiTableId.of(TABLE0);
+
protected static final PiHeaderFieldId ETH_DST_ID = PiHeaderFieldId.of("ethernet", "dstAddr");
protected static final PiHeaderFieldId ETH_SRC_ID = PiHeaderFieldId.of("ethernet", "srcAddr");
protected static final PiHeaderFieldId ETH_TYPE_ID = PiHeaderFieldId.of("ethernet", "etherType");
private static final ImmutableBiMap<Integer, PiTableId> TABLE_MAP = ImmutableBiMap.of(
- 0, PiTableId.of(TABLE0));
+ 0, TABLE0_ID);
+
+ private static final ImmutableBiMap<PiTableId, PiCounterId> TABLE_COUNTER_MAP = ImmutableBiMap.of(
+ TABLE0_ID, PiCounterId.of(TABLE0_COUNTER, PiCounterType.DIRECT));
private boolean targetAttributesInitialized = false;
@@ -189,6 +197,11 @@
}
@Override
+ public Optional<PiCounterId> mapTableCounter(PiTableId piTableId) {
+ return Optional.ofNullable(TABLE_COUNTER_MAP.get(piTableId));
+ }
+
+ @Override
public Collection<PiPacketOperation> mapOutboundPacket(OutboundPacket packet)
throws PiInterpreterException {
TrafficTreatment treatment = packet.treatment();
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java
index 2946304..b127855 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/DefaultP4PortStatisticsDiscovery.java
@@ -24,6 +24,7 @@
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
import java.util.Collection;
import java.util.Collections;
@@ -32,6 +33,8 @@
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
+import static org.onosproject.net.pi.runtime.PiCounterType.INDIRECT;
+
/**
* Implementation of a PortStatisticsBehaviour that can be used for any P4 program based on default.p4 (i.e. those
* under onos/tools/test/p4src).
@@ -39,8 +42,8 @@
public class DefaultP4PortStatisticsDiscovery extends AbstractP4RuntimeHandlerBehaviour
implements PortStatisticsDiscovery {
- private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("ingress_port_counter");
- private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("egress_port_counter");
+ private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("ingress_port_counter", INDIRECT);
+ private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("egress_port_counter", INDIRECT);
@Override
public Collection<PortStatistics> discoverPortStatistics() {
@@ -54,14 +57,14 @@
deviceService.getPorts(deviceId)
.forEach(p -> portStatBuilders.put(p.number().toLong(),
DefaultPortStatistics.builder()
- .setPort((int) p.number().toLong())
+ .setPort(p.number())
.setDeviceId(deviceId)));
Set<PiCounterCellId> counterCellIds = Sets.newHashSet();
portStatBuilders.keySet().forEach(p -> {
// Counter cell/index = port number.
- counterCellIds.add(PiCounterCellId.of(INGRESS_COUNTER_ID, p));
- counterCellIds.add(PiCounterCellId.of(EGRESS_COUNTER_ID, p));
+ counterCellIds.add(PiIndirectCounterCellId.of(INGRESS_COUNTER_ID, p));
+ counterCellIds.add(PiIndirectCounterCellId.of(EGRESS_COUNTER_ID, p));
});
Collection<PiCounterCellData> counterEntryResponse;
@@ -73,20 +76,25 @@
return Collections.emptyList();
}
- counterEntryResponse.forEach(counterEntry -> {
- if (!portStatBuilders.containsKey(counterEntry.cellId().index())) {
- log.warn("Unrecognized counter index {}, skipping", counterEntry);
+ counterEntryResponse.forEach(counterData -> {
+ if (counterData.cellId().type() != INDIRECT) {
+ log.warn("Invalid counter data type {}, skipping", counterData.cellId().type());
return;
}
- DefaultPortStatistics.Builder statsBuilder = portStatBuilders.get(counterEntry.cellId().index());
- if (counterEntry.cellId().counterId().equals(INGRESS_COUNTER_ID)) {
- statsBuilder.setPacketsReceived(counterEntry.packets());
- statsBuilder.setBytesReceived(counterEntry.bytes());
- } else if (counterEntry.cellId().counterId().equals(EGRESS_COUNTER_ID)) {
- statsBuilder.setPacketsSent(counterEntry.packets());
- statsBuilder.setBytesSent(counterEntry.bytes());
+ PiIndirectCounterCellId indCellId = (PiIndirectCounterCellId) counterData.cellId();
+ if (!portStatBuilders.containsKey(indCellId.index())) {
+ log.warn("Unrecognized counter index {}, skipping", counterData);
+ return;
+ }
+ DefaultPortStatistics.Builder statsBuilder = portStatBuilders.get(indCellId.index());
+ if (counterData.cellId().counterId().equals(INGRESS_COUNTER_ID)) {
+ statsBuilder.setPacketsReceived(counterData.packets());
+ statsBuilder.setBytesReceived(counterData.bytes());
+ } else if (counterData.cellId().counterId().equals(EGRESS_COUNTER_ID)) {
+ statsBuilder.setPacketsSent(counterData.packets());
+ statsBuilder.setBytesSent(counterData.bytes());
} else {
- log.warn("Unrecognized counter ID {}, skipping", counterEntry);
+ log.warn("Unrecognized counter ID {}, skipping", counterData);
}
});
diff --git a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
index ad9329a..50b808e 100644
--- a/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
+++ b/drivers/p4runtime/src/main/java/org/onosproject/drivers/p4runtime/P4RuntimeFlowRuleProgrammable.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import io.grpc.StatusRuntimeException;
import org.onosproject.net.flow.DefaultFlowEntry;
import org.onosproject.net.flow.FlowEntry;
import org.onosproject.net.flow.FlowRule;
@@ -26,6 +27,10 @@
import org.onosproject.net.pi.model.PiPipelineInterpreter;
import org.onosproject.net.pi.model.PiPipelineModel;
import org.onosproject.net.pi.model.PiTableModel;
+import org.onosproject.net.pi.runtime.PiCounterCellData;
+import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiDirectCounterCellId;
import org.onosproject.net.pi.runtime.PiFlowRuleTranslationService;
import org.onosproject.net.pi.runtime.PiTableEntry;
import org.onosproject.net.pi.runtime.PiTableId;
@@ -36,6 +41,8 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Lock;
@@ -72,6 +79,13 @@
// TODO: can remove this check as soon as the BMv2 bug when reading ECMP entries is fixed.
private boolean ignoreDeviceWhenGet = true;
+ /*
+ If true, we read all direct counters of a table with one request. Otherwise, send as many request as the number of
+ table entries.
+ */
+ // TODO: set to true as soon as the feature is implemented in P4Runtime.
+ private boolean readAllDirectCounters = false;
+
// Needed to synchronize operations over the same table entry.
private static final ConcurrentMap<P4RuntimeTableEntryReference, Lock> ENTRY_LOCKS = Maps.newConcurrentMap();
@@ -132,33 +146,70 @@
Collection<PiTableEntry> installedEntries;
try {
+ // TODO: optimize by dumping entries and counters in parallel, from ALL tables with the same request.
installedEntries = client.dumpTable(piTableId, pipeconf).get();
} catch (InterruptedException | ExecutionException e) {
- log.error("Exception while dumping table {} of {}", piTableId, deviceId, e);
+ if (!(e.getCause() instanceof StatusRuntimeException)) {
+ // gRPC errors are logged in the client.
+ log.error("Exception while dumping table {} of {}", piTableId, deviceId, e);
+ }
return Collections.emptyList();
}
+ Map<PiTableEntry, PiCounterCellData> counterCellMap;
+ try {
+ if (interpreter.mapTableCounter(piTableId).isPresent()) {
+ PiCounterId piCounterId = interpreter.mapTableCounter(piTableId).get();
+ Collection<PiCounterCellData> cellDatas;
+ if (readAllDirectCounters) {
+ cellDatas = client.readAllCounterCells(Collections.singleton(piCounterId), pipeconf).get();
+ } else {
+ Set<PiCounterCellId> cellIds = installedEntries.stream()
+ .map(entry -> PiDirectCounterCellId.of(piCounterId, entry))
+ .collect(Collectors.toSet());
+ cellDatas = client.readCounterCells(cellIds, pipeconf).get();
+ }
+ counterCellMap = cellDatas.stream()
+ .collect(Collectors.toMap(c -> ((PiDirectCounterCellId) c.cellId()).tableEntry(), c -> c));
+ } else {
+ counterCellMap = Collections.emptyMap();
+ }
+ installedEntries = client.dumpTable(piTableId, pipeconf).get();
+ } catch (InterruptedException | ExecutionException e) {
+ if (!(e.getCause() instanceof StatusRuntimeException)) {
+ // gRPC errors are logged in the client.
+ log.error("Exception while reading counters of table {} of {}", piTableId, deviceId, e);
+ }
+ counterCellMap = Collections.emptyMap();
+ }
+
for (PiTableEntry installedEntry : installedEntries) {
- P4RuntimeTableEntryReference entryRef = new P4RuntimeTableEntryReference(deviceId, piTableId,
+ P4RuntimeTableEntryReference entryRef = new P4RuntimeTableEntryReference(deviceId,
+ piTableId,
installedEntry.matchKey());
- P4RuntimeFlowRuleWrapper frWrapper = ENTRY_STORE.get(entryRef);
-
-
- if (frWrapper == null) {
+ if (!ENTRY_STORE.containsKey(entryRef)) {
// Inconsistent entry
inconsistentEntries.add(installedEntry);
continue; // next one.
}
- // TODO: implement table entry counter retrieval.
+ P4RuntimeFlowRuleWrapper frWrapper = ENTRY_STORE.get(entryRef);
+
long bytes = 0L;
long packets = 0L;
+ if (counterCellMap.containsKey(installedEntry)) {
+ PiCounterCellData counterCellData = counterCellMap.get(installedEntry);
+ bytes = counterCellData.bytes();
+ packets = counterCellData.packets();
+ }
- FlowEntry entry = new DefaultFlowEntry(frWrapper.rule(), ADDED, frWrapper.lifeInSeconds(),
- packets, bytes);
- resultBuilder.add(entry);
+ resultBuilder.add(new DefaultFlowEntry(frWrapper.rule(),
+ ADDED,
+ frWrapper.lifeInSeconds(),
+ packets,
+ bytes));
}
}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
new file mode 100644
index 0000000..025a5a3
--- /dev/null
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/CounterEntryCodec.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * 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 org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiCounterCellData;
+import org.onosproject.net.pi.runtime.PiCounterCellId;
+import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiDirectCounterCellId;
+import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
+import org.onosproject.net.pi.runtime.PiTableEntry;
+import org.slf4j.Logger;
+import p4.P4RuntimeOuterClass.CounterData;
+import p4.P4RuntimeOuterClass.CounterEntry;
+import p4.P4RuntimeOuterClass.DirectCounterEntry;
+import p4.P4RuntimeOuterClass.Entity;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static java.lang.String.format;
+import static org.slf4j.LoggerFactory.getLogger;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.COUNTER_ENTRY;
+import static p4.P4RuntimeOuterClass.Entity.EntityCase.DIRECT_COUNTER_ENTRY;
+
+/**
+ * Encoder/decoder of PI counter IDs to counter entry protobuf messages, and vice versa.
+ */
+final class CounterEntryCodec {
+
+ private static final Logger log = getLogger(CounterEntryCodec.class);
+
+ private CounterEntryCodec() {
+ // Hides constructor.
+ }
+
+ /**
+ * Returns a collection of P4Runtime entity protobuf messages describing both counter or direct counter entries,
+ * encoded from the given collection of PI counter cell identifiers, for the given pipeconf. If a PI counter cell
+ * identifier cannot be encoded, it is skipped, hence the returned collection might have different size than the
+ * input one.
+ * <p>
+ * This method takes as parameter also a map between numeric P4Info IDs and PI counter IDs, that will be populated
+ * during the process and that is then needed to aid in the decode process.
+ *
+ * @param cellIds counter cell identifiers
+ * @param counterIdMap counter ID map (empty, it will be populated during this method execution)
+ * @param pipeconf pipeconf
+ * @return collection of entity messages describing both counter or direct counter entries
+ */
+ static Collection<Entity> encodePiCounterCellIds(Collection<PiCounterCellId> cellIds,
+ Map<Integer, PiCounterId> counterIdMap,
+ PiPipeconf pipeconf) {
+ return cellIds
+ .stream()
+ .map(cellId -> {
+ try {
+ return encodePiCounterCellId(cellId, counterIdMap, pipeconf);
+ } catch (P4InfoBrowser.NotFoundException | EncodeException e) {
+ log.warn("Unable to encode PI counter cell id: {}", e.getMessage());
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Returns a collection of PI counter cell data, decoded from the given P4Runtime entity protobuf messages
+ * describing both counter or direct counter entries, for the given counter ID map (populated by {@link
+ * #encodePiCounterCellIds(Collection, Map, PiPipeconf)}), and pipeconf. If an entity message cannot be encoded, it
+ * is skipped, hence the returned collection might have different size than the input one.
+ *
+ * @param entities P4Runtime entity messages
+ * @param counterIdMap counter ID map (previously populated)
+ * @param pipeconf pipeconf
+ * @return collection of PI counter cell data
+ */
+ static Collection<PiCounterCellData> decodeCounterEntities(Collection<Entity> entities,
+ Map<Integer, PiCounterId> counterIdMap,
+ PiPipeconf pipeconf) {
+ return entities
+ .stream()
+ .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY ||
+ entity.getEntityCase() == DIRECT_COUNTER_ENTRY)
+ .map(entity -> {
+ try {
+ return decodeCounterEntity(entity, counterIdMap, pipeconf);
+ } catch (EncodeException | P4InfoBrowser.NotFoundException e) {
+ log.warn("Unable to decode counter entity message: {}", e.getMessage());
+ return null;
+ }
+ })
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ }
+
+ private static Entity encodePiCounterCellId(PiCounterCellId cellId, Map<Integer, PiCounterId> counterIdMap,
+ PiPipeconf pipeconf)
+ throws P4InfoBrowser.NotFoundException, EncodeException {
+
+ final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+
+ int counterId;
+ Entity entity;
+ // Encode PI cell ID into entity message and add to read request.
+ switch (cellId.type()) {
+ case INDIRECT:
+ counterId = browser.counters().getByNameOrAlias(cellId.counterId().name()).getPreamble().getId();
+ PiIndirectCounterCellId indCellId = (PiIndirectCounterCellId) cellId;
+ entity = Entity.newBuilder().setCounterEntry(CounterEntry.newBuilder()
+ .setCounterId(counterId)
+ .setIndex(indCellId.index())
+ .build())
+ .build();
+ break;
+ case DIRECT:
+ counterId = browser.directCounters().getByNameOrAlias(cellId.counterId().name()).getPreamble().getId();
+ PiDirectCounterCellId dirCellId = (PiDirectCounterCellId) cellId;
+ DirectCounterEntry.Builder entryBuilder = DirectCounterEntry.newBuilder().setCounterId(counterId);
+ if (!dirCellId.tableEntry().equals(PiTableEntry.EMTPY)) {
+ entryBuilder.setTableEntry(TableEntryEncoder.encode(dirCellId.tableEntry(), pipeconf));
+ }
+ entity = Entity.newBuilder().setDirectCounterEntry(entryBuilder.build()).build();
+ break;
+ default:
+ throw new EncodeException(format("Unrecognized PI counter cell ID type '%s'", cellId.type()));
+ }
+ counterIdMap.put(counterId, cellId.counterId());
+
+ return entity;
+ }
+
+ private static PiCounterCellData decodeCounterEntity(Entity entity, Map<Integer, PiCounterId> counterIdMap,
+ PiPipeconf pipeconf)
+ throws EncodeException, P4InfoBrowser.NotFoundException {
+
+ int counterId;
+ CounterData counterData;
+
+ if (entity.getEntityCase() == COUNTER_ENTRY) {
+ counterId = entity.getCounterEntry().getCounterId();
+ counterData = entity.getCounterEntry().getData();
+ } else {
+ counterId = entity.getDirectCounterEntry().getCounterId();
+ counterData = entity.getDirectCounterEntry().getData();
+ }
+
+ // Process only counter IDs that were requested in the first place.
+ if (!counterIdMap.containsKey(counterId)) {
+ throw new EncodeException(format("Unrecognized counter ID '%s'", counterId));
+ }
+
+ PiCounterId piCounterId = counterIdMap.get(counterId);
+
+ // Compute PI cell ID.
+ PiCounterCellId piCellId;
+
+ switch (piCounterId.type()) {
+ case INDIRECT:
+ if (entity.getEntityCase() != COUNTER_ENTRY) {
+ throw new EncodeException(format(
+ "Counter ID '%s' is indirect, but processed entity is %s",
+ piCounterId, entity.getEntityCase()));
+ }
+ piCellId = PiIndirectCounterCellId.of(piCounterId,
+ entity.getCounterEntry().getIndex());
+ break;
+ case DIRECT:
+ if (entity.getEntityCase() != DIRECT_COUNTER_ENTRY) {
+ throw new EncodeException(format(
+ "Counter ID '%s' is direct, but processed entity is %s",
+ piCounterId, entity.getEntityCase()));
+ }
+ PiTableEntry piTableEntry = TableEntryEncoder.decode(entity.getDirectCounterEntry().getTableEntry(),
+ pipeconf);
+ piCellId = PiDirectCounterCellId.of(piCounterId, piTableEntry);
+ break;
+ default:
+ throw new EncodeException(format("Unrecognized PI counter ID type '%s'", piCounterId.type()));
+ }
+
+ return new PiCounterCellData(piCellId, counterData.getPacketCount(), counterData.getByteCount());
+ }
+}
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
index 00a2683..a9adf55 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/P4RuntimeClientImpl.java
@@ -16,9 +16,9 @@
package org.onosproject.p4runtime.ctl;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import com.google.protobuf.ByteString;
import io.grpc.Context;
import io.grpc.ManagedChannel;
@@ -31,6 +31,8 @@
import org.onosproject.net.pi.runtime.PiCounterCellData;
import org.onosproject.net.pi.runtime.PiCounterCellId;
import org.onosproject.net.pi.runtime.PiCounterId;
+import org.onosproject.net.pi.runtime.PiDirectCounterCellId;
+import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
import org.onosproject.net.pi.runtime.PiPacketOperation;
import org.onosproject.net.pi.runtime.PiPipeconfService;
import org.onosproject.net.pi.runtime.PiTableEntry;
@@ -39,7 +41,6 @@
import org.onosproject.p4runtime.api.P4RuntimeEvent;
import org.slf4j.Logger;
import p4.P4RuntimeGrpc;
-import p4.P4RuntimeOuterClass.CounterEntry;
import p4.P4RuntimeOuterClass.Entity;
import p4.P4RuntimeOuterClass.ForwardingPipelineConfig;
import p4.P4RuntimeOuterClass.MasterArbitrationUpdate;
@@ -77,7 +78,6 @@
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.net.pi.model.PiPipeconf.ExtensionType;
import static org.slf4j.LoggerFactory.getLogger;
-import static p4.P4RuntimeOuterClass.Entity.EntityCase.COUNTER_ENTRY;
import static p4.P4RuntimeOuterClass.Entity.EntityCase.TABLE_ENTRY;
import static p4.P4RuntimeOuterClass.PacketOut;
import static p4.P4RuntimeOuterClass.SetForwardingPipelineConfigRequest.Action.VERIFY_AND_COMMIT;
@@ -87,11 +87,6 @@
*/
public final class P4RuntimeClientImpl implements P4RuntimeClient {
- private static final int DEADLINE_SECONDS = 15;
-
- // FIXME: use static election ID, since mastership arbitration is not yet support on BMv2 or Tofino.
- private static final int ELECTION_ID = 1;
-
private static final Map<WriteOperationType, Update.Type> UPDATE_TYPES = ImmutableMap.of(
WriteOperationType.UNSPECIFIED, Update.Type.UNSPECIFIED,
WriteOperationType.INSERT, Update.Type.INSERT,
@@ -142,7 +137,11 @@
try {
return supplier.get();
} catch (Throwable ex) {
- log.error("Exception in P4Runtime client of {}, executing {}", deviceId, opDescription, ex);
+ if (ex instanceof StatusRuntimeException) {
+ log.warn("Unable to execute {} on {}: {}", opDescription, deviceId, ex.toString());
+ } else {
+ log.error("Exception in client of {}, executing {}", deviceId, opDescription, ex);
+ }
throw ex;
} finally {
writeLock.unlock();
@@ -187,10 +186,31 @@
@Override
public CompletableFuture<Collection<PiCounterCellData>> readAllCounterCells(Set<PiCounterId> counterIds,
PiPipeconf pipeconf) {
- Set<PiCounterCellId> cellIds = counterIds.stream()
- // Cell with index 0 means all cells.
- .map(counterId -> PiCounterCellId.of(counterId, 0))
- .collect(Collectors.toSet());
+
+ /*
+ From p4runtime.proto, the scope of a ReadRequest is defined as follows:
+ CounterEntry:
+ - All counter cells for all meters if counter_id = 0 (default).
+ - All counter cells for given counter_id if index = 0 (default).
+ DirectCounterEntry:
+ - All counter cells for all meters if counter_id = 0 (default).
+ - All counter cells for given counter_id if table_entry.match is empty.
+ */
+
+ Set<PiCounterCellId> cellIds = Sets.newHashSet();
+
+ for (PiCounterId counterId : counterIds) {
+ switch (counterId.type()) {
+ case INDIRECT:
+ cellIds.add(PiIndirectCounterCellId.of(counterId, 0));
+ break;
+ case DIRECT:
+ cellIds.add(PiDirectCounterCellId.of(counterId, PiTableEntry.EMTPY));
+ break;
+ default:
+ log.warn("Unrecognized PI counter ID '{}'", counterId.type());
+ }
+ }
return supplyInContext(() -> doReadCounterCells(cellIds, pipeconf),
"readAllCounterCells-" + cellIds.hashCode());
@@ -416,62 +436,32 @@
private Collection<PiCounterCellData> doReadCounterCells(Collection<PiCounterCellId> cellIds, PiPipeconf pipeconf) {
- // From p4runtime.proto:
- // For ReadRequest, the scope is defined as follows:
- // - All counter cells for all meters if counter_id = 0 (default).
- // - All counter cells for given counter_id if index = 0 (default).
-
- final ReadRequest.Builder requestBuilder = ReadRequest.newBuilder().setDeviceId(p4DeviceId);
- final P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+ // We use this map to remember the original PI counter IDs of the returned response.
final Map<Integer, PiCounterId> counterIdMap = Maps.newHashMap();
- for (PiCounterCellId cellId : cellIds) {
- int counterId;
- try {
- counterId = browser.counters().getByNameOrAlias(cellId.counterId().id()).getPreamble().getId();
- } catch (P4InfoBrowser.NotFoundException e) {
- log.warn("Skipping counter cell {}: {}", cellId, e.getMessage());
- continue;
- }
- requestBuilder
- .addEntities(Entity.newBuilder()
- .setCounterEntry(CounterEntry.newBuilder()
- .setCounterId(counterId)
- .setIndex(cellId.index())
- .build()));
- counterIdMap.put(counterId, cellId.counterId());
+ final ReadRequest request = ReadRequest.newBuilder()
+ .setDeviceId(p4DeviceId)
+ .addAllEntities(CounterEntryCodec.encodePiCounterCellIds(cellIds, counterIdMap, pipeconf))
+ .build();
+
+ if (request.getEntitiesList().size() == 0) {
+ return Collections.emptyList();
}
- final Iterator<ReadResponse> responses;
+ final Iterable<ReadResponse> responses;
try {
- responses = blockingStub.read(requestBuilder.build());
+ responses = () -> blockingStub.read(request);
} catch (StatusRuntimeException e) {
log.warn("Unable to read counters: {}", e.getMessage());
return Collections.emptyList();
}
- final Iterable<ReadResponse> responseIterable = () -> responses;
- final ImmutableList.Builder<PiCounterCellData> piCounterEntryListBuilder = ImmutableList.builder();
-
- StreamSupport
- .stream(responseIterable.spliterator(), false)
+ List<Entity> entities = StreamSupport.stream(responses.spliterator(), false)
.map(ReadResponse::getEntitiesList)
.flatMap(List::stream)
- .filter(entity -> entity.getEntityCase() == COUNTER_ENTRY)
- .map(Entity::getCounterEntry)
- .forEach(counterEntryMsg -> {
- if (!counterIdMap.containsKey(counterEntryMsg.getCounterId())) {
- log.warn("Unrecognized counter ID '{}', skipping", counterEntryMsg.getCounterId());
- return;
- }
- PiCounterCellId cellId = PiCounterCellId.of(counterIdMap.get(counterEntryMsg.getCounterId()),
- counterEntryMsg.getIndex());
- piCounterEntryListBuilder.add(new PiCounterCellData(cellId,
- counterEntryMsg.getData().getPacketCount(),
- counterEntryMsg.getData().getByteCount()));
- });
+ .collect(Collectors.toList());
- return piCounterEntryListBuilder.build();
+ return CounterEntryCodec.decodeCounterEntities(entities, counterIdMap, pipeconf);
}
/**
diff --git a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
index 0dd82f3..9ffc18f 100644
--- a/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
+++ b/protocols/p4runtime/ctl/src/main/java/org/onosproject/p4runtime/ctl/TableEntryEncoder.java
@@ -70,8 +70,8 @@
}
/**
- * Returns a collection of P4Runtime table entry protobuf messages, encoded from the given collection of PI
- * table entries for the given pipeconf. If a PI table entry cannot be encoded, it is skipped, hence the returned
+ * Returns a collection of P4Runtime table entry protobuf messages, encoded from the given collection of PI table
+ * entries for the given pipeconf. If a PI table entry cannot be encoded, it is skipped, hence the returned
* collection might have different size than the input one.
* <p>
* Please check the log for an explanation of any error that might have occurred.
@@ -103,6 +103,26 @@
}
/**
+ * Same as {@link #encode(Collection, PiPipeconf)} but encodes only one entry.
+ *
+ * @param piTableEntry table entry
+ * @param pipeconf pipeconf
+ * @return encoded table entry message
+ * @throws EncodeException if entry cannot be encoded
+ * @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
+ */
+ static TableEntry encode(PiTableEntry piTableEntry, PiPipeconf pipeconf)
+ throws EncodeException, P4InfoBrowser.NotFoundException {
+
+ P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+ if (browser == null) {
+ throw new EncodeException(format("Unable to get a P4Info browser for pipeconf %s", pipeconf.id()));
+ }
+
+ return encodePiTableEntry(piTableEntry, browser);
+ }
+
+ /**
* Returns a collection of PI table entry objects, decoded from the given collection of P4Runtime table entry
* messages for the given pipeconf. If a table entry message cannot be decoded, it is skipped, hence the returned
* collection might have different size than the input one.
@@ -135,12 +155,63 @@
return piTableEntryListBuilder.build();
}
+ /**
+ * Same as {@link #decode(Collection, PiPipeconf)} but decodes only one entry.
+ *
+ * @param tableEntryMsg table entry message
+ * @param pipeconf pipeconf
+ * @return decoded PI table entry
+ * @throws EncodeException if message cannot be decoded
+ * @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
+ */
+ static PiTableEntry decode(TableEntry tableEntryMsg, PiPipeconf pipeconf)
+ throws EncodeException, P4InfoBrowser.NotFoundException {
+
+ P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+ if (browser == null) {
+ throw new EncodeException(format("Unable to get a P4Info browser for pipeconf %s", pipeconf.id()));
+ }
+ return decodeTableEntryMsg(tableEntryMsg, browser);
+ }
+
+ /**
+ * Returns a table entry protobuf message, encoded from the given table id and match key, for the given pipeconf.
+ * The returned table entry message can be only used to reference an existing entry, i.e. a read operation, and not
+ * a write one wince it misses other fields (action, priority, etc.).
+ *
+ * @param tableId table identifier
+ * @param matchKey match key
+ * @param pipeconf pipeconf
+ * @return table entry message
+ * @throws EncodeException if message cannot be encoded
+ * @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
+ */
+ static TableEntry encode(PiTableId tableId, PiMatchKey matchKey, PiPipeconf pipeconf)
+ throws EncodeException, P4InfoBrowser.NotFoundException {
+
+ P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+ TableEntry.Builder tableEntryMsgBuilder = TableEntry.newBuilder();
+
+ //FIXME this throws some kind of NPE
+ P4InfoOuterClass.Table tableInfo = browser.tables().getByName(tableId.id());
+
+ // Table id.
+ tableEntryMsgBuilder.setTableId(tableInfo.getPreamble().getId());
+
+ // Field matches.
+ for (PiFieldMatch piFieldMatch : matchKey.fieldMatches()) {
+ tableEntryMsgBuilder.addMatch(encodePiFieldMatch(piFieldMatch, tableInfo, browser));
+ }
+
+ return tableEntryMsgBuilder.build();
+ }
+
private static TableEntry encodePiTableEntry(PiTableEntry piTableEntry, P4InfoBrowser browser)
throws P4InfoBrowser.NotFoundException, EncodeException {
TableEntry.Builder tableEntryMsgBuilder = TableEntry.newBuilder();
- //FIXME this thorws some kind of NPE
+ //FIXME this throws some kind of NPE
P4InfoOuterClass.Table tableInfo = browser.tables().getByName(piTableEntry.table().id());
// Table id.
@@ -193,11 +264,7 @@
// FIXME: how to decode table entry messages with timeout, given that the timeout value is lost after encoding?
// Match key for field matches.
- PiMatchKey.Builder piMatchKeyBuilder = PiMatchKey.builder();
- for (FieldMatch fieldMatchMsg : tableEntryMsg.getMatchList()) {
- piMatchKeyBuilder.addFieldMatch(decodeFieldMatchMsg(fieldMatchMsg, tableInfo, browser));
- }
- piTableEntryBuilder.withMatchKey(piMatchKeyBuilder.build());
+ piTableEntryBuilder.withMatchKey(decodeFieldMatchMsgs(tableEntryMsg.getMatchList(), tableInfo, browser));
return piTableEntryBuilder.build();
}
@@ -280,6 +347,33 @@
}
}
+ /**
+ * Returns a PI match key, decoded from the given table entry protobuf message, for the given pipeconf.
+ *
+ * @param tableEntryMsg table entry message
+ * @param pipeconf pipeconf
+ * @return PI match key
+ * @throws EncodeException if message cannot be decoded
+ * @throws P4InfoBrowser.NotFoundException if the required information cannot be find in the pipeconf's P4info
+ */
+ static PiMatchKey decodeMatchKey(TableEntry tableEntryMsg, PiPipeconf pipeconf)
+ throws P4InfoBrowser.NotFoundException, EncodeException {
+ P4InfoBrowser browser = PipeconfHelper.getP4InfoBrowser(pipeconf);
+ P4InfoOuterClass.Table tableInfo = browser.tables().getById(tableEntryMsg.getTableId());
+ return decodeFieldMatchMsgs(tableEntryMsg.getMatchList(), tableInfo, browser);
+ }
+
+ private static PiMatchKey decodeFieldMatchMsgs(Collection<FieldMatch> fieldMatchs, P4InfoOuterClass.Table tableInfo,
+ P4InfoBrowser browser)
+ throws P4InfoBrowser.NotFoundException, EncodeException {
+ // Match key for field matches.
+ PiMatchKey.Builder piMatchKeyBuilder = PiMatchKey.builder();
+ for (FieldMatch fieldMatchMsg : fieldMatchs) {
+ piMatchKeyBuilder.addFieldMatch(decodeFieldMatchMsg(fieldMatchMsg, tableInfo, browser));
+ }
+ return piMatchKeyBuilder.build();
+ }
+
private static PiFieldMatch decodeFieldMatchMsg(FieldMatch fieldMatchMsg, P4InfoOuterClass.Table tableInfo,
P4InfoBrowser browser)
throws P4InfoBrowser.NotFoundException, EncodeException {