[AETHER-599] Implement FabricPipelineTraceable

Core changes supporting fabric traceable implementation.
Includes minor fixes to the OFDPA traceable unit tests

Change-Id: I2f0d1172022a8fc725df9e96526092c59ddc0e0b
diff --git a/core/api/src/main/java/org/onosproject/net/PipelineTraceableHitChain.java b/core/api/src/main/java/org/onosproject/net/PipelineTraceableHitChain.java
index 4eb26b5..6e5f566 100644
--- a/core/api/src/main/java/org/onosproject/net/PipelineTraceableHitChain.java
+++ b/core/api/src/main/java/org/onosproject/net/PipelineTraceableHitChain.java
@@ -17,7 +17,6 @@
 package org.onosproject.net;
 
 import com.google.common.collect.Lists;
-import org.onosproject.net.flow.TrafficSelector;
 
 import java.util.List;
 import java.util.Objects;
@@ -25,11 +24,11 @@
 /**
  * Class to represent the pipeline hit chain and the result of the pipeline processing.
  */
-public class PipelineTraceableHitChain {
+public final class PipelineTraceableHitChain {
 
     private ConnectPoint outputPort;
     private List<DataPlaneEntity> hitChain;
-    private TrafficSelector egressPacket;
+    private PipelineTraceablePacket egressPacket;
     // By default packets are dropped
     private boolean dropped = true;
 
@@ -42,10 +41,10 @@
      *
      * @param output   the output connect point
      * @param hits     the hits in the pipeline (flows, groups and other abstractions)
-     * @param packet   the selector representing the final packet
+     * @param packet   the traceable packet representing the final packet
      */
     public PipelineTraceableHitChain(ConnectPoint output, List<DataPlaneEntity> hits,
-                                     TrafficSelector packet) {
+                                     PipelineTraceablePacket packet) {
         this.outputPort = output;
         this.hitChain = hits;
         this.egressPacket = packet;
@@ -65,7 +64,7 @@
      *
      * @return the connect point
      */
-    public ConnectPoint getOutputPort() {
+    public ConnectPoint outputPort() {
         return outputPort;
     }
 
@@ -83,7 +82,7 @@
      *
      * @return flows and groups that matched.
      */
-    public List<DataPlaneEntity> getHitChain() {
+    public List<DataPlaneEntity> hitChain() {
         return hitChain;
     }
 
@@ -113,9 +112,9 @@
     /**
      * Returns the egress packet after traversing the pipeline.
      *
-     * @return the selector representing the packet infos
+     * @return the traceable packet representing the packet infos
      */
-    public TrafficSelector getEgressPacket() {
+    public PipelineTraceablePacket egressPacket() {
         return egressPacket;
     }
 
@@ -124,7 +123,7 @@
      *
      * @param egressPacket the egress packet
      */
-    public void setEgressPacket(TrafficSelector egressPacket) {
+    public void setEgressPacket(PipelineTraceablePacket egressPacket) {
         this.egressPacket = egressPacket;
     }
 
@@ -164,7 +163,7 @@
         if (obj instanceof PipelineTraceableHitChain) {
             PipelineTraceableHitChain that = (PipelineTraceableHitChain) obj;
             return Objects.equals(this.outputPort, that.outputPort) &&
-                    Objects.equals(this.hitChain, that.getHitChain()) &&
+                    Objects.equals(this.hitChain, that.hitChain) &&
                     Objects.equals(this.egressPacket, that.egressPacket) &&
                     Objects.equals(this.dropped, that.dropped);
         }
diff --git a/core/api/src/main/java/org/onosproject/net/PipelineTraceableInput.java b/core/api/src/main/java/org/onosproject/net/PipelineTraceableInput.java
index 2403c5e..8df5d95 100644
--- a/core/api/src/main/java/org/onosproject/net/PipelineTraceableInput.java
+++ b/core/api/src/main/java/org/onosproject/net/PipelineTraceableInput.java
@@ -20,7 +20,6 @@
 import com.google.common.collect.Maps;
 import org.onosproject.core.GroupId;
 import org.onosproject.net.flow.FlowEntry;
-import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.group.Group;
 
 import java.util.List;
@@ -29,20 +28,29 @@
 /**
  * Represents the input of the pipeline traceable processing.
  */
-public class PipelineTraceableInput {
+public final class PipelineTraceableInput {
 
     // Input state for the traceable behavior
-    TrafficSelector ingressPacket;
-    ConnectPoint ingressPort;
+    private PipelineTraceablePacket ingressPacket;
+    private ConnectPoint ingressPort;
     // List here all possible device state using
     // possibly an optimized reference
-    List<FlowEntry> flows = Lists.newArrayList();
-    Map<GroupId, Group> groups = Maps.newHashMap();
+    private List<FlowEntry> flows = Lists.newArrayList();
+    private Map<GroupId, Group> groups = Maps.newHashMap();
+    private List<DataPlaneEntity> deviceState;
 
-    public PipelineTraceableInput(TrafficSelector ingressPacket, ConnectPoint ingressPort,
+    /**
+     * Builds a pipeline traceable input.
+     *
+     * @param ingressPacket the input packet
+     * @param ingressPort the input port
+     * @param deviceState the device state
+     */
+    public PipelineTraceableInput(PipelineTraceablePacket ingressPacket, ConnectPoint ingressPort,
                                   List<DataPlaneEntity> deviceState) {
         this.ingressPacket = ingressPacket;
         this.ingressPort = ingressPort;
+        this.deviceState = deviceState;
         processDeviceState(deviceState);
     }
 
@@ -62,7 +70,7 @@
      *
      * @return the ingress packet
      */
-    public TrafficSelector ingressPacket() {
+    public PipelineTraceablePacket ingressPacket() {
         return ingressPacket;
     }
 
@@ -76,6 +84,15 @@
     }
 
     /**
+     * Getter for the device state.
+     *
+     * @return the device state
+     */
+    public List<DataPlaneEntity> deviceState() {
+        return deviceState;
+    }
+
+    /**
      * Getter for the flows.
      *
      * @return the flows
@@ -99,7 +116,7 @@
      * @param groupId the group id
      * @return the group, otherwise null.
      */
-    public Group getGroup(GroupId groupId) {
+    public Group groupById(GroupId groupId) {
         return groups.get(groupId);
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/PipelineTraceableMetadata.java b/core/api/src/main/java/org/onosproject/net/PipelineTraceableMetadata.java
new file mode 100644
index 0000000..2c92afc
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/PipelineTraceableMetadata.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020-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;
+
+/**
+ * Stores traceable processing metadata.
+ */
+public interface PipelineTraceableMetadata {
+
+}
diff --git a/core/api/src/main/java/org/onosproject/net/PipelineTraceableOutput.java b/core/api/src/main/java/org/onosproject/net/PipelineTraceableOutput.java
index 21a9819..66a18c0 100644
--- a/core/api/src/main/java/org/onosproject/net/PipelineTraceableOutput.java
+++ b/core/api/src/main/java/org/onosproject/net/PipelineTraceableOutput.java
@@ -74,7 +74,7 @@
      *
      * @return the log message
      */
-    public String getLog() {
+    public String log() {
         return log;
     }
 
@@ -83,7 +83,7 @@
      *
      * @return the pipeline hit chains
      */
-    public List<PipelineTraceableHitChain> getHitChains() {
+    public List<PipelineTraceableHitChain> hitChains() {
         return hitChains;
     }
 
@@ -92,7 +92,7 @@
      *
      * @return the pipeline traceable result
      */
-    public PipelineTraceableResult getResult() {
+    public PipelineTraceableResult result() {
         return result;
     }
 
@@ -106,6 +106,16 @@
     }
 
     /**
+     * Returns a new builder initialized with the traceable output.
+     *
+     * @param pipelineTraceableOutput the output used for the initialization
+     * @return an initialized builder
+     */
+    public static PipelineTraceableOutput.Builder builder(PipelineTraceableOutput pipelineTraceableOutput) {
+        return new PipelineTraceableOutput.Builder(pipelineTraceableOutput);
+    }
+
+    /**
      * Builder of pipeline traceable entities.
      */
     public static final class Builder {
@@ -114,6 +124,15 @@
         private List<PipelineTraceableHitChain> hitChains = Lists.newArrayList();
         private PipelineTraceableResult result = PipelineTraceableResult.SUCCESS;
 
+        private Builder() {
+        }
+
+        private Builder(PipelineTraceableOutput traceableOutput) {
+            appendToLog("\n" + traceableOutput.log());
+            setResult(traceableOutput.result());
+            traceableOutput.hitChains().forEach(this::addHitChain);
+        }
+
         /**
          * Appends a message to the log.
          *
@@ -128,7 +147,7 @@
             return this;
         }
 
-        private Builder setResult(PipelineTraceableResult result) {
+        public Builder setResult(PipelineTraceableResult result) {
             // Do not override original failure
             if (this.result == PipelineTraceableResult.SUCCESS) {
                 this.result = result;
diff --git a/core/api/src/main/java/org/onosproject/net/PipelineTraceablePacket.java b/core/api/src/main/java/org/onosproject/net/PipelineTraceablePacket.java
new file mode 100644
index 0000000..ffa2063
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/PipelineTraceablePacket.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020-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;
+
+import org.onosproject.net.flow.TrafficSelector;
+
+import java.util.Objects;
+
+/**
+ * Represents a traceable packet composed by a traffic selector and metadata.
+ */
+public final class PipelineTraceablePacket {
+
+    // stores metadata associated with the packet
+    private PipelineTraceableMetadata metadata;
+    // representation of the packet
+    private TrafficSelector packet;
+
+    /**
+     * Builds a traceable packet without metadata.
+     * Note this can be used for legacy device like ofdpa.
+     *
+     * @param packet the packet selector
+     */
+    public PipelineTraceablePacket(TrafficSelector packet) {
+        this.packet = packet;
+    }
+
+    /**
+     * Builds a traceable packet with metadata.
+     * @param packet the packet selector
+     * @param metadata the packet metadata
+     */
+    public PipelineTraceablePacket(TrafficSelector packet, PipelineTraceableMetadata metadata) {
+        this.packet = packet;
+        this.metadata = metadata;
+    }
+
+    /**
+     * Getter for the metadata.
+     *
+     * @return the packet metadata
+     */
+    public PipelineTraceableMetadata metadata() {
+        return metadata;
+    }
+
+    /**
+     * Getter for the packet selector.
+     *
+     * @return the packet selector
+     */
+    public TrafficSelector packet() {
+        return packet;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(metadata, packet);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof PipelineTraceablePacket) {
+            PipelineTraceablePacket that = (PipelineTraceablePacket) obj;
+            return Objects.equals(this.metadata, that.metadata) &&
+                    Objects.equals(this.packet, that.packet);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return "PipelineTraceablePacket{" +
+                "metadata=" + metadata +
+                ", packet=" + packet +
+                '}';
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/flow/criteria/PiCriterion.java b/core/api/src/main/java/org/onosproject/net/flow/criteria/PiCriterion.java
index a346927..17d1270 100644
--- a/core/api/src/main/java/org/onosproject/net/flow/criteria/PiCriterion.java
+++ b/core/api/src/main/java/org/onosproject/net/flow/criteria/PiCriterion.java
@@ -108,6 +108,16 @@
     }
 
     /**
+     * Returns the PiCriterion builder initialized by the given PiCriterion.
+     *
+     * @param piCriterion the input PiCriterion
+     * @return PiCriterion builder
+     */
+    public static Builder builder(PiCriterion piCriterion) {
+        return new Builder(piCriterion);
+    }
+
+    /**
      * PiCriterion Builder.
      */
     @Beta
@@ -120,6 +130,21 @@
             // ban constructor.
         }
 
+        private Builder(PiCriterion piCriterion) {
+            piCriterion.fieldMatchMap.forEach(((piMatchFieldId, piFieldMatch) -> add(piFieldMatch)));
+        }
+
+        /**
+         * Adds a match field to the builder.
+         *
+         * @param field the field value
+         * @return this
+         */
+        public Builder add(PiFieldMatch field) {
+            fieldMatchMapBuilder.put(field.fieldId(), field);
+            return this;
+        }
+
         /**
          * Adds an exact field match for the given fieldId and value.
          *