ONOS-7066 ONOS-7067 PI abstractions refactoring and P4Info model parser

Includes changes previously reviewed in #15607, #15877, and #15955.

Change-Id: Ie2ff62e415f2099832ebfe05961a879b7b188fc3
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
index 6300660..bba3338 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiAction.java
@@ -21,6 +21,8 @@
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
+import org.onosproject.net.pi.model.PiActionId;
+import org.onosproject.net.pi.model.PiActionParamId;
 
 import java.util.Collection;
 import java.util.Map;
@@ -29,8 +31,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Instance of an action, and its runtime parameters, of a table entry in a protocol-independent
- * pipeline.
+ * Instance of an action, and its runtime parameters, of a table entry in a protocol-independent pipeline.
  */
 @Beta
 public final class PiAction implements PiTableAction {
@@ -64,8 +65,8 @@
     }
 
     /**
-     * Returns all runtime parameters of this action.
-     * Return an empty collection if the action doesn't take any runtime parameters.
+     * Returns all runtime parameters of this action. Return an empty collection if the action doesn't take any runtime
+     * parameters.
      *
      * @return list of byte sequences
      */
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
index 1de4d61..1d9a94b 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroup.java
@@ -21,6 +21,8 @@
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
+import org.onosproject.net.pi.model.PiActionGroupType;
+import org.onosproject.net.pi.model.PiActionProfileId;
 
 import java.util.Collection;
 import java.util.Map;
