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