@@ -29,27 +31,17 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Action group of a protocol-independent pipeline.
+ * Instance of an action group of a protocol-independent pipeline.
  */
 @Beta
 public final class PiActionGroup {
 
-    /**
-     * Type of action group.
-     */
-    public enum Type {
-        /**
-         * Load-balancing among different members in a group.
-         */
-        SELECT
-    }
-
     private final PiActionGroupId id;
-    private final Type type;
+    private final PiActionGroupType type;
     private final ImmutableSet<PiActionGroupMember> members;
     private final PiActionProfileId piActionProfileId;
 
-    private PiActionGroup(PiActionGroupId id, Type type,
+    private PiActionGroup(PiActionGroupId id, PiActionGroupType type,
                           ImmutableSet<PiActionGroupMember> members,
                           PiActionProfileId piActionProfileId) {
         this.id = id;
@@ -72,7 +64,7 @@
      *
      * @return action group type
      */
-    public Type type() {
+    public PiActionGroupType type() {
         return type;
     }
 
@@ -139,7 +131,7 @@
     public static final class Builder {
 
         private PiActionGroupId id;
-        private Type type;
+        private PiActionGroupType type;
         private Map<PiActionGroupMemberId, PiActionGroupMember> members = Maps.newHashMap();
         private PiActionProfileId piActionProfileId;
 
@@ -164,7 +156,7 @@
          * @param type action group type
          * @return this
          */
-        public Builder withType(Type type) {
+        public Builder withType(PiActionGroupType type) {
             this.type = type;
             return this;
         }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupId.java
index 380a4a1..b604237 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupId.java
@@ -20,7 +20,7 @@
 import org.onlab.util.Identifier;
 
 /**
- * Identifier of an action group in a protocol-independent pipeline.
+ * Identifier of an action group in a protocol-independent pipeline, unique within the scope of an action profile.
  */
 @Beta
 public final class PiActionGroupId extends Identifier<Integer> implements PiTableAction {
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMember.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMember.java
index b5bd542..2905eb9 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMember.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMember.java
@@ -23,7 +23,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Member of an action group in a protocol-independent pipeline.
+ * Instance of a member of an action group in a protocol-independent pipeline.
  */
 @Beta
 public final class PiActionGroupMember {
@@ -57,8 +57,8 @@
     }
 
     /**
-     * Returns the weight associated to this member. Valid if the action group is of type {@link
-     * PiActionGroup.Type#SELECT}.
+     * Returns the weight associated to this member. Meaningful if the action group of this member is of type {@link
+     * org.onosproject.net.pi.model.PiActionGroupType#SELECT}.
      *
      * @return weight
      */
@@ -139,7 +139,8 @@
         }
 
         /**
-         * Sets the weight of this member. Valid if the action group is of type {@link PiActionGroup.Type#SELECT}.
+         * Sets the weight of this member. Meaningful only if the action group is of type {@link
+         * org.onosproject.net.pi.model.PiActionGroupType#SELECT}.
          * <p>
          * Default value is 0.
          *
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMemberId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMemberId.java
index 98b9d31..3b233a0 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMemberId.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionGroupMemberId.java
@@ -20,7 +20,8 @@
 import org.onlab.util.Identifier;
 
 /**
- * Identifier of a member of an action group in a protocol-independent pipeline.
+ * Identifier of a member of an action group in a protocol-independent pipeline, unique withing the scope on an action
+ * profile.
  */
 @Beta
 public final class PiActionGroupMemberId extends Identifier<Integer> implements PiTableAction {
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionId.java
deleted file mode 100644
index 7059fb1..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionId.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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;
-
-/**
- * Identifier of an action of a match+action table in a protocol-independent pipeline.
- */
-@Beta
-public final class PiActionId extends Identifier<String> {
-
-    /**
-     * Creates an action identifier.
-     *
-     * @param name action name
-     */
-    private PiActionId(String name) {
-        super(name);
-    }
-
-    /**
-     * Returns the name of the action.
-     *
-     * @return action name
-     */
-    public String name() {
-        return this.identifier;
-    }
-
-    /**
-     * Returns an action identifier with the given name.
-     *
-     * @param name action name
-     * @return action identifier
-     */
-    public static PiActionId of(String name) {
-        checkNotNull(name);
-        checkArgument(!name.isEmpty(), "Name can't be empty");
-        return new PiActionId(name);
-    }
-}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParam.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParam.java
index 2e4ced3..4498109 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParam.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParam.java
@@ -19,12 +19,13 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiActionParamId;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Runtime parameter of an action in a match+action table of a protocol-independent pipeline.
+ * Instance of an action runtime parameter in a match+action table of a protocol-independent pipeline.
  */
 @Beta
 public final class PiActionParam {
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParamId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParamId.java
deleted file mode 100644
index 70279ab..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionParamId.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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;
-
-/**
- * Identifier of an action's runtime parameter in a match+action table of a protocol-independent pipeline.
- */
-@Beta
-public final class PiActionParamId extends Identifier<String> {
-
-    private final String name;
-
-    // TODO: if needed, we might add here support for positional parameters.
-    // E.g. add a second constructor that takes the "position" of a parameter, vs. its name.
-
-    private PiActionParamId(String name) {
-        super(name);
-        this.name = name;
-    }
-
-    /**
-     * Returns the name of this parameter.
-     *
-     * @return parameter name
-     */
-    public String name() {
-        return name;
-    }
-
-    /**
-     * Returns a parameter identifier with the given name.
-     *
-     * @param name parameter name
-     * @return parameter identifier
-     */
-    public static PiActionParamId of(String name) {
-        checkNotNull(name);
-        checkArgument(!name.isEmpty(), "Name can't be empty");
-        return new PiActionParamId(name);
-    }
-}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileId.java
deleted file mode 100644
index e0158c9..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiActionProfileId.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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;
-
-/**
- * Identifier of an action profile of a protocol-independent pipeline.
- */
-@Beta
-public final class PiActionProfileId extends Identifier<String> {
-
-    private PiActionProfileId(String actionProfileName) {
-        super(actionProfileName);
-    }
-
-    /**
-     * Returns action profile id with given action profile name.
-     *
-     * @param actionProfileName action profile name
-     * @return action profile id
-     */
-    public static PiActionProfileId of(String actionProfileName) {
-        return new PiActionProfileId(actionProfileName);
-    }
-}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiControlMetadata.java
similarity index 70%
rename from core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java
rename to core/api/src/main/java/org/onosproject/net/pi/runtime/PiControlMetadata.java
index 118195b..3caa71c 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadata.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiControlMetadata.java
@@ -19,35 +19,36 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiControlMetadataId;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Instance of a metadata for a packet I/O operation, with id and value for a protocol-independent pipeline.
+ * Instance of a control metadata for a protocol-independent pipeline.
  */
 @Beta
-public final class PiPacketMetadata {
+public final class PiControlMetadata {
 
-    private final PiPacketMetadataId id;
+    private final PiControlMetadataId id;
     private final ImmutableByteSequence value;
 
     /**
-     * Creates a new packet metadata instance for the given identifier and value.
+     * Creates a new control metadata instance for the given identifier and value.
      *
-     * @param id    packet metadata identifier
+     * @param id    control metadata identifier
      * @param value value for this metadata
      */
-    private PiPacketMetadata(PiPacketMetadataId id, ImmutableByteSequence value) {
+    private PiControlMetadata(PiControlMetadataId id, ImmutableByteSequence value) {
         this.id = id;
         this.value = value;
     }
 
     /**
-     * Return the identifier of this packet metadata.
+     * Return the identifier of this control metadata.
      *
-     * @return packet metadata identifier
+     * @return control metadata identifier
      */
-    public PiPacketMetadataId id() {
+    public PiControlMetadataId id() {
         return id;
     }
 
@@ -68,7 +69,7 @@
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        PiPacketMetadata piPacket = (PiPacketMetadata) o;
+        PiControlMetadata piPacket = (PiControlMetadata) o;
         return Objects.equal(id, piPacket.id()) &&
                 Objects.equal(value, piPacket.value());
     }
@@ -84,7 +85,7 @@
     }
 
     /**
-     * Returns a packet metadata builder.
+     * Returns a control metadata builder.
      *
      * @return a new builder
      */
@@ -93,11 +94,11 @@
     }
 
     /**
-     * Builder of protocol-independent packet metadatas.
+     * Builder of protocol-independent control metadatas.
      */
     public static final class Builder {
 
-        private PiPacketMetadataId id;
+        private PiControlMetadataId id;
         private ImmutableByteSequence value;
 
         private Builder() {
@@ -105,12 +106,12 @@
         }
 
         /**
-         * Sets the identifier of this packet metadata.
+         * Sets the identifier of this control metadata.
          *
-         * @param id packet metadata identifier
+         * @param id control metadata identifier
          * @return this
          */
-        public Builder withId(PiPacketMetadataId id) {
+        public Builder withId(PiControlMetadataId id) {
             this.id = id;
             return this;
         }
@@ -127,14 +128,14 @@
         }
 
         /**
-         * Returns a new packet metadata instance.
+         * Returns a new control metadata instance.
          *
-         * @return packet metadata
+         * @return control metadata
          */
-        public PiPacketMetadata build() {
+        public PiControlMetadata build() {
             checkNotNull(id);
             checkNotNull(value);
-            return new PiPacketMetadata(id, value);
+            return new PiControlMetadata(id, value);
         }
     }
 }
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 cd88d2a..da8a882 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,22 +16,120 @@
 
 package org.onosproject.net.pi.runtime;
 
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onosproject.net.pi.model.PiCounterId;
+import org.onosproject.net.pi.model.PiCounterType;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
 /**
  * Identifier of a counter cell in a protocol-independent pipeline.
  */
-public interface PiCounterCellId {
+@Beta
+public final class PiCounterCellId {
+
+    private final PiCounterId counterId;
+    private final PiCounterType counterType;
+    private final long index;
+    private final PiTableEntry tableEntry;
+
+    private PiCounterCellId(PiCounterId counterId, PiCounterType counterType, long index,
+                            PiTableEntry tableEntry) {
+        this.counterId = counterId;
+        this.counterType = counterType;
+        this.index = index;
+        this.tableEntry = tableEntry;
+    }
 
     /**
      * Returns the identifier of the counter instance where this cell is contained.
      *
      * @return counter identifier
      */
-    PiCounterId counterId();
+    public PiCounterId counterId() {
+        return counterId;
+    }
 
     /**
-     * Returns the type of counter identified.
+     * Returns the type of the counter identified.
      *
      * @return counter type
      */
-    PiCounterType type();
+    public PiCounterType counterType() {
+        return counterType;
+    }
+
+    /**
+     * Returns the counter index to which this cell ID is associated. Meaningful only if the counter is of type {@link
+     * PiCounterType#INDIRECT}.
+     *
+     * @return counter index
+     */
+    public long index() {
+        return index;
+    }
+
+    /**
+     * Returns the table entry to which this cell ID is associated. Meaningful only if the counter is of type {@link
+     * PiCounterType#DIRECT}, otherwise returns null.
+     *
+     * @return PI table entry or null
+     */
+    public PiTableEntry tableEntry() {
+        return tableEntry;
+    }
+
+    /**
+     * Return a direct counter cell ID for the given counter ID and table entry.
+     *
+     * @param counterId  counter ID
+     * @param tableEntry table entry
+     * @return counter cell ID
+     */
+    public static PiCounterCellId ofDirect(PiCounterId counterId, PiTableEntry tableEntry) {
+        checkNotNull(counterId);
+        checkNotNull(tableEntry);
+        return new PiCounterCellId(counterId, PiCounterType.DIRECT, -1, tableEntry);
+    }
+
+    /**
+     * Return an indirect counter cell ID for the given counter ID and index.
+     *
+     * @param counterId counter ID
+     * @param index     index
+     * @return counter cell ID
+     */
+    public static PiCounterCellId ofIndirect(PiCounterId counterId, long index) {
+        checkNotNull(counterId);
+        checkArgument(index >= 0, "Index must be a positive number");
+        return new PiCounterCellId(counterId, PiCounterType.INDIRECT, index, null);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null || getClass() != obj.getClass()) {
+            return false;
+        }
+        final PiCounterCellId other = (PiCounterCellId) obj;
+        return Objects.equal(this.counterId, other.counterId)
+                && Objects.equal(this.counterType, other.counterType)
+                && Objects.equal(this.index, other.index)
+                && Objects.equal(this.tableEntry, other.tableEntry);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(counterId, counterType, index, tableEntry);
+    }
+
+    @Override
+    public String toString() {
+        return counterId.toString() + ':'
+                + (counterType == PiCounterType.DIRECT ? tableEntry.toString() : String.valueOf(index));
+    }
 }
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
deleted file mode 100644
index 015d86b..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterId.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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 org.onlab.util.Identifier;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Identifier of a counter of a protocol-independent pipeline.
- */
-@Beta
-public final class PiCounterId extends Identifier<String> {
-
-    private final String scope;
-    private final String name;
-    private final PiCounterType type;
-
-    private PiCounterId(String scope, String name, PiCounterType type) {
-        super((!scope.isEmpty() ? scope + "." : "") + name);
-        this.scope = scope;
-        this.name = name;
-        this.type = type;
-    }
-
-    /**
-     * 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, PiCounterType type) {
-        checkNotNull(name);
-        checkNotNull(type);
-        checkArgument(!name.isEmpty(), "Name can't be empty");
-        return new PiCounterId("", name, type);
-    }
-
-    /**
-     * Returns a counter identifier for the given scope, name, and type.
-     *
-     * @param scope counter scope
-     * @param name  counter name
-     * @param type  counter type
-     * @return counter identifier
-     */
-    public static PiCounterId of(String scope, String name, PiCounterType type) {
-        checkNotNull(scope);
-        checkNotNull(name);
-        checkNotNull(type);
-        checkArgument(!scope.isEmpty(), "Scope can't be empty");
-        checkArgument(!name.isEmpty(), "Name can't be empty");
-        return new PiCounterId(scope, name, type);
-    }
-
-    /**
-     * Returns the scope of the counter.
-     *
-     * @return counter scope
-     */
-    public String scope() {
-        return this.scope;
-    }
-
-    /**
-     * Returns the name of the counter.
-     *
-     * @return counter name
-     */
-    public String name() {
-        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(id(), that.id()) &&
-                type == that.type;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(id(), type);
-    }
-}
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
deleted file mode 100644
index b4a709a..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiCounterType.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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
deleted file mode 100644
index 26ae363..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiDirectCounterCellId.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * 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/PiExactFieldMatch.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiExactFieldMatch.java
index 946f6c1..d81da99 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiExactFieldMatch.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiExactFieldMatch.java
@@ -19,13 +19,14 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Exact field match in a protocol-independent pipeline.
+ * Instance of an exact field match in a protocol-independent pipeline.
  */
 @Beta
 public final class PiExactFieldMatch extends PiFieldMatch {
@@ -38,7 +39,7 @@
      * @param fieldId field identifier
      * @param value   value
      */
-    public PiExactFieldMatch(PiHeaderFieldId fieldId, ImmutableByteSequence value) {
+    public PiExactFieldMatch(PiMatchFieldId fieldId, ImmutableByteSequence value) {
         super(fieldId);
         this.value = checkNotNull(value);
         checkArgument(value.size() > 0, "Value can't have size 0");
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiFieldMatch.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiFieldMatch.java
index 8df0452..aa34f22 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiFieldMatch.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiFieldMatch.java
@@ -16,23 +16,24 @@
 
 package org.onosproject.net.pi.runtime;
 
+import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Header's field match in a protocol-independent pipeline.
+ * Instance of a field match in a protocol-independent pipeline.
  */
 public abstract class PiFieldMatch {
 
-    private final PiHeaderFieldId fieldId;
+    private final PiMatchFieldId fieldId;
 
     /**
      * Creates a new field match for the given header field identifier.
      *
      * @param fieldId field identifier.
      */
-    PiFieldMatch(PiHeaderFieldId fieldId) {
+    PiFieldMatch(PiMatchFieldId fieldId) {
         this.fieldId = checkNotNull(fieldId);
     }
 
@@ -42,7 +43,7 @@
      *
      * @return a header field ID value
      */
-    public final PiHeaderFieldId fieldId() {
+    public final PiMatchFieldId fieldId() {
         return fieldId;
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiGroupKey.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiGroupKey.java
index 598f3e7..14fec81 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiGroupKey.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiGroupKey.java
@@ -18,6 +18,8 @@
 
 import com.google.common.base.Objects;
 import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.pi.model.PiActionProfileId;
+import org.onosproject.net.pi.model.PiTableId;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHeaderFieldId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHeaderFieldId.java
deleted file mode 100644
index 99f7e75..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiHeaderFieldId.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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 org.onlab.util.Identifier;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Identifier of a packet's header field.
- */
-public final class PiHeaderFieldId extends Identifier<String> {
-
-    // FIXME: this abstraction is brittle and we should drop any string-composition logic.
-    // e.g. in P4_14 there is no scope for match fields.
-    // In light of ONOS-7066, the best solution seems to have IDs defined as arbitrary
-    // strings equal to the entity names defined in the P4Info.
-    private final String headerName;
-    private final String fieldName;
-    private final int index;
-
-    private PiHeaderFieldId(String headerName, String fieldName, int index) {
-        super(headerName +
-                      (index > 0 ? "[" + String.valueOf(index) + "]" : "") +
-                      "." + fieldName);
-        this.headerName = headerName;
-        this.fieldName = fieldName;
-        this.index = index;
-    }
-
-    /**
-     * Returns an header field identifier for the given header name, field name and index.
-     * <p>
-     * Index represents the position of this header in the packet w.r.t. to other headers of the
-     * same type. Index 0 points to the first instance of the header, 1 the second one, etc. Helpful
-     * when dealing with stacked headers, e.g. to match on the second MPLS label.
-     *
-     * @param headerName header name
-     * @param fieldName  field name
-     * @param index      index
-     * @return header field identifier
-     */
-    public static PiHeaderFieldId of(String headerName, String fieldName, int index) {
-        checkNotNull(headerName);
-        checkNotNull(fieldName);
-        checkArgument(!headerName.isEmpty(), "Header name can't be empty");
-        checkArgument(!fieldName.isEmpty(), "Field name can't be empty");
-        checkArgument(index >= 0, "Index must be a positive integer");
-        return new PiHeaderFieldId(headerName, fieldName, index);
-    }
-
-    /**
-     * Returns an header field identifier for the given header name and field name.
-     * Index is set to default value 0.
-     *
-     * @param headerName header name
-     * @param fieldName  field name
-     * @return header field identifier
-     */
-    public static PiHeaderFieldId of(String headerName, String fieldName) {
-        return of(headerName, fieldName, 0);
-    }
-
-    /**
-     * Returns the name of the header.
-     *
-     * @return a string value
-     */
-    public String headerName() {
-        return headerName;
-    }
-
-    /**
-     * Returns the name of the field.
-     *
-     * @return a string value
-     */
-    public String fieldName() {
-        return fieldName;
-    }
-
-    /**
-     * Returns the index of this header.
-     *
-     * @return an integer value.
-     */
-    public int index() {
-        return index;
-    }
-}
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
deleted file mode 100644
index 6f3f73a..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiIndirectCounterCellId.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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/PiLpmFieldMatch.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiLpmFieldMatch.java
index 87b8723..24ef957 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiLpmFieldMatch.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiLpmFieldMatch.java
@@ -19,13 +19,14 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Longest-prefix field match in a protocol-independent pipeline.
+ * Instance of a longest-prefix field match in a protocol-independent pipeline.
  */
 @Beta
 public final class PiLpmFieldMatch extends PiFieldMatch {
@@ -40,7 +41,7 @@
      * @param value        value
      * @param prefixLength prefix length
      */
-    public PiLpmFieldMatch(PiHeaderFieldId fieldId, ImmutableByteSequence value, int prefixLength) {
+    public PiLpmFieldMatch(PiMatchFieldId fieldId, ImmutableByteSequence value, int prefixLength) {
         super(fieldId);
         this.value = checkNotNull(value);
         this.prefixLength = prefixLength;
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 e2e8c3d..a978bfa 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
@@ -19,6 +19,7 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableMap;
+import org.onosproject.net.pi.model.PiMatchFieldId;
 
 import java.util.Collection;
 import java.util.Optional;
@@ -32,9 +33,9 @@
 
     public static final PiMatchKey EMPTY = builder().build();
 
-    private final ImmutableMap<PiHeaderFieldId, PiFieldMatch> fieldMatches;
+    private final ImmutableMap<PiMatchFieldId, PiFieldMatch> fieldMatches;
 
-    private PiMatchKey(ImmutableMap<PiHeaderFieldId, PiFieldMatch> fieldMatches) {
+    private PiMatchKey(ImmutableMap<PiMatchFieldId, PiFieldMatch> fieldMatches) {
         this.fieldMatches = fieldMatches;
     }
 
@@ -53,7 +54,7 @@
      * @param fieldId field identifier
      * @return optional field match
      */
-    public Optional<PiFieldMatch> fieldMatch(PiHeaderFieldId fieldId) {
+    public Optional<PiFieldMatch> fieldMatch(PiMatchFieldId fieldId) {
         return Optional.ofNullable(fieldMatches.get(fieldId));
     }
 
@@ -95,7 +96,7 @@
      */
     public static final class Builder {
 
-        private final ImmutableMap.Builder<PiHeaderFieldId, PiFieldMatch> fieldMatchesBuilder = ImmutableMap.builder();
+        private final ImmutableMap.Builder<PiMatchFieldId, PiFieldMatch> fieldMatchesBuilder = ImmutableMap.builder();
 
         private Builder() {
             // hides constructor.
@@ -129,7 +130,7 @@
          * @return match key
          */
         public PiMatchKey build() {
-            ImmutableMap<PiHeaderFieldId, PiFieldMatch> fieldMatches = fieldMatchesBuilder.build();
+            ImmutableMap<PiMatchFieldId, PiFieldMatch> fieldMatches = fieldMatchesBuilder.build();
             return new PiMatchKey(fieldMatches);
         }
     }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java
deleted file mode 100644
index 55bdf5c..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketMetadataId.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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;
-
-/**
- * Identifier of a metadata for a packet I/O operation in a protocol-independent pipeline.
- */
-@Beta
-public final class PiPacketMetadataId extends Identifier<String> {
-
-    /**
-     * Creates a packet metadata identifier.
-     *
-     * @param name packet metadata name
-     */
-    private PiPacketMetadataId(String name) {
-        super(name);
-    }
-
-    /**
-     * Returns the name of the packet metadata.
-     *
-     * @return packet metadata name
-     */
-    public String name() {
-        return this.identifier;
-    }
-
-    /**
-     * Returns a identifier with the given name.
-     *
-     * @param name packet metadata name
-     * @return packet metadata identifier
-     */
-    public static PiPacketMetadataId of(String name) {
-        checkNotNull(name);
-        checkArgument(!name.isEmpty(), "Name can't be empty");
-        return new PiPacketMetadataId(name);
-    }
-}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java
index 4bbab76..74c68d6 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPacketOperation.java
@@ -21,6 +21,9 @@
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
 import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.pi.model.PiControlMetadataId;
+import org.onosproject.net.pi.model.PiPacketOperationType;
 
 import java.util.Collection;
 import java.util.HashMap;
@@ -30,46 +33,48 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Instance of a packet I/O operation, and its metadatas for a protocol-independent pipeline.
+ * Instance of a packet I/O operation, and its control metadatas, for a protocol-independent pipeline.
  */
 @Beta
 public final class PiPacketOperation {
 
-    public enum Type {
-        /**
-         * Represents a packet out.
-         */
-        PACKET_OUT,
-
-        /**
-         * Represents a packet in.
-         */
-        PACKET_IN,
-    }
-
+    private final DeviceId deviceId;
     private final ImmutableByteSequence data;
-    private final Set<PiPacketMetadata> packetMetadatas;
-    private final PiPacketOperation.Type type;
+    private final Set<PiControlMetadata> packetMetadatas;
+    private final PiPacketOperationType type;
 
     /**
-     * Creates a new packet I/O operation for the given data, metadatas and operation type.
+     * Creates a new packet I/O operation for the given device ID, data, control metadatas and operation type.
      *
+     * @param deviceId        device ID
      * @param data            the packet raw data
-     * @param packetMetadatas set of packet metadata
+     * @param packetMetadatas collection of control metadata
      * @param type            type of this packet operation
      */
-    private PiPacketOperation(ImmutableByteSequence data, Collection<PiPacketMetadata> packetMetadatas, Type type) {
+    private PiPacketOperation(DeviceId deviceId, ImmutableByteSequence data,
+                              Collection<PiControlMetadata> packetMetadatas,
+                              PiPacketOperationType type) {
+        this.deviceId = deviceId;
         this.data = data;
         this.packetMetadatas = ImmutableSet.copyOf(packetMetadatas);
         this.type = type;
     }
 
     /**
+     * Returns the device ID of this packet operation.
+     *
+     * @return device ID
+     */
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    /**
      * Return the type of this packet.
      *
      * @return packet type
      */
-    public Type type() {
+    public PiPacketOperationType type() {
         return type;
     }
 
@@ -83,12 +88,11 @@
     }
 
     /**
-     * Returns all metadatas of this packet.
-     * Returns an empty collection if the packet doesn't have any metadata.
+     * Returns all metadatas of this packet. Returns an empty collection if the packet doesn't have any metadata.
      *
      * @return collection of metadatas
      */
-    public Collection<PiPacketMetadata> metadatas() {
+    public Collection<PiControlMetadata> metadatas() {
         return packetMetadatas;
     }
 
@@ -102,25 +106,27 @@
         }
         PiPacketOperation that = (PiPacketOperation) o;
         return Objects.equal(packetMetadatas, that.packetMetadatas) &&
+                Objects.equal(deviceId, that.deviceId) &&
                 Objects.equal(data, that.data()) &&
                 Objects.equal(type, that.type());
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(data, packetMetadatas, type);
+        return Objects.hashCode(deviceId, data, packetMetadatas, type);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
-                .addValue(packetMetadatas)
+                .add("deviceId", deviceId)
                 .addValue(type.toString())
+                .addValue(packetMetadatas)
                 .toString();
     }
 
     /**
-     * Returns an packet builder.
+     * Returns a new builder of packet operations.
      *
      * @return a new builder
      */
@@ -129,12 +135,13 @@
     }
 
     /**
-     * Builder of protocol-independent packets.
+     * Builder of packet operations.
      */
     public static final class Builder {
 
-        private Map<PiPacketMetadataId, PiPacketMetadata> packetMetadatas = new HashMap<>();
-        private PiPacketOperation.Type type;
+        private DeviceId deviceId;
+        private Map<PiControlMetadataId, PiControlMetadata> packetMetadatas = new HashMap<>();
+        private PiPacketOperationType type;
         private ImmutableByteSequence data;
 
         private Builder() {
@@ -142,7 +149,19 @@
         }
 
         /**
-         * Adds the raw packet data.
+         * Sets the device ID.
+         *
+         * @param deviceId device ID
+         * @return this
+         */
+        public Builder forDevice(DeviceId deviceId) {
+            checkNotNull(deviceId);
+            this.deviceId = deviceId;
+            return this;
+        }
+
+        /**
+         * Sets the raw packet data.
          *
          * @param data the packet raw data
          * @return this
@@ -154,14 +173,13 @@
         }
 
         /**
-         * Adds a metadata.
-         * Only one metadata is allowed for a given metadata id.
-         * If a metadata with same id already exists it will be replaced by the given one.
+         * Adds a control metadata. Only one metadata is allowed for a given metadata id. If a metadata with same id
+         * already exists it will be replaced by the given one.
          *
          * @param metadata packet metadata
          * @return this
          */
-        public Builder withMetadata(PiPacketMetadata metadata) {
+        public Builder withMetadata(PiControlMetadata metadata) {
             checkNotNull(metadata);
             packetMetadatas.put(metadata.id(), metadata);
 
@@ -174,7 +192,7 @@
          * @param metadatas collection of metadata
          * @return this
          */
-        public Builder withMetadatas(Collection<PiPacketMetadata> metadatas) {
+        public Builder withMetadatas(Collection<PiControlMetadata> metadatas) {
             checkNotNull(metadatas);
             metadatas.forEach(this::withMetadata);
             return this;
@@ -186,21 +204,22 @@
          * @param type type of the packet
          * @return this
          */
-        public Builder withType(Type type) {
+        public Builder withType(PiPacketOperationType type) {
             this.type = type;
             return this;
         }
 
         /**
-         * Returns a new packet instance.
+         * Builds a new instance of a packet operation.
          *
-         * @return packet
+         * @return packet operation
          */
         public PiPacketOperation build() {
+            checkNotNull(deviceId);
             checkNotNull(data);
             checkNotNull(packetMetadatas);
             checkNotNull(type);
-            return new PiPacketOperation(data, packetMetadatas.values(), type);
+            return new PiPacketOperation(deviceId, data, packetMetadatas.values(), type);
         }
     }
 }
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfConfig.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfConfig.java
index 0dfaad9..a04dbf2 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfConfig.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfConfig.java
@@ -21,7 +21,7 @@
 import org.onosproject.net.pi.model.PiPipeconfId;
 
 /**
- * Configuration fot the PiPipeconf susbystem.
+ * Configuration for the PiPipeconf susbystem.
  */
 @Beta
 public class PiPipeconfConfig extends Config<DeviceId> {
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfService.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfService.java
index 79bc624..2c6c66c 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfService.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiPipeconfService.java
@@ -41,10 +41,9 @@
     void register(PiPipeconf pipeconf) throws IllegalStateException;
 
     /**
-     * Unregisters the Pipeconf identified by the given PiPipeconfId.
-     * Unregistering a Pipeconf removes it from the ONOS controller, thus making it un-capable
-     * of controlling (e.g installing flow rules) the devices that have the pipeconf's p4 program deployed.
-     * For now this method DOES NOT remove the p4 program from the devices.
+     * Unregisters the Pipeconf identified by the given PiPipeconfId. Unregistering a Pipeconf removes it from the ONOS
+     * controller, thus making it un-capable of controlling (e.g installing flow rules) the devices that have the
+     * pipeconf's P4 program deployed. For now this method DOES NOT remove the P4 program from the devices.
      *
      * @param pipeconfId a pipeconfId
      * @throws IllegalStateException if the same pipeconf identifier is already registered.
@@ -59,8 +58,8 @@
     Iterable<PiPipeconf> getPipeconfs();
 
     /**
-     * Returns the pipeconf instance associated with the given identifier, if present.
-     * If not present, it means that no pipeconf with such identifier has been registered so far.
+     * Returns the pipeconf instance associated with the given identifier, if present. If not present, it means that no
+     * pipeconf with such identifier has been registered so far.
      *
      * @param id a pipeconf identifier
      * @return an optional pipeconf
@@ -68,10 +67,9 @@
     Optional<PiPipeconf> getPipeconf(PiPipeconfId id);
 
     /**
-     * Binds the given pipeconf to the given infrastructure device. As a result of this method call,
-     * if the given pipeconf exposes any pipeline-specific behaviours, those will be merged to the
-     * device's driver. Returns a completable future to provide async methods with a boolean if the merge
-     * of the drivers succeeded.
+     * Binds the given pipeconf to the given infrastructure device. As a result of this method call, if the given
+     * pipeconf exposes any pipeline-specific behaviours, those will be merged to the device's driver. Returns a
+     * completable future to provide async methods with a boolean if the merge of the drivers succeeded.
      *
      * @param deviceId   a device identifier
      * @param pipeconfId a pipeconf identifier
@@ -82,8 +80,8 @@
     CompletableFuture<Boolean> bindToDevice(PiPipeconfId pipeconfId, DeviceId deviceId);
 
     /**
-     * Returns the pipeconf identifier currently associated with the given device identifier, if
-     * present. If not present, it means no pipeconf has been associated with that device so far.
+     * Returns the pipeconf identifier currently associated with the given device identifier, if present. If not
+     * present, it means no pipeconf has been associated with that device so far.
      *
      * @param deviceId device identifier
      * @return an optional pipeconf identifier
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiRangeFieldMatch.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiRangeFieldMatch.java
index b6c39aa..0905d9c 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiRangeFieldMatch.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiRangeFieldMatch.java
@@ -19,13 +19,14 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Range field match in a protocol-independent pipeline.
+ * Instance of a range field match in a protocol-independent pipeline.
  */
 @Beta
 public final class PiRangeFieldMatch extends PiFieldMatch {
@@ -40,7 +41,7 @@
      * @param lowValue  low value
      * @param highValue high value
      */
-    public PiRangeFieldMatch(PiHeaderFieldId fieldId, ImmutableByteSequence lowValue,
+    public PiRangeFieldMatch(PiMatchFieldId fieldId, ImmutableByteSequence lowValue,
                              ImmutableByteSequence highValue) {
         super(fieldId);
         this.lowValue = checkNotNull(lowValue);
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java
index cacb49e..7d25e1a 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableAction.java
@@ -19,19 +19,15 @@
 import com.google.common.annotations.Beta;
 
 /**
- * An action that can be executed as a consequence of a match in a match+action table of a protocol-independent
- * pipeline.
+ * Instance of an action that can be executed as a consequence of a match in a match+action table of a
+ * protocol-independent pipeline.
  */
 @Beta
 public interface PiTableAction {
 
     /**
-     * Type of this action.
-     *
-     * @return a type
+     * Types of table action.
      */
-    Type type();
-
     enum Type {
         /**
          * Simple action with runtime parameters set by the control plane.
@@ -48,4 +44,11 @@
          */
         GROUP_MEMBER_ID
     }
+
+    /**
+     * Type of this action.
+     *
+     * @return a type
+     */
+    Type type();
 }
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 6a5f75b..cd7e493 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
@@ -19,6 +19,7 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.MoreObjects;
 import com.google.common.base.Objects;
+import org.onosproject.net.pi.model.PiTableId;
 
 import java.util.Optional;
 
@@ -26,7 +27,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Table entry in a protocol-independent pipeline.
+ * Instance of a table entry in a protocol-independent pipeline.
  */
 @Beta
 public final class PiTableEntry {
@@ -99,8 +100,8 @@
     }
 
     /**
-     * Returns the priority of this table entry, if present.
-     * If the priority value is not present, then this table entry has no explicit priority.
+     * Returns the priority of this table entry, if present. If the priority value is not present, then this table entry
+     * has no explicit priority.
      *
      * @return optional priority
      */
@@ -109,8 +110,8 @@
     }
 
     /**
-     * Returns the timeout in seconds of this table entry, if present.
-     * If the timeout value is not present, then this table entry is meant to be permanent.
+     * Returns the timeout in seconds of this table entry, if present. If the timeout value is not present, then this
+     * table entry is meant to be permanent.
      *
      * @return optional timeout value in seconds
      */
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableId.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableId.java
deleted file mode 100644
index 7b988b1..0000000
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTableId.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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 org.onosproject.net.flow.TableId;
-
-import java.util.Optional;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Identifier of a table in a protocol-independent pipeline.
- */
-@Beta
-public final class PiTableId extends Identifier<String> implements TableId  {
-
-    private final String scope;
-    private final String name;
-
-    private PiTableId(String scope, String name) {
-        super((scope != null ? scope + "." : "") + name);
-        this.scope = scope;
-        this.name = name;
-    }
-
-    /**
-     * Returns a table identifier for the given table scope and name.
-     *
-     * @param scope table scope
-     * @param name  table name
-     * @return table identifier
-     */
-    public static PiTableId of(String scope, String name) {
-        checkNotNull(name);
-        checkNotNull(scope);
-        checkArgument(!name.isEmpty(), "Name can't be empty");
-        checkArgument(!scope.isEmpty(), "Scope can't be empty");
-        return new PiTableId(scope, name);
-    }
-
-    /**
-     * Returns a table identifier for the given table name.
-     *
-     * @param name table name
-     * @return table identifier
-     */
-    public static PiTableId of(String name) {
-        checkNotNull(name);
-        checkArgument(!name.isEmpty(), "Name can't be empty");
-        return new PiTableId(null, name);
-    }
-
-
-    /**
-     * Returns the name of this table.
-     *
-     * @return table name
-     */
-    public String name() {
-        return name;
-    }
-
-    /**
-     * Returns the scope of this table, if present.
-     *
-     * @return optional scope
-     */
-    public Optional<String> scope() {
-        return Optional.ofNullable(scope);
-    }
-
-    @Override
-    public Type type() {
-        return Type.PIPELINE_INDEPENDENT;
-    }
-}
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTernaryFieldMatch.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTernaryFieldMatch.java
index 29679ba..8406aed 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTernaryFieldMatch.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTernaryFieldMatch.java
@@ -19,13 +19,14 @@
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
 import org.onlab.util.ImmutableByteSequence;
+import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Ternary field match in a protocol-independent pipeline.
+ * Instance of a ternary field match in a protocol-independent pipeline.
  */
 @Beta
 public final class PiTernaryFieldMatch extends PiFieldMatch {
@@ -37,10 +38,10 @@
      * Creates a new ternary field match.
      *
      * @param fieldId field identifier
-     * @param value value
-     * @param mask  mask
+     * @param value   value
+     * @param mask    mask
      */
-    public PiTernaryFieldMatch(PiHeaderFieldId fieldId, ImmutableByteSequence value,
+    public PiTernaryFieldMatch(PiMatchFieldId fieldId, ImmutableByteSequence value,
                                ImmutableByteSequence mask) {
         super(fieldId);
         this.value = checkNotNull(value);
@@ -49,11 +50,6 @@
                       "Value and mask must have same non-zero size");
     }
 
-    @Override
-    public PiMatchType type() {
-        return PiMatchType.TERNARY;
-    }
-
     /**
      * Returns the value matched by this field.
      *
@@ -73,6 +69,11 @@
     }
 
     @Override
+    public PiMatchType type() {
+        return PiMatchType.TERNARY;
+    }
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) {
             return true;
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTranslationService.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTranslationService.java
index a0c1101..d099583 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTranslationService.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiTranslationService.java
@@ -22,7 +22,7 @@
 import org.onosproject.net.pi.model.PiPipeconf;
 
 /**
- * A service to translate ONOS entities to protocol-independent ones.
+ * A service to translate protocol-dependent entities to protocol-independent ones.
  */
 @Beta
 public interface PiTranslationService {
diff --git a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiValidFieldMatch.java b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiValidFieldMatch.java
index 8b172de..7894ba82 100644
--- a/core/api/src/main/java/org/onosproject/net/pi/runtime/PiValidFieldMatch.java
+++ b/core/api/src/main/java/org/onosproject/net/pi/runtime/PiValidFieldMatch.java
@@ -18,10 +18,11 @@
 
 import com.google.common.annotations.Beta;
 import com.google.common.base.Objects;
+import org.onosproject.net.pi.model.PiMatchFieldId;
 import org.onosproject.net.pi.model.PiMatchType;
 
 /**
- * A valid field match in a protocol-independent pipeline.
+ * Instance of a valid field match in a protocol-independent pipeline.
  */
 @Beta
 public final class PiValidFieldMatch extends PiFieldMatch {
@@ -34,7 +35,7 @@
      * @param fieldId field identifier
      * @param isValid validity flag
      */
-    public PiValidFieldMatch(PiHeaderFieldId fieldId, boolean isValid) {
+    public PiValidFieldMatch(PiMatchFieldId fieldId, boolean isValid) {
         super(fieldId);
         this.isValid = isValid;
     }