[AETHER-38] Extract pipeline-dependent code from current T3 implementation

- Executes also a refactoring of several components of the app;
- Uses the new abstractions introduced in the ONOS core and
  deprecates the old ones
- Deprecates unused APIs of the TroubleshootService
- Generator and other utils has been moved to the ONOS core
- Updates and fixes unit tests to make the build happy

Change-Id: Iac9ba644e1e199a58e8da53d0db78e36db56647c
diff --git a/app/pom.xml b/app/pom.xml
index 6c355b4..932d834 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -84,6 +84,14 @@
             <scope>provided</scope>
         </dependency>
 
+        <!-- ONOS drivers -->
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-drivers-default</artifactId>
+            <version>${onos.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
         <!-- Other Trellis apps -->
         <dependency>
             <groupId>org.onosproject</groupId>
diff --git a/app/src/main/java/org/onosproject/t3/api/FlowNib.java b/app/src/main/java/org/onosproject/t3/api/FlowNib.java
index b9f2226..1b70551 100644
--- a/app/src/main/java/org/onosproject/t3/api/FlowNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/FlowNib.java
@@ -67,7 +67,7 @@
                 .filter(flow -> flow.state() == flowState
                         && flow.deviceId().equals(deviceId))
                 .collect(Collectors.toSet());
-        return flowsFiltered != null ? ImmutableSet.copyOf(flowsFiltered) : ImmutableSet.of();
+        return ImmutableSet.copyOf(flowsFiltered);
     }
 
     /**
diff --git a/app/src/main/java/org/onosproject/t3/api/GroupNib.java b/app/src/main/java/org/onosproject/t3/api/GroupNib.java
index b1fb563..14f55a7 100644
--- a/app/src/main/java/org/onosproject/t3/api/GroupNib.java
+++ b/app/src/main/java/org/onosproject/t3/api/GroupNib.java
@@ -56,6 +56,21 @@
     }
 
     /**
+     * Returns all groups associated with the given device and filtered by the group state.
+     *
+     * @param deviceId device ID to get groups for
+     * @param groupState the group state
+     * @return iterable of device's groups
+     */
+    public Iterable<Group> getGroupsByState(DeviceId deviceId, Group.GroupState groupState) {
+        Set<Group> groupsFiltered = groups.stream()
+                .filter(group -> group.state() == groupState
+                        && group.deviceId().equals(deviceId))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(groupsFiltered);
+    }
+
+    /**
      * Returns all groups associated with the given device.
      *
      * @param deviceId device ID to get groups for
@@ -63,9 +78,9 @@
      */
     public Iterable<Group> getGroups(DeviceId deviceId) {
         Set<Group> groupsFiltered = groups.stream()
-                .filter(g -> g.deviceId().equals(deviceId))
+                .filter(group -> group.deviceId().equals(deviceId))
                 .collect(Collectors.toSet());
-        return groupsFiltered != null ? ImmutableSet.copyOf(groupsFiltered) : ImmutableSet.of();
+        return ImmutableSet.copyOf(groupsFiltered);
     }
 
     /**
diff --git a/app/src/main/java/org/onosproject/t3/api/GroupsInDevice.java b/app/src/main/java/org/onosproject/t3/api/GroupsInDevice.java
index 74203af..b17cfbb 100644
--- a/app/src/main/java/org/onosproject/t3/api/GroupsInDevice.java
+++ b/app/src/main/java/org/onosproject/t3/api/GroupsInDevice.java
@@ -25,8 +25,9 @@
 
 /**
  * Class to represent the groups in a device for a given output and packet.
+ * @deprecated in t3-4.0
  */
-//FIXME consider name change.
+@Deprecated
 public class GroupsInDevice {
 
     private ConnectPoint output;
diff --git a/app/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java b/app/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
index 22db007..57e1dce 100644
--- a/app/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
+++ b/app/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
@@ -21,6 +21,7 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
+import org.onosproject.net.PipelineTraceableHitChain;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.TrafficSelector;
 
@@ -36,10 +37,11 @@
  */
 public class StaticPacketTrace {
 
-    private final TrafficSelector inPacket;
-    private final ConnectPoint in;
+    private final TrafficSelector ingressPacket;
+    private final ConnectPoint ingressPoint;
     List<List<ConnectPoint>> completePaths;
     private Map<DeviceId, List<GroupsInDevice>> outputsForDevice;
+    private Map<DeviceId, List<PipelineTraceableHitChain>> hitChainsForDevice;
     private Map<DeviceId, List<FlowEntry>> flowsForDevice;
     private StringBuilder resultMessage;
     private Pair<Host, Host> hosts;
@@ -48,15 +50,16 @@
     /**
      * Builds the trace with a given packet and a connect point.
      *
-     * @param packet the packet to trace
-     * @param in     the initial connect point
+     * @param inPacket the packet to trace
+     * @param inPoint  the initial connect point
      */
-    public StaticPacketTrace(TrafficSelector packet, ConnectPoint in) {
-        this.inPacket = packet;
-        this.in = in;
+    public StaticPacketTrace(TrafficSelector inPacket, ConnectPoint inPoint) {
+        this.ingressPacket = inPacket;
+        this.ingressPoint = inPoint;
         completePaths = new ArrayList<>();
         outputsForDevice = new HashMap<>();
         flowsForDevice = new HashMap<>();
+        hitChainsForDevice = new HashMap<>();
         resultMessage = new StringBuilder();
         hosts = null;
     }
@@ -64,16 +67,17 @@
     /**
      * Builds the trace with a given packet and a connect point.
      *
-     * @param packet the packet to trace
-     * @param in     the initial connect point
-     * @param hosts  pair of source and destination hosts
+     * @param inPacket the packet to trace
+     * @param inPoint  the initial connect point
+     * @param hosts    pair of source and destination hosts
      */
-    public StaticPacketTrace(TrafficSelector packet, ConnectPoint in, Pair<Host, Host> hosts) {
-        this.inPacket = packet;
-        this.in = in;
+    public StaticPacketTrace(TrafficSelector inPacket, ConnectPoint inPoint, Pair<Host, Host> hosts) {
+        this.ingressPacket = inPacket;
+        this.ingressPoint = inPoint;
         completePaths = new ArrayList<>();
         outputsForDevice = new HashMap<>();
         flowsForDevice = new HashMap<>();
+        hitChainsForDevice = new HashMap<>();
         resultMessage = new StringBuilder();
         this.hosts = hosts;
     }
@@ -84,7 +88,7 @@
      * @return the initial packet in the form of a selector.
      */
     public TrafficSelector getInitialPacket() {
-        return inPacket;
+        return ingressPacket;
     }
 
     /**
@@ -93,7 +97,7 @@
      * @return the connect point
      */
     public ConnectPoint getInitialConnectPoint() {
-        return in;
+        return ingressPoint;
     }
 
     /**
@@ -122,7 +126,9 @@
      *
      * @param deviceId   the device
      * @param outputPath the groups in device objects
+     * @deprecated in t3-4.0
      */
+    @Deprecated
     public void addGroupOutputPath(DeviceId deviceId, GroupsInDevice outputPath) {
         if (!outputsForDevice.containsKey(deviceId)) {
             outputsForDevice.put(deviceId, new ArrayList<>());
@@ -131,16 +137,48 @@
     }
 
     /**
+     * Adds the pipeline hit chain for a given device.
+     *
+     * @param deviceId the device
+     * @param hitChain the hit chain
+     */
+    public void addHitChain(DeviceId deviceId, PipelineTraceableHitChain hitChain) {
+        hitChainsForDevice.compute(deviceId, (k, v) -> {
+            if (v == null) {
+                v = new ArrayList<>();
+            }
+            if (!v.contains(hitChain)) {
+                v.add(hitChain);
+            }
+            return v;
+        });
+    }
+
+    /**
      * Returns all the possible group-based outputs for a given device.
      *
      * @param deviceId the device
      * @return the list of Groups for this device.
+     * @deprecated in t3-4.0
      */
+    @Deprecated
     public List<GroupsInDevice> getGroupOuputs(DeviceId deviceId) {
         return outputsForDevice.get(deviceId) == null ? null : ImmutableList.copyOf(outputsForDevice.get(deviceId));
     }
 
     /**
+     * Returns all the possible pipeline hit chains for a given device.
+     *
+     * @param deviceId the device
+     * @return the list of hit chains
+     */
+    public List<PipelineTraceableHitChain> getHitChains(DeviceId deviceId) {
+        List<PipelineTraceableHitChain> hitChains = hitChainsForDevice.get(deviceId);
+        return hitChains == null ? null : ImmutableList.copyOf(hitChains);
+
+    }
+
+    /**
      * Adds a complete possible path.
      *
      * @param completePath the path
@@ -163,7 +201,9 @@
      *
      * @param deviceId the device considered
      * @param flows    the flows
+     * @deprecated in t3-4.0
      */
+    @Deprecated
     public void addFlowsForDevice(DeviceId deviceId, List<FlowEntry> flows) {
         flowsForDevice.put(deviceId, flows);
     }
@@ -173,7 +213,9 @@
      *
      * @param deviceId the device
      * @return the flows matched
+     * @deprecated in t3-4.0
      */
+    @Deprecated
     public List<FlowEntry> getFlowsForDevice(DeviceId deviceId) {
         return flowsForDevice.getOrDefault(deviceId, ImmutableList.of());
     }
@@ -214,15 +256,15 @@
         this.success.add(success);
     }
 
-
     @Override
     public String toString() {
         return "StaticPacketTrace{" +
-                "inPacket=" + inPacket +
-                ", in=" + in +
+                "ingressPacket=" + ingressPacket +
+                ", ingressPoint=" + ingressPoint +
                 ", completePaths=" + completePaths +
                 ", outputsForDevice=" + outputsForDevice +
                 ", flowsForDevice=" + flowsForDevice +
+                ", hitChains=" + hitChainsForDevice +
                 ", resultMessage=" + resultMessage +
                 '}';
     }
diff --git a/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java b/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
index 3ab6052..d9c2f59 100644
--- a/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
+++ b/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
@@ -18,10 +18,10 @@
 
 import org.onlab.packet.EthType;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Generator;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
 import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.t3.impl.Generator;
 
 import java.util.List;
 import java.util.Set;
@@ -37,7 +37,9 @@
      *
      * @param type the etherType of the traffic we want to trace.
      * @return trace result
+     * @deprecated in t3-4.0
      */
+    @Deprecated
     List<StaticPacketTrace> pingAll(EthType.EtherType type);
 
     /**
@@ -81,7 +83,9 @@
      *
      * @param vlanId the vlan id configured for multicast
      * @return list of trace result
+     * @deprecated in t3-4.0
      */
+    @Deprecated
     List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId);
 
     /**
diff --git a/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java b/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
index ddbdb38..8568775 100644
--- a/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
+++ b/app/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
@@ -18,9 +18,12 @@
 
 import org.apache.commons.lang.StringUtils;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DataPlaneEntity;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.Group;
 import org.onosproject.net.group.GroupBucket;
-import org.onosproject.t3.api.GroupsInDevice;
 import org.onosproject.t3.api.StaticPacketTrace;
 
 import java.util.List;
@@ -88,8 +91,7 @@
                 tracePrint.append("\n");
                 tracePrint.append("Input from " + connectPoint);
                 tracePrint.append("\n");
-                tracePrint = printFlows(trace, verbose, connectPoint, tracePrint);
-                tracePrint = printGroups(trace, verbose, connectPoint, tracePrint);
+                tracePrint = printHitChains(trace, verbose, connectPoint.deviceId(), tracePrint);
                 tracePrint.append("\n");
             } else {
                 for (ConnectPoint connectPoint : path) {
@@ -98,11 +100,7 @@
                         tracePrint.append("\n");
                         tracePrint.append("    Input from " + connectPoint);
                         tracePrint.append("\n");
-                        tracePrint = printFlows(trace, verbose, connectPoint, tracePrint);
-                    } else {
-                        tracePrint = printGroups(trace, verbose, connectPoint, tracePrint);
-                        tracePrint.append("    Output through " + connectPoint);
-                        tracePrint.append("\n");
+                        tracePrint = printHitChains(trace, verbose, connectPoint.deviceId(), tracePrint);
                     }
                     previous = connectPoint;
                 }
@@ -112,62 +110,71 @@
         return tracePrint;
     }
 
-
-    //Prints the flows for a given trace and a specified level of verbosity
-    private static StringBuilder printFlows(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint,
-                                            StringBuilder tracePrint) {
-        tracePrint.append("    Flows ");
-        tracePrint.append(trace.getFlowsForDevice(connectPoint.deviceId()).size());
+    private static StringBuilder printHitChains(StaticPacketTrace trace, boolean verbose, DeviceId deviceId,
+                                                        StringBuilder tracePrint) {
+        tracePrint.append("    Hit chains ");
+        tracePrint.append(trace.getHitChains(deviceId).size());
         tracePrint.append("    \n");
-        trace.getFlowsForDevice(connectPoint.deviceId()).forEach(f -> {
-            if (verbose) {
-                tracePrint.append("    " + String.format(FLOW_SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
-                        f.table(), f.priority(), f.selector().criteria(),
-                        printTreatment(f.treatment())));
-                tracePrint.append("\n");
-            } else {
-                tracePrint.append(String.format("       flowId=%s, table=%s, selector=%s", f.id(), f.table(),
-                        f.selector().criteria()));
-                tracePrint.append("\n");
-            }
+        tracePrint.append("    \n");
+        int[] index = {1};
+        trace.getHitChains(deviceId).forEach(hitChain -> {
+            tracePrint.append("    Hit chain " + index[0]++);
+            tracePrint.append("    \n");
+            // Print for each chain the matchable entities first
+            hitChain.getHitChain().forEach(dataPlaneEntity -> {
+                if (dataPlaneEntity.getType() == DataPlaneEntity.Type.FLOWRULE) {
+                    printFlow(dataPlaneEntity.getFlowEntry(), verbose, tracePrint);
+                } else if (dataPlaneEntity.getType() == DataPlaneEntity.Type.GROUP) {
+                    printGroup(dataPlaneEntity.getGroupEntry(), verbose, tracePrint);
+                }
+            });
+            // Then the output packet of the current chain
+            tracePrint.append("    Outgoing Packet " + hitChain.getEgressPacket());
+            tracePrint.append("\n");
+            // The output port of the current chain
+            tracePrint.append("    Output through " + hitChain.getOutputPort());
+            tracePrint.append("\n");
+            // Dropped during the processing ?
+            tracePrint.append("    Dropped " + hitChain.isDropped());
+            tracePrint.append("\n");
+            tracePrint.append("\n");
         });
+
         return tracePrint;
     }
 
-    //Prints the groups for a given trace and a specified level of verbosity
-    private static StringBuilder printGroups(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint,
-                                             StringBuilder tracePrint) {
-        List<GroupsInDevice> groupsInDevice = trace.getGroupOuputs(connectPoint.deviceId());
-        if (groupsInDevice != null) {
-            tracePrint.append("    Groups");
-            tracePrint.append("\n");
-            groupsInDevice.forEach(output -> {
-                if (output.getOutput().equals(connectPoint)) {
-                    output.getGroups().forEach(group -> {
-                        if (verbose) {
-                            tracePrint.append("    " + String.format(GROUP_FORMAT, Integer.toHexString(group.id().id()),
-                                    group.state(), group.type(), group.bytes(), group.packets(),
-                                    group.appId().name(), group.referenceCount()));
-                            tracePrint.append("\n");
-                            int i = 0;
-                            for (GroupBucket bucket : group.buckets().buckets()) {
-                                tracePrint.append("    " + String.format(GROUP_BUCKET_FORMAT,
-                                        Integer.toHexString(group.id().id()),
-                                        ++i, bucket.bytes(), bucket.packets(),
-                                        bucket.treatment().allInstructions()));
-                                tracePrint.append("\n");
-                            }
-                        } else {
-                            tracePrint.append("       groupId=" + group.id());
-                            tracePrint.append("\n");
-                        }
-                    });
-                    tracePrint.append("    Outgoing Packet " + output.getFinalPacket());
-                    tracePrint.append("\n");
-                }
-            });
+    // Prints the flows for a given trace and a specified level of verbosity
+    private static void printFlow(FlowEntry f, boolean verbose, StringBuilder tracePrint) {
+        if (verbose) {
+            tracePrint.append("    " + String.format(FLOW_SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
+                    f.table(), f.priority(), f.selector().criteria(),
+                    printTreatment(f.treatment())));
+        } else {
+            tracePrint.append(String.format("       flowId=%s, table=%s, selector=%s", f.id(), f.table(),
+                    f.selector().criteria()));
         }
-        return tracePrint;
+        tracePrint.append("\n");
+    }
+
+    // Prints the groups for a given trace and a specified level of verbosity
+    private static void printGroup(Group group, boolean verbose, StringBuilder tracePrint) {
+        if (verbose) {
+            tracePrint.append("    " + String.format(GROUP_FORMAT, Integer.toHexString(group.id().id()),
+                    group.state(), group.type(), group.bytes(), group.packets(),
+                    group.appId().name(), group.referenceCount()));
+            tracePrint.append("\n");
+            int i = 0;
+            for (GroupBucket bucket : group.buckets().buckets()) {
+                tracePrint.append("    " + String.format(GROUP_BUCKET_FORMAT,
+                        Integer.toHexString(group.id().id()),
+                        ++i, bucket.bytes(), bucket.packets(),
+                        bucket.treatment().allInstructions()));
+                tracePrint.append("\n");
+            }
+        } else {
+            tracePrint.append("       groupId=" + group.id());
+            tracePrint.append("\n");
+        }
     }
 
     private static String printTreatment(TrafficTreatment treatment) {
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
index d635c23..0e51066 100644
--- a/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootMcastCommand.java
@@ -24,6 +24,7 @@
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Generator;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.cli.PlaceholderCompleter;
 import org.onosproject.net.ConnectPoint;
@@ -33,7 +34,6 @@
 import org.onosproject.net.flow.criteria.IPCriterion;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.onosproject.t3.api.TroubleshootService;
-import org.onosproject.t3.impl.Generator;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java b/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
index e61a533..40f747f 100644
--- a/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
+++ b/app/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllCommand.java
@@ -22,6 +22,7 @@
 import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.onlab.packet.IpAddress;
+import org.onlab.util.Generator;
 import org.onosproject.cli.AbstractShellCommand;
 import org.onosproject.cli.PlaceholderCompleter;
 import org.onosproject.cli.net.EthTypeCompleter;
@@ -30,7 +31,6 @@
 import org.onosproject.net.flow.criteria.IPCriterion;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.onosproject.t3.api.TroubleshootService;
-import org.onosproject.t3.impl.Generator;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/app/src/main/java/org/onosproject/t3/impl/Generator.java b/app/src/main/java/org/onosproject/t3/impl/Generator.java
deleted file mode 100644
index 82ad628..0000000
--- a/app/src/main/java/org/onosproject/t3/impl/Generator.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2018-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.t3.impl;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-/**
- * Generator class that yields instances of T type objects as soon as they are ready.
- *
- * @param <T> type of the object.
- */
-public abstract class Generator<T> implements Iterable<T> {
-
-    private class Condition {
-        private boolean isSet;
-
-        synchronized void set() {
-            isSet = true;
-            notifyAll();
-        }
-
-        synchronized void await() throws InterruptedException {
-            try {
-
-                if (isSet) {
-                    return;
-                }
-
-                while (!isSet) {
-                    wait();
-                }
-            } finally {
-                isSet = false;
-            }
-        }
-    }
-
-    private static ThreadGroup threadGroup;
-
-    private Thread producer;
-    private boolean hasFinished;
-    private final Condition itemAvailableOrHasFinished = new Condition();
-    private final Condition itemRequested = new Condition();
-    private T nextItem;
-    private boolean nextItemAvailable;
-    private RuntimeException exceptionRaisedByProducer;
-
-    @Override
-    public Iterator<T> iterator() {
-        return new Iterator<T>() {
-            @Override
-            public boolean hasNext() {
-                return waitForNext();
-            }
-
-            @Override
-            public T next() {
-                if (!waitForNext()) {
-                    throw new NoSuchElementException();
-                }
-                nextItemAvailable = false;
-                return nextItem;
-            }
-
-            @Override
-            public void remove() {
-                throw new UnsupportedOperationException();
-            }
-
-            private boolean waitForNext() {
-                if (nextItemAvailable) {
-                    return true;
-                }
-                if (hasFinished) {
-                    return false;
-                }
-                if (producer == null) {
-                    startProducer();
-                }
-                itemRequested.set();
-                try {
-                    itemAvailableOrHasFinished.await();
-                } catch (InterruptedException e) {
-                    hasFinished = true;
-                    producer.interrupt();
-                    try {
-                        producer.join();
-                    } catch (InterruptedException e1) {
-                        // Interrupting the broken thread
-                        Thread.currentThread().interrupt();
-                        throw new IllegalStateException(e1);
-                    }
-                }
-                if (exceptionRaisedByProducer != null) {
-                    throw exceptionRaisedByProducer;
-                }
-                return !hasFinished;
-            }
-        };
-    }
-
-    protected abstract void run() throws InterruptedException;
-
-    void yield(T element) throws InterruptedException {
-        nextItem = element;
-        nextItemAvailable = true;
-        itemAvailableOrHasFinished.set();
-        itemRequested.await();
-    }
-
-    private void startProducer() {
-        assert producer == null;
-        synchronized (this) {
-            if (threadGroup == null) {
-                threadGroup = new ThreadGroup("onos-t3-generator");
-            }
-        }
-        producer = new Thread(threadGroup, () -> {
-            try {
-                itemRequested.await();
-                Generator.this.run();
-            } catch (InterruptedException e) {
-                // Remaining steps in run() will shut down thread.
-            } catch (RuntimeException e) {
-                exceptionRaisedByProducer = e;
-            }
-            hasFinished = true;
-            itemAvailableOrHasFinished.set();
-        });
-        producer.setDaemon(true);
-        producer.start();
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java b/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java
index 7520843..780de09 100644
--- a/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java
+++ b/app/src/main/java/org/onosproject/t3/impl/McastGenerator.java
@@ -21,6 +21,7 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Generator;
 import org.onosproject.mcast.api.McastRouteData;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
diff --git a/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java b/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java
index e935fad..7d656c5 100644
--- a/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java
+++ b/app/src/main/java/org/onosproject/t3/impl/PingAllGenerator.java
@@ -19,6 +19,7 @@
 import com.google.common.collect.Sets;
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpAddress;
+import org.onlab.util.Generator;
 import org.onosproject.t3.api.HostNib;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.slf4j.Logger;
diff --git a/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index bc04136..b683351 100644
--- a/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -21,25 +21,32 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.google.common.collect.Maps;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Generator;
 import org.onosproject.cluster.NodeId;
+
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DataPlaneEntity;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.Link;
+import org.onosproject.net.PipelineTraceableHitChain;
+import org.onosproject.net.PipelineTraceableInput;
+import org.onosproject.net.PipelineTraceableOutput;
+import org.onosproject.net.PipelineTraceableOutput.PipelineTraceableResult;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PipelineTraceable;
 import org.onosproject.net.config.ConfigException;
 import org.onosproject.net.config.basics.InterfaceConfig;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.FlowEntry;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.IndexTableId;
-import org.onosproject.net.flow.TableId;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criteria;
 import org.onosproject.net.flow.criteria.Criterion;
@@ -47,12 +54,7 @@
 import org.onosproject.net.flow.criteria.EthTypeCriterion;
 import org.onosproject.net.flow.criteria.IPCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.intf.Interface;
 import org.onosproject.routeservice.ResolvedRoute;
@@ -62,7 +64,6 @@
 import org.onosproject.t3.api.EdgePortNib;
 import org.onosproject.t3.api.FlowNib;
 import org.onosproject.t3.api.GroupNib;
-import org.onosproject.t3.api.GroupsInDevice;
 import org.onosproject.t3.api.HostNib;
 import org.onosproject.t3.api.LinkNib;
 import org.onosproject.t3.api.MastershipNib;
@@ -75,27 +76,20 @@
 import org.osgi.service.component.annotations.Component;
 import org.slf4j.Logger;
 
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
+import java.util.Map;
 import java.util.List;
-import java.util.Optional;
 import java.util.Set;
+import java.util.HashSet;
+import java.util.Optional;
+import java.util.ArrayList;
+import java.util.Collections;
+
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
 import static org.onlab.packet.EthType.EtherType;
 import static org.onosproject.net.flow.TrafficSelector.Builder;
-import static org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsHeaderInstruction;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
-import static org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
-import static org.onosproject.t3.impl.TroubleshootUtils.compareMac;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -123,6 +117,9 @@
     protected NetworkConfigNib networkConfigNib = NetworkConfigNib.getInstance();
     protected MulticastRouteNib mcastRouteNib = MulticastRouteNib.getInstance();
 
+    // FIXME Revisit offline mode after a first implementation
+    private final Map<DeviceId, PipelineTraceable> pipelineTraceables = Maps.newConcurrentMap();
+
     @Override
     public boolean checkNibValidity() {
         return Stream.of(flowNib, groupNib, linkNib, hostNib, deviceNib, driverNib,
@@ -146,32 +143,6 @@
     }
 
     @Override
-    public List<StaticPacketTrace> pingAll(EtherType type) {
-        ImmutableList.Builder<StaticPacketTrace> tracesBuilder = ImmutableList.builder();
-        hostNib.getHosts().forEach(host -> {
-            List<IpAddress> ipAddresses = getIpAddresses(host, type, false);
-            if (ipAddresses.size() > 0) {
-                //check if the host has only local IPs of that ETH type
-                boolean onlyLocalSrc = ipAddresses.size() == 1 && ipAddresses.get(0).isLinkLocal();
-                hostNib.getHosts().forEach(hostToPing -> {
-                    List<IpAddress> ipAddressesToPing = getIpAddresses(hostToPing, type, false);
-                    //check if the other host has only local IPs of that ETH type
-                    boolean onlyLocalDst = ipAddressesToPing.size() == 1 && ipAddressesToPing.get(0).isLinkLocal();
-                    boolean sameLocation = Sets.intersection(host.locations(), hostToPing.locations()).size() > 0;
-                    //Trace is done only if they are both local and under the same location
-                    // or not local and if they are not the same host.
-                    if (((sameLocation && onlyLocalDst && onlyLocalSrc) ||
-                            (!onlyLocalSrc && !onlyLocalDst && ipAddressesToPing.size() > 0))
-                            && !host.equals(hostToPing)) {
-                        tracesBuilder.addAll(trace(host.id(), hostToPing.id(), type));
-                    }
-                });
-            }
-        });
-        return tracesBuilder.build();
-    }
-
-    @Override
     public Generator<Set<StaticPacketTrace>> pingAllGenerator(EtherType type) {
         return new PingAllGenerator(type, hostNib, this);
     }
@@ -269,6 +240,32 @@
         }
     }
 
+    private PipelineTraceable getPipelineMatchable(DeviceId deviceId) {
+        return pipelineTraceables.compute(deviceId, (k, v) -> {
+            if (v == null) {
+                log.info("PipelineMatchable not found for {}", deviceId);
+                Device d = deviceNib.getDevice(deviceId);
+                if (d.is(PipelineTraceable.class)) {
+                    v = d.as(PipelineTraceable.class);
+                    v.init();
+                } else {
+                    log.warn("PipelineMatchable behaviour not supported for device {}",
+                            deviceId);
+                }
+            }
+            return v;
+        });
+    }
+
+    private List<DataPlaneEntity> getDataPlaneEntities(DeviceId deviceId) {
+        List<DataPlaneEntity> dataPlaneEntities = Lists.newArrayList();
+        flowNib.getFlowEntriesByState(deviceId, FlowEntry.FlowEntryState.ADDED).forEach(entity ->
+                dataPlaneEntities.add(new DataPlaneEntity(entity)));
+        groupNib.getGroupsByState(deviceId, Group.GroupState.ADDED).forEach(entity ->
+                dataPlaneEntities.add(new DataPlaneEntity(entity)));
+        return dataPlaneEntities;
+    }
+
     /**
      * Matches src and dst IPs based on host information.
      *
@@ -392,14 +389,6 @@
         return trace;
     }
 
-    @Override
-    public List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId) {
-        Generator<Set<StaticPacketTrace>> gen = new McastGenerator(mcastRouteNib, this, vlanId);
-        List<Set<StaticPacketTrace>> multicastTraceList =
-                StreamSupport.stream(gen.spliterator(), false).collect(Collectors.toList());
-        return multicastTraceList;
-    }
-
     /**
      * Computes a trace for a give packet that start in the network at the given connect point.
      *
@@ -427,20 +416,24 @@
         completePath.add(in);
 
         //If the trace has no outputs for the given input we stop here
-        if (trace.getGroupOuputs(in.deviceId()) == null) {
-            computePath(completePath, trace, null);
+        if (trace.getHitChains(in.deviceId()) == null) {
+            TroubleshootUtils.computePath(completePath, trace, null);
             trace.addResultMessage("No output out of device " + in.deviceId() + ". Packet is dropped");
             trace.setSuccess(false);
             return trace;
         }
 
         //If the trace has outputs we analyze them all
-        for (GroupsInDevice outputPath : trace.getGroupOuputs(in.deviceId())) {
+        for (PipelineTraceableHitChain outputPath : trace.getHitChains(in.deviceId())) {
 
-            ConnectPoint cp = outputPath.getOutput();
+            ConnectPoint cp = outputPath.getOutputPort();
             log.debug("Connect point in {}", in);
             log.debug("Output path {}", cp);
-            log.debug("{}", outputPath.getFinalPacket());
+            log.debug("{}", outputPath.getEgressPacket());
+
+            if (outputPath.isDropped()) {
+                continue;
+            }
 
             //Hosts for the the given output
             Set<Host> hostsList = hostNib.getConnectedHosts(cp);
@@ -448,34 +441,31 @@
             Set<Host> hosts = getHosts(trace);
 
             if (in.equals(cp) && trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID) != null &&
-                    outputPath.getFinalPacket().getCriterion(Criterion.Type.VLAN_VID) != null
+                    outputPath.getEgressPacket().getCriterion(Criterion.Type.VLAN_VID) != null
                     && ((VlanIdCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID)).vlanId()
-                    .equals(((VlanIdCriterion) outputPath.getFinalPacket().getCriterion(Criterion.Type.VLAN_VID))
+                    .equals(((VlanIdCriterion) outputPath.getEgressPacket().getCriterion(Criterion.Type.VLAN_VID))
                             .vlanId())) {
-                if (trace.getGroupOuputs(in.deviceId()).size() == 1 &&
-                        computePath(completePath, trace, outputPath.getOutput())) {
+                if (trace.getHitChains(in.deviceId()).size() == 1 &&
+                        TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort())) {
                     trace.addResultMessage("Connect point out " + cp + " is same as initial input " + in);
                     trace.setSuccess(false);
                 }
             } else if (!Collections.disjoint(hostsList, hosts)) {
                 //If the two host collections contain the same item it means we reached the proper output
-                log.debug("Stopping here because host is expected destination {}, reached through", completePath);
-                if (computePath(completePath, trace, outputPath.getOutput())) {
+                log.debug("Stopping here because host is expected destination, reached through {}", completePath);
+                if (TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort())) {
                     trace.addResultMessage("Reached required destination Host " + cp);
                     trace.setSuccess(true);
                 }
                 break;
-            } else if (cp.port().equals(PortNumber.CONTROLLER)) {
 
+            } else if (cp.port().equals(PortNumber.CONTROLLER)) {
                 //Getting the master when the packet gets sent as packet in
                 NodeId master = mastershipNib.getMasterFor(cp.deviceId());
                 // TODO if we don't need to print master node id, exclude mastership NIB which is used only here
                 trace.addResultMessage(PACKET_TO_CONTROLLER + " " + master.id());
-                computePath(completePath, trace, outputPath.getOutput());
-                handleVlanToController(outputPath, trace);
-
+                TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort());
             } else if (linkNib.getEgressLinks(cp).size() > 0) {
-
                 //TODO this can be optimized if we use a Tree structure for paths.
                 //if we already have outputs let's check if the one we are considering starts from one of the devices
                 // in any of the ones we have.
@@ -513,7 +503,7 @@
                     ConnectPoint dst = link.dst();
                     //change in-port to the dst link in port
                     Builder updatedPacket = DefaultTrafficSelector.builder();
-                    outputPath.getFinalPacket().criteria().forEach(updatedPacket::add);
+                    outputPath.getEgressPacket().criteria().forEach(updatedPacket::add);
                     updatedPacket.add(Criteria.matchInPort(dst.port()));
                     log.debug("DST Connect Point {}", dst);
                     //build the elements for that device
@@ -521,15 +511,15 @@
                     //continue the trace along the path
                     getTrace(completePath, dst, trace, isDualHomed);
                 }
-            } else if (edgePortNib.isEdgePoint(outputPath.getOutput()) &&
+            } else if (edgePortNib.isEdgePoint(outputPath.getOutputPort()) &&
                     trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST) != null &&
                     ((EthCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.ETH_DST))
                             .mac().isMulticast()) {
-                trace.addResultMessage("Packet is multicast and reached output " + outputPath.getOutput() +
+                trace.addResultMessage("Packet is multicast and reached output " + outputPath.getOutputPort() +
                         " which is enabled and is edge port");
                 trace.setSuccess(true);
-                computePath(completePath, trace, outputPath.getOutput());
-                if (!hasOtherOutput(in.deviceId(), trace, outputPath.getOutput())) {
+                TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort());
+                if (!hasOtherOutput(in.deviceId(), trace, outputPath.getOutputPort())) {
                     return trace;
                 }
             } else if (deviceNib.getPort(cp) != null && deviceNib.getPort(cp).isEnabled()) {
@@ -538,9 +528,9 @@
                 //We treat as correct output only if it's not LLDP or BDDP
                 if (!(ethTypeCriterion.ethType().equals(EtherType.LLDP.ethType())
                         && !ethTypeCriterion.ethType().equals(EtherType.BDDP.ethType()))) {
-                    if (computePath(completePath, trace, outputPath.getOutput())) {
+                    if (TroubleshootUtils.computePath(completePath, trace, outputPath.getOutputPort())) {
                         if (hostsList.isEmpty()) {
-                            trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getFinalPacket()
+                            trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getEgressPacket()
                                     .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
                                     cp + " with no hosts connected ");
                         } else {
@@ -557,7 +547,7 @@
                                 if (hostsList.stream().anyMatch(host -> host.ipAddresses().contains(finalIpAddress)) ||
                                         hostNib.getHostsByIp(finalIpAddress).isEmpty()) {
                                     trace.addResultMessage("Packet is " +
-                                            ((EthTypeCriterion) outputPath.getFinalPacket()
+                                            ((EthTypeCriterion) outputPath.getEgressPacket()
                                                     .getCriterion(Criterion.Type.ETH_TYPE)).ethType() +
                                             " and reached " + cp + " with hosts " + hostsList);
                                 } else {
@@ -566,7 +556,7 @@
                                     trace.setSuccess(false);
                                 }
                             } else {
-                                trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getFinalPacket()
+                                trace.addResultMessage("Packet is " + ((EthTypeCriterion) outputPath.getEgressPacket()
                                         .getCriterion(Criterion.Type.ETH_TYPE)).ethType() + " and reached " +
                                         cp + " with hosts " + hostsList);
                             }
@@ -576,7 +566,7 @@
                 }
 
             } else {
-                computePath(completePath, trace, cp);
+                TroubleshootUtils.computePath(completePath, trace, cp);
                 trace.setSuccess(false);
                 if (deviceNib.getPort(cp) == null) {
                     //Port is not existent on device.
@@ -592,34 +582,6 @@
         return trace;
     }
 
-
-    /**
-     * If the initial packet comes tagged with a Vlan we output it with that to ONOS.
-     * If ONOS applied a vlan we remove it.
-     *
-     * @param outputPath the output
-     * @param trace      the trace we are building
-     */
-
-    private void handleVlanToController(GroupsInDevice outputPath, StaticPacketTrace trace) {
-
-        VlanIdCriterion initialVid = (VlanIdCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.VLAN_VID);
-        VlanIdCriterion finalVid = (VlanIdCriterion) outputPath.getFinalPacket().getCriterion(Criterion.Type.VLAN_VID);
-
-        if (initialVid != null && !initialVid.equals(finalVid) && initialVid.vlanId().equals(VlanId.NONE)) {
-
-            Set<Criterion> finalCriteria = new HashSet<>(outputPath.getFinalPacket().criteria());
-            //removing the final vlanId
-            finalCriteria.remove(finalVid);
-            Builder packetUpdated = DefaultTrafficSelector.builder();
-            finalCriteria.forEach(packetUpdated::add);
-            //Initial was none so we set it to that
-            packetUpdated.add(Criteria.matchVlanId(VlanId.NONE));
-            //Update final packet
-            outputPath.setFinalPacket(packetUpdated.build());
-        }
-    }
-
     /**
      * Checks if the device has other outputs than the given connect point.
      *
@@ -629,9 +591,8 @@
      * @return true if the device has other outputs.
      */
     private boolean hasOtherOutput(DeviceId inDeviceId, StaticPacketTrace trace, ConnectPoint cp) {
-        return trace.getGroupOuputs(inDeviceId).stream().filter(groupsInDevice -> {
-            return !groupsInDevice.getOutput().equals(cp);
-        }).count() > 0;
+        return trace.getHitChains(inDeviceId).stream().filter(groupsInDevice ->
+                !groupsInDevice.getOutputPort().equals(cp)).count() > 0;
     }
 
     /**
@@ -678,35 +639,6 @@
     }
 
     /**
-     * Computes the list of traversed connect points.
-     *
-     * @param completePath the list of devices
-     * @param trace        the trace we are building
-     * @param output       the final output connect point
-     */
-    private boolean computePath(List<ConnectPoint> completePath, StaticPacketTrace trace, ConnectPoint output) {
-        List<ConnectPoint> traverseList = new ArrayList<>();
-        if (!completePath.contains(trace.getInitialConnectPoint())) {
-            traverseList.add(trace.getInitialConnectPoint());
-        }
-
-        if (output != null && trace.getInitialConnectPoint().deviceId().equals(output.deviceId())) {
-            trace.addCompletePath(ImmutableList.of(trace.getInitialConnectPoint(), output));
-            return true;
-        }
-
-        traverseList.addAll(completePath);
-        if (output != null && !completePath.contains(output)) {
-            traverseList.add(output);
-        }
-        if (!trace.getCompletePaths().contains(traverseList)) {
-            trace.addCompletePath(ImmutableList.copyOf(traverseList));
-            return true;
-        }
-        return false;
-    }
-
-    /**
      * Traces the packet inside a device starting from an input connect point.
      *
      * @param trace        the trace we are building
@@ -718,12 +650,21 @@
      */
     private StaticPacketTrace traceInDevice(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in,
                                             boolean isDualHomed, List<ConnectPoint> completePath) {
+        // Get the behavior - do not proceed if there is no PipelineMatchable for the given device
+        PipelineTraceable pipelineMatchable = getPipelineMatchable(in.deviceId());
+        if (pipelineMatchable == null) {
+            trace.addResultMessage("No PipelineMatchable behavior for " + in.deviceId() + ". Aborting");
+            TroubleshootUtils.computePath(completePath, trace, null);
+            trace.setSuccess(false);
+            return trace;
+        }
 
+        // Verify the presence of multiple routes - if the device has been visited in the past
         boolean multipleRoutes = false;
-        if (trace.getGroupOuputs(in.deviceId()) != null) {
+        if (trace.getHitChains(in.deviceId()) != null) {
             multipleRoutes = multipleRoutes(trace);
         }
-        if (trace.getGroupOuputs(in.deviceId()) != null && !isDualHomed && !multipleRoutes) {
+        if (trace.getHitChains(in.deviceId()) != null && !isDualHomed && !multipleRoutes) {
             log.debug("Trace already contains device and given outputs");
             return trace;
         }
@@ -733,12 +674,12 @@
         //if device is not available exit here.
         if (!deviceNib.isAvailable(in.deviceId())) {
             trace.addResultMessage("Device is offline " + in.deviceId());
-            computePath(completePath, trace, null);
+            TroubleshootUtils.computePath(completePath, trace, null);
             return trace;
         }
 
-        //handle when the input is the controller
-        //NOTE, we are using the input port as a convenience to carry the CONTROLLER port number even if
+        // Handle when the input is the controller.
+        // NOTE, we are using the input port as a convenience to carry the CONTROLLER port number even if
         // a packet in from the controller will not actually traverse the pipeline and have no such notion
         // as the input port.
         if (in.port().equals(PortNumber.CONTROLLER)) {
@@ -748,197 +689,38 @@
             }
         }
 
-        List<FlowEntry> flows = new ArrayList<>();
-        List<FlowEntry> outputFlows = new ArrayList<>();
-        List<Instruction> deferredInstructions = new ArrayList<>();
-
-        FlowEntry nextTableIdEntry = findNextTableIdEntry(in.deviceId(), -1);
-        if (nextTableIdEntry == null) {
-            trace.addResultMessage("No flow rules for device " + in.deviceId() + ". Aborting");
-            computePath(completePath, trace, null);
+        // Get the device state in the form of DataPlaneEntity objects - do not proceed if there is no state
+        List<DataPlaneEntity> dataPlaneEntities = getDataPlaneEntities(in.deviceId());
+        if (dataPlaneEntities.isEmpty()) {
+            trace.addResultMessage("No device state for " + in.deviceId() + ". Aborting");
+            TroubleshootUtils.computePath(completePath, trace, null);
             trace.setSuccess(false);
             return trace;
         }
-        TableId tableId = nextTableIdEntry.table();
-        FlowEntry flowEntry;
-        boolean output = false;
-        while (!output) {
-            log.debug("Searching a Flow Entry on table {} for packet {}", tableId, packet);
-            //get the rule that matches the incoming packet
-            flowEntry = matchHighestPriority(packet, in, tableId);
-            log.debug("Found Flow Entry {}", flowEntry);
 
-            boolean isOfdpaHardware = TroubleshootUtils.hardwareOfdpaMap
-                    .getOrDefault(driverNib.getDriverName(in.deviceId()), false);
+        // Applies pipeline processing
+        PipelineTraceableInput input = new PipelineTraceableInput(packet, in, dataPlaneEntities);
+        PipelineTraceableOutput output = pipelineMatchable.apply(input);
 
-            //if the flow entry on a table is null and we are on hardware we treat as table miss, with few exceptions
-            if (flowEntry == null && isOfdpaHardware) {
-                log.debug("Ofdpa Hw setup, no flow rule means table miss");
+        // Update the trace
+        List<PipelineTraceableHitChain> hitChains = output.getHitChains();
+        hitChains.forEach(hitChain -> trace.addHitChain(in.deviceId(), hitChain));
+        trace.addResultMessage(output.getLog());
 
-                if (((IndexTableId) tableId).id() == 27) {
-                    //Apparently a miss but Table 27 on OFDPA is a fixed table
-                    packet = handleOfdpa27FixedTable(trace, packet);
-                }
-
-                //Finding next table to go In case of miss
-                nextTableIdEntry = findNextTableIdEntry(in.deviceId(), ((IndexTableId) tableId).id());
-                log.debug("Next table id entry {}", nextTableIdEntry);
-
-                //FIXME find better solution that enable granularity greater than 0 or all rules
-                //(another possibility is max tableId)
-                if (nextTableIdEntry == null && flows.size() == 0) {
-                    trace.addResultMessage("No matching flow rules for device " + in.deviceId() + ". Aborting");
-                    computePath(completePath, trace, null);
-                    trace.setSuccess(false);
-                    return trace;
-
-                } else if (nextTableIdEntry == null) {
-                    //Means that no more flow rules are present
-                    output = true;
-
-                } else if (((IndexTableId) tableId).id() == 20) {
-                    //if the table is 20 OFDPA skips to table 50
-                    log.debug("A miss on Table 20 on OFDPA means that we skip directly to table 50");
-                    tableId = IndexTableId.of(50);
-
-                } else if (((IndexTableId) tableId).id() == 40) {
-                    //if the table is 40 OFDPA skips to table 60
-                    log.debug("A miss on Table 40 on OFDPA means that we skip directly to table 60");
-                    tableId = IndexTableId.of(60);
-                } else {
-                    tableId = nextTableIdEntry.table();
-                }
-
-            } else if (flowEntry == null) {
-                trace.addResultMessage("Packet has no match on table " + tableId + " in device " +
-                        in.deviceId() + ". Dropping");
-                computePath(completePath, trace, null);
-                trace.setSuccess(false);
-                return trace;
-            } else {
-
-                //IF the table has a transition
-                if (flowEntry.treatment().tableTransition() != null) {
-                    //update the next table we transitions to
-                    tableId = IndexTableId.of(flowEntry.treatment().tableTransition().tableId());
-                    log.debug("Flow Entry has transition to table Id {}", tableId);
-                    flows.add(flowEntry);
-                } else {
-                    //table has no transition so it means that it's an output rule if on the last table
-                    log.debug("Flow Entry has no transition to table, treating as last rule {}", flowEntry);
-                    flows.add(flowEntry);
-                    outputFlows.add(flowEntry);
-                    output = true;
-                }
-                //update the packet according to the immediate actions of this flow rule.
-                packet = updatePacket(packet, flowEntry.treatment().immediate()).build();
-
-                //save the deferred rules for later
-                deferredInstructions.addAll(flowEntry.treatment().deferred());
-
-                //If the flow requires to clear deferred actions we do so for all the ones we encountered.
-                if (flowEntry.treatment().clearedDeferred()) {
-                    deferredInstructions.clear();
-                }
-
-                //On table 10 OFDPA needs two rules to apply the vlan if none and then to transition to the next table.
-                if (needsSecondTable10Flow(flowEntry, isOfdpaHardware)) {
-
-                    //Let's get the packet vlanId instruction
-                    VlanIdCriterion packetVlanIdCriterion =
-                            (VlanIdCriterion) packet.getCriterion(Criterion.Type.VLAN_VID);
-
-                    //Let's get the flow entry vlan mod instructions
-                    ModVlanIdInstruction entryModVlanIdInstruction = (ModVlanIdInstruction) flowEntry.treatment()
-                            .immediate().stream()
-                            .filter(instruction -> instruction instanceof ModVlanIdInstruction)
-                            .findFirst().orElse(null);
-
-                    //If the entry modVlan is not null we need to make sure that the packet has been updated and there
-                    // is a flow rule that matches on same criteria and with updated vlanId
-                    if (entryModVlanIdInstruction != null) {
-
-                        FlowEntry secondVlanFlow = getSecondFlowEntryOnTable10(packet, in,
-                                packetVlanIdCriterion, entryModVlanIdInstruction);
-
-                        //We found the flow that we expected
-                        if (secondVlanFlow != null) {
-                            flows.add(secondVlanFlow);
-                        } else {
-                            trace.addResultMessage("Missing forwarding rule for tagged packet on " + in);
-                            computePath(completePath, trace, null);
-                            return trace;
-                        }
-                    }
-
-                }
-
-            }
+        // If there was an error set the success to false
+        if (output.getResult() != PipelineTraceableResult.SUCCESS) {
+            TroubleshootUtils.computePath(completePath, trace, null);
+            trace.setSuccess(false);
         }
 
-        //Creating a modifiable builder for the output packet
-        Builder builder = DefaultTrafficSelector.builder();
-        packet.criteria().forEach(builder::add);
+        log.info("Logs -> {}", output.getLog());
+        hitChains.forEach(hitChain -> log.info("HitChain -> {}", hitChain));
 
-        //Adding all the flows to the trace
-        trace.addFlowsForDevice(in.deviceId(), ImmutableList.copyOf(flows));
-
-        List<PortNumber> outputPorts = new ArrayList<>();
-        List<FlowEntry> outputFlowEntries = handleFlows(trace, packet, in, outputFlows, builder, outputPorts);
-
-
-        log.debug("Handling Groups");
-        //Analyze Groups
-        List<Group> groups = new ArrayList<>();
-
-        Collection<FlowEntry> nonOutputFlows = flows;
-        nonOutputFlows.removeAll(outputFlowEntries);
-
-        //Handling groups pointed at by immediate instructions
-        for (FlowEntry entry : flows) {
-            getGroupsFromInstructions(trace, groups, entry.treatment().immediate(),
-                    entry.deviceId(), builder, outputPorts, in, completePath);
-        }
-
-        //If we have deferred instructions at this point we handle them.
-        if (deferredInstructions.size() > 0) {
-            builder = handleDeferredActions(trace, packet, in, deferredInstructions, outputPorts, groups, completePath);
-
-        }
-        packet = builder.build();
-
-        log.debug("Output Packet {}", packet);
+        // We are done!
         return trace;
     }
 
-    private List<FlowEntry> handleFlows(StaticPacketTrace trace, TrafficSelector packet, ConnectPoint in,
-                                        List<FlowEntry> outputFlows, Builder builder, List<PortNumber> outputPorts) {
-        //TODO optimization
-        //outputFlows contains also last rule of device, so we need filtering for OUTPUT instructions.
-        List<FlowEntry> outputFlowEntries = outputFlows.stream().filter(flow -> flow.treatment()
-                .allInstructions().stream().filter(instruction -> instruction.type()
-                        .equals(Instruction.Type.OUTPUT)).count() > 0).collect(Collectors.toList());
-
-        if (outputFlowEntries.size() > 1) {
-            trace.addResultMessage("More than one flow rule with OUTPUT instruction");
-            log.warn("There cannot be more than one flow entry with OUTPUT instruction for {}", packet);
-        }
-
-        if (outputFlowEntries.size() == 1) {
-
-            OutputInstruction outputInstruction = (OutputInstruction) outputFlowEntries.get(0).treatment()
-                    .allInstructions().stream()
-                    .filter(instruction -> {
-                        return instruction.type().equals(Instruction.Type.OUTPUT);
-                    }).findFirst().get();
-
-            //FIXME using GroupsInDevice for output even if flows.
-            buildOutputFromDevice(trace, in, builder, outputPorts, outputInstruction, ImmutableList.of());
-
-        }
-        return outputFlowEntries;
-    }
-
+    // Compute whether or not there are multiple routes.
     private boolean multipleRoutes(StaticPacketTrace trace) {
         boolean multipleRoutes = false;
         IPCriterion ipCriterion = ((IPCriterion) trace.getInitialPacket().getCriterion(Criterion.Type.IPV4_DST));
@@ -980,424 +762,51 @@
                     .collect(Collectors.toList());
             //build an output from each one
             enabledPorts.forEach(port -> {
-                GroupsInDevice output = new GroupsInDevice(new ConnectPoint(port.element().id(), port.number()),
-                        ImmutableList.of(), trace.getInitialPacket());
-                trace.addGroupOutputPath(in.deviceId(), output);
+                PipelineTraceableHitChain hitChain = new PipelineTraceableHitChain(
+                        new ConnectPoint(port.element().id(), port.number()), ImmutableList.of(),
+                        trace.getInitialPacket());
+                trace.addHitChain(in.deviceId(), hitChain);
             });
             return trace;
         }
         return null;
     }
 
-    private boolean needsSecondTable10Flow(FlowEntry flowEntry, boolean isOfdpaHardware) {
-        return isOfdpaHardware && flowEntry.table().equals(IndexTableId.of(10))
-                && flowEntry.selector().getCriterion(Criterion.Type.VLAN_VID) != null
-                && ((VlanIdCriterion) flowEntry.selector().getCriterion(Criterion.Type.VLAN_VID))
-                .vlanId().equals(VlanId.NONE);
-    }
-
-    /**
-     * Method that finds a flow rule on table 10 that matches the packet and the VLAN of the already
-     * found rule on table 10. This is because OFDPA needs two rules on table 10, first to apply the rule,
-     * second to transition to following table
-     *
-     * @param packet                    the incoming packet
-     * @param in                        the input connect point
-     * @param packetVlanIdCriterion     the vlan criterion from the packet
-     * @param entryModVlanIdInstruction the entry vlan instruction
-     * @return the second flow entry that matched
-     */
-    private FlowEntry getSecondFlowEntryOnTable10(TrafficSelector packet, ConnectPoint in,
-                                                  VlanIdCriterion packetVlanIdCriterion,
-                                                  ModVlanIdInstruction entryModVlanIdInstruction) {
-        FlowEntry secondVlanFlow = null;
-        //Check the packet has been update from the first rule.
-        if (packetVlanIdCriterion.vlanId().equals(entryModVlanIdInstruction.vlanId())) {
-            //find a rule on the same table that matches the vlan and
-            // also all the other elements of the flow such as input port
-            secondVlanFlow = Lists.newArrayList(flowNib.getFlowEntriesByState(in.deviceId(),
-                    FlowEntry.FlowEntryState.ADDED)
-                    .iterator()).stream()
-                    .filter(entry -> {
-                        return entry.table().equals(IndexTableId.of(10));
-                    })
-                    .filter(entry -> {
-                        VlanIdCriterion criterion = (VlanIdCriterion) entry.selector()
-                                .getCriterion(Criterion.Type.VLAN_VID);
-                        return criterion != null && match(packet, entry)
-                                && criterion.vlanId().equals(entryModVlanIdInstruction.vlanId());
-                    }).findFirst().orElse(null);
-
-        }
-        return secondVlanFlow;
-    }
-
-
-    /**
-     * Handles table 27 in Ofpda which is a fixed table not visible to any controller that handles Mpls Labels.
-     *
-     * @param packet the incoming packet
-     * @return the updated packet
-     */
-    private TrafficSelector handleOfdpa27FixedTable(StaticPacketTrace trace, TrafficSelector packet) {
-        log.debug("Handling table 27 on OFDPA, removing mpls ETH Type and change mpls label");
-        Criterion mplsCriterion = packet.getCriterion(Criterion.Type.ETH_TYPE);
-        ImmutableList.Builder<Instruction> builder = ImmutableList.builder();
-
-        //If the pakcet comes in with the expected elements we update it as per OFDPA spec.
-        if (mplsCriterion != null && ((EthTypeCriterion) mplsCriterion).ethType()
-                .equals(EtherType.MPLS_UNICAST.ethType())) {
-            //TODO update with parsing with eth MPLS pop Instruction for treating label an bos
-            Instruction ethInstruction = Instructions.popMpls(((EthTypeCriterion) trace.getInitialPacket()
-                    .getCriterion(Criterion.Type.ETH_TYPE)).ethType());
-            //FIXME what do we use as L3_Unicast mpls Label ?
-            //translateInstruction(builder, ethInstruction);
-            builder.add(ethInstruction);
-        }
-        packet = updatePacket(packet, builder.build()).build();
-        return packet;
-    }
-
-    /**
-     * Finds the flow entry with the minimun next table Id.
-     *
-     * @param deviceId  the device to search
-     * @param currentId the current id. the search will use this as minimum
-     * @return the flow entry with the minimum table Id after the given one.
-     */
-    private FlowEntry findNextTableIdEntry(DeviceId deviceId, int currentId) {
-
-        final Comparator<FlowEntry> comparator = Comparator.comparing((FlowEntry f) -> ((IndexTableId) f.table()).id());
-        return Lists.newArrayList(flowNib.getFlowEntriesByState(deviceId, FlowEntry.FlowEntryState.ADDED)
-                .iterator()).stream()
-                .filter(f -> ((IndexTableId) f.table()).id() > currentId).min(comparator).orElse(null);
-    }
-
-    private Builder handleDeferredActions(StaticPacketTrace trace, TrafficSelector packet,
-                                          ConnectPoint in, List<Instruction> deferredInstructions,
-                                          List<PortNumber> outputPorts, List<Group> groups,
-                                          List<ConnectPoint> completePath) {
-
-        //Update the packet with the deferred instructions
-        Builder builder = updatePacket(packet, deferredInstructions);
-
-        //Gather any output instructions from the deferred instruction
-        List<Instruction> outputFlowInstruction = deferredInstructions.stream().filter(instruction -> {
-            return instruction.type().equals(Instruction.Type.OUTPUT);
-        }).collect(Collectors.toList());
-
-        //We are considering deferred instructions from flows, there can only be one output.
-        if (outputFlowInstruction.size() > 1) {
-            trace.addResultMessage("More than one flow rule with OUTPUT instruction");
-            log.warn("There cannot be more than one flow entry with OUTPUT instruction for {}", packet);
-        }
-        //If there is one output let's go through that
-        if (outputFlowInstruction.size() == 1) {
-            buildOutputFromDevice(trace, in, builder, outputPorts, (OutputInstruction) outputFlowInstruction.get(0),
-                    ImmutableList.of());
-        }
-        //If there is no output let's see if there any deferred instruction point to groups.
-        if (outputFlowInstruction.size() == 0) {
-            getGroupsFromInstructions(trace, groups, deferredInstructions,
-                    in.deviceId(), builder, outputPorts, in, completePath);
-        }
-        return builder;
-    }
-
-    /**
-     * Gets group information from instructions.
-     *
-     * @param trace           the trace we are building
-     * @param groupsForDevice the set of groups for this device
-     * @param instructions    the set of instructions we are searching for groups.
-     * @param deviceId        the device we are considering
-     * @param builder         the builder of the input packet
-     * @param outputPorts     the output ports for that packet
-     */
-    private void getGroupsFromInstructions(StaticPacketTrace trace, List<Group> groupsForDevice,
-                                           List<Instruction> instructions, DeviceId deviceId,
-                                           Builder builder, List<PortNumber> outputPorts,
-                                           ConnectPoint in, List<ConnectPoint> completePath) {
-        List<Instruction> groupInstructionlist = new ArrayList<>();
-        // sort instructions according to priority (larger Instruction.Type ENUM constant first)
-        // which enables to treat other actions before the OUTPUT action
-        //TODO improve the priority scheme according to the OpenFlow ActionSet spec
-        List<Instruction> instructionsSorted = new ArrayList<>();
-        instructionsSorted.addAll(instructions);
-        instructionsSorted.sort((instr1, instr2) -> {
-            return Integer.compare(instr2.type().ordinal(), instr1.type().ordinal());
-        });
-
-        for (Instruction instruction : instructionsSorted) {
-            log.debug("Considering Instruction {}", instruction);
-            //if the instruction is not group we need to update the packet or add the output
-            //to the possible outputs for this packet
-            if (!instruction.type().equals(Instruction.Type.GROUP)) {
-                //if the instruction is not group we need to update the packet or add the output
-                //to the possible outputs for this packet
-                if (instruction.type().equals(Instruction.Type.OUTPUT)) {
-                    buildOutputFromDevice(trace, in, builder, outputPorts,
-                            (OutputInstruction) instruction, ImmutableList.copyOf(groupsForDevice));
-                    //clearing the groups because we start from the top.
-                    groupsForDevice.clear();
-                } else {
-                    builder = translateInstruction(builder, instruction);
-                }
-            } else {
-                //if the instuction is pointing to a group we need to get the group
-                groupInstructionlist.add(instruction);
-            }
-        }
-        //handle all the internal instructions pointing to a group.
-        for (Instruction instr : groupInstructionlist) {
-            GroupInstruction groupInstruction = (GroupInstruction) instr;
-            Group group = Lists.newArrayList(groupNib.getGroups(deviceId)).stream().filter(groupInternal -> {
-                return groupInternal.id().equals(groupInstruction.groupId());
-            }).findAny().orElse(null);
-            if (group == null) {
-                trace.addResultMessage("Null group for Instruction " + instr);
-                trace.setSuccess(false);
-                break;
-            }
-            if (group.buckets().buckets().size() == 0) {
-                trace.addResultMessage("Group " + group.id() + " has no buckets");
-                trace.setSuccess(false);
-                computePath(completePath, trace, null);
-                break;
-            }
-
-            //Cycle in each of the group's buckets and add them to the groups for this Device.
-            for (GroupBucket bucket : group.buckets().buckets()) {
-
-                //add the group to the traversed groups
-                if (!groupsForDevice.contains(group)) {
-                    groupsForDevice.add(group);
-                }
-
-                getGroupsFromInstructions(trace, groupsForDevice, bucket.treatment().allInstructions(),
-                        deviceId, builder, outputPorts, in, completePath);
-            }
-        }
-    }
-
-    /**
-     * Check if the output is the input port, if so adds a dop result message, otherwise builds
-     * a possible output from this device.
-     *
-     * @param trace             the trace
-     * @param in                the input connect point
-     * @param builder           the packet builder
-     * @param outputPorts       the list of output ports for this device
-     * @param outputInstruction the output instruction
-     * @param groupsForDevice   the groups we output from
-     */
-    private void buildOutputFromDevice(StaticPacketTrace trace, ConnectPoint in, Builder builder,
-                                       List<PortNumber> outputPorts, OutputInstruction outputInstruction,
-                                       List<Group> groupsForDevice) {
-        ConnectPoint output = new ConnectPoint(in.deviceId(), outputInstruction.port());
-
-        outputPorts.add(outputInstruction.port());
-
-        GroupsInDevice device = new GroupsInDevice(output, groupsForDevice, builder.build());
-        if (trace.getGroupOuputs(output.deviceId()) != null
-                && trace.getGroupOuputs(output.deviceId()).contains(device)) {
-            return;
-        }
-        trace.addGroupOutputPath(in.deviceId(),
-                new GroupsInDevice(output, groupsForDevice, builder.build()));
-    }
-
-    /**
-     * Applies all give instructions to the input packet.
-     *
-     * @param packet       the input packet
-     * @param instructions the set of instructions
-     * @return the packet with the applied instructions
-     */
-    private Builder updatePacket(TrafficSelector packet, List<Instruction> instructions) {
-        Builder newSelector = DefaultTrafficSelector.builder();
-        packet.criteria().forEach(newSelector::add);
-        //FIXME optimize
-        for (Instruction instruction : instructions) {
-            newSelector = translateInstruction(newSelector, instruction);
-        }
-        return newSelector;
-    }
-
-    /**
-     * Applies an instruction to the packet in the form of a selector.
-     *
-     * @param newSelector the packet selector
-     * @param instruction the instruction to be translated
-     * @return the new selector with the applied instruction
-     */
-    private Builder translateInstruction(Builder newSelector, Instruction instruction) {
-        log.debug("Translating instruction {}", instruction);
-        log.debug("New Selector {}", newSelector.build());
-        //TODO add as required
-        Criterion criterion = null;
-        switch (instruction.type()) {
-            case L2MODIFICATION:
-                L2ModificationInstruction l2Instruction = (L2ModificationInstruction) instruction;
-                switch (l2Instruction.subtype()) {
-                    case VLAN_ID:
-                        ModVlanIdInstruction vlanIdInstruction =
-                                (ModVlanIdInstruction) instruction;
-                        VlanId id = vlanIdInstruction.vlanId();
-                        criterion = Criteria.matchVlanId(id);
-                        break;
-                    case VLAN_POP:
-                        criterion = Criteria.matchVlanId(VlanId.NONE);
-                        break;
-                    case MPLS_PUSH:
-                        ModMplsHeaderInstruction mplsEthInstruction =
-                                (ModMplsHeaderInstruction) instruction;
-                        criterion = Criteria.matchEthType(mplsEthInstruction.ethernetType().toShort());
-                        break;
-                    case MPLS_POP:
-                        ModMplsHeaderInstruction mplsPopInstruction =
-                                (ModMplsHeaderInstruction) instruction;
-                        criterion = Criteria.matchEthType(mplsPopInstruction.ethernetType().toShort());
-
-                        //When popping MPLS we remove label and BOS
-                        TrafficSelector temporaryPacket = newSelector.build();
-                        if (temporaryPacket.getCriterion(Criterion.Type.MPLS_LABEL) != null) {
-                            Builder noMplsSelector = DefaultTrafficSelector.builder();
-                            temporaryPacket.criteria().stream().filter(c -> {
-                                return !c.type().equals(Criterion.Type.MPLS_LABEL) &&
-                                        !c.type().equals(Criterion.Type.MPLS_BOS);
-                            }).forEach(noMplsSelector::add);
-                            newSelector = noMplsSelector;
-                        }
-
-                        break;
-                    case MPLS_LABEL:
-                        ModMplsLabelInstruction mplsLabelInstruction =
-                                (ModMplsLabelInstruction) instruction;
-                        criterion = Criteria.matchMplsLabel(mplsLabelInstruction.label());
-                        newSelector.matchMplsBos(true);
-                        break;
-                    case ETH_DST:
-                        ModEtherInstruction modEtherDstInstruction =
-                                (ModEtherInstruction) instruction;
-                        criterion = Criteria.matchEthDst(modEtherDstInstruction.mac());
-                        break;
-                    case ETH_SRC:
-                        ModEtherInstruction modEtherSrcInstruction =
-                                (ModEtherInstruction) instruction;
-                        criterion = Criteria.matchEthSrc(modEtherSrcInstruction.mac());
-                        break;
-                    default:
-                        log.debug("Unsupported L2 Instruction");
-                        break;
-                }
-                break;
-            default:
-                log.debug("Unsupported Instruction");
-                break;
-        }
-        if (criterion != null) {
-            log.debug("Adding criterion {}", criterion);
-            newSelector.add(criterion);
-        }
-        return newSelector;
-    }
-
-    /**
-     * Finds the rule in the device that mathces the input packet and has the highest priority.
-     *
-     * @param packet  the input packet
-     * @param in      the connect point the packet comes in from
-     * @param tableId the table to search
-     * @return the flow entry
-     */
-    private FlowEntry matchHighestPriority(TrafficSelector packet, ConnectPoint in, TableId tableId) {
-        //Computing the possible match rules.
-        final Comparator<FlowEntry> comparator = Comparator.comparing(FlowRule::priority);
-        return Lists.newArrayList(flowNib.getFlowEntriesByState(in.deviceId(), FlowEntry.FlowEntryState.ADDED)
-                .iterator()).stream()
-                .filter(flowEntry -> {
-                    return flowEntry.table().equals(tableId);
-                })
-                .filter(flowEntry -> {
-                    return match(packet, flowEntry);
-                }).max(comparator).orElse(null);
-    }
-
-    /**
-     * Matches the packet with the given flow entry.
-     *
-     * @param packet    the packet to match
-     * @param flowEntry the flow entry to match the packet against
-     * @return true if the packet matches the flow.
-     */
-    private boolean match(TrafficSelector packet, FlowEntry flowEntry) {
-        return flowEntry.selector().criteria().stream().allMatch(criterion -> {
-            Criterion.Type type = criterion.type();
-            //If the criterion has IP we need to do LPM to establish matching.
-            if (type.equals(Criterion.Type.IPV4_SRC) || type.equals(Criterion.Type.IPV4_DST) ||
-                    type.equals(Criterion.Type.IPV6_SRC) || type.equals(Criterion.Type.IPV6_DST)) {
-                return matchIp(packet, (IPCriterion) criterion);
-                //we check that the packet contains the criterion provided by the flow rule.
-            } else if (type.equals(Criterion.Type.ETH_SRC_MASKED)) {
-                return matchMac(packet, (EthCriterion) criterion, false);
-            } else if (type.equals(Criterion.Type.ETH_DST_MASKED)) {
-                return matchMac(packet, (EthCriterion) criterion, true);
-            } else {
-                return packet.criteria().contains(criterion);
+    ////////////////////////////////
+    // Cemetery - Deprecated code //
+    ////////////////////////////////
+    @Override
+    public List<StaticPacketTrace> pingAll(EtherType type) {
+        ImmutableList.Builder<StaticPacketTrace> tracesBuilder = ImmutableList.builder();
+        hostNib.getHosts().forEach(host -> {
+            List<IpAddress> ipAddresses = getIpAddresses(host, type, false);
+            if (ipAddresses.size() > 0) {
+                //check if the host has only local IPs of that ETH type
+                boolean onlyLocalSrc = ipAddresses.size() == 1 && ipAddresses.get(0).isLinkLocal();
+                hostNib.getHosts().forEach(hostToPing -> {
+                    List<IpAddress> ipAddressesToPing = getIpAddresses(hostToPing, type, false);
+                    //check if the other host has only local IPs of that ETH type
+                    boolean onlyLocalDst = ipAddressesToPing.size() == 1 && ipAddressesToPing.get(0).isLinkLocal();
+                    boolean sameLocation = Sets.intersection(host.locations(), hostToPing.locations()).size() > 0;
+                    //Trace is done only if they are both local and under the same location
+                    // or not local and if they are not the same host.
+                    if (((sameLocation && onlyLocalDst && onlyLocalSrc) ||
+                            (!onlyLocalSrc && !onlyLocalDst && ipAddressesToPing.size() > 0))
+                            && !host.equals(hostToPing)) {
+                        tracesBuilder.addAll(trace(host.id(), hostToPing.id(), type));
+                    }
+                });
             }
         });
+        return tracesBuilder.build();
     }
 
-    /**
-     * Checks if the packet has an dst or src IP and if that IP matches the subnet of the ip criterion.
-     *
-     * @param packet    the incoming packet
-     * @param criterion the criterion to match
-     * @return true if match
-     */
-    private boolean matchIp(TrafficSelector packet, IPCriterion criterion) {
-        IPCriterion matchCriterion = (IPCriterion) packet.getCriterion(criterion.type());
-        //if the packet does not have an IPv4 or IPv6 criterion we return true
-        if (matchCriterion == null) {
-            return false;
-        }
-        try {
-            log.debug("Checking if {} is under {}", matchCriterion.ip(), criterion.ip());
-            Subnet subnet = Subnet.createInstance(criterion.ip().toString());
-            return subnet.isInSubnet(matchCriterion.ip().address().toInetAddress());
-        } catch (UnknownHostException e) {
-            return false;
-        }
+    @Override
+    public List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId) {
+        Generator<Set<StaticPacketTrace>> gen = new McastGenerator(mcastRouteNib, this, vlanId);
+        List<Set<StaticPacketTrace>> multicastTraceList =
+                StreamSupport.stream(gen.spliterator(), false).collect(Collectors.toList());
+        return multicastTraceList;
     }
 
-    /**
-     * Checks if the packet has a dst or src MAC and if that Mac matches the mask of the mac criterion.
-     *
-     * @param packet       the incoming packet
-     * @param hitCriterion the criterion to match
-     * @param dst          true if we are checking DST MAC
-     * @return true if match
-     */
-    private boolean matchMac(TrafficSelector packet, EthCriterion hitCriterion, boolean dst) {
-        //Packet can have only one EthCriterion
-        EthCriterion matchCriterion;
-        if (dst) {
-            matchCriterion = (EthCriterion) packet.criteria().stream().filter(criterion1 -> {
-                return criterion1.type().equals(Criterion.Type.ETH_DST_MASKED) ||
-                        criterion1.type().equals(Criterion.Type.ETH_DST);
-            }).findFirst().orElse(null);
-        } else {
-            matchCriterion = (EthCriterion) packet.criteria().stream().filter(criterion1 -> {
-                return criterion1.type().equals(Criterion.Type.ETH_SRC_MASKED) ||
-                        criterion1.type().equals(Criterion.Type.ETH_SRC);
-            }).findFirst().orElse(null);
-        }
-        //if the packet does not have an ETH criterion we return true
-        if (matchCriterion == null) {
-            return true;
-        }
-        log.debug("Checking if {} is under {}/{}", matchCriterion.mac(), hitCriterion.mac(), hitCriterion.mask());
-        return compareMac(matchCriterion.mac(), hitCriterion.mac(), hitCriterion.mask());
-    }
 }
diff --git a/app/src/main/java/org/onosproject/t3/impl/TroubleshootUtils.java b/app/src/main/java/org/onosproject/t3/impl/TroubleshootUtils.java
index 097fa92..ccc4715 100644
--- a/app/src/main/java/org/onosproject/t3/impl/TroubleshootUtils.java
+++ b/app/src/main/java/org/onosproject/t3/impl/TroubleshootUtils.java
@@ -16,10 +16,12 @@
 
 package org.onosproject.t3.impl;
 
-import com.google.common.collect.ImmutableMap;
-import org.onlab.packet.MacAddress;
+import com.google.common.collect.ImmutableList;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.t3.api.StaticPacketTrace;
 
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Utility class for the troubleshooting tool.
@@ -31,50 +33,33 @@
     }
 
     /**
-     * Map defining if a specific driver is for a HW switch.
+     * Computes the list of traversed connect points.
+     *
+     * @param completePath the list of devices
+     * @param trace        the trace we are building
+     * @param output       the final output connect point
+     * @return true only if the path is successfully computed and added to the trace
      */
-    //Done with builder() instead of of() for clarity
-    static Map<String, Boolean> hardwareOfdpaMap = ImmutableMap.<String, Boolean>builder()
-            .put("ofdpa", true)
-            .put("ofdpa3", true)
-            .put("qmx-ofdpa3", true)
-            .put("as7712-32x-premium", true)
-            .put("as5912-54x-premium", true)
-            .put("as5916-54x-premium", true)
-            .put("accton-ofdpa3", true)
-            .put("delta-ofdpa3", true)
-            .put("znyx-ofdpa", true)
-            .build();
-
-    /**
-     * Checks if the Mac Address is inside a range between the min MAC and the mask.
-     * @param macAddress the MAC address to check
-     * @param minAddr the min MAC address
-     * @param maskAddr the mask
-     * @return true if in range, false otherwise.
-     */
-    static boolean compareMac(MacAddress macAddress, MacAddress minAddr, MacAddress maskAddr) {
-        byte[] mac = macAddress.toBytes();
-        byte[] min = minAddr.toBytes();
-        byte[] mask = maskAddr.toBytes();
-        boolean inRange = true;
-
-        int i = 0;
-
-        //if mask is 00 stop
-        while (inRange && i < mask.length && (mask[i] & 0xFF) != 0) {
-            int ibmac = mac[i] & 0xFF;
-            int ibmin = min[i] & 0xFF;
-            int ibmask = mask[i] & 0xFF;
-            if (ibmask == 255) {
-                inRange = ibmac == ibmin;
-            } else if (ibmac < ibmin || ibmac >= ibmask) {
-                inRange = false;
-                break;
-            }
-            i++;
+    static boolean computePath(List<ConnectPoint> completePath, StaticPacketTrace trace, ConnectPoint output) {
+        List<ConnectPoint> traverseList = new ArrayList<>();
+        if (!completePath.contains(trace.getInitialConnectPoint())) {
+            traverseList.add(trace.getInitialConnectPoint());
         }
 
-        return inRange;
+        if (output != null && trace.getInitialConnectPoint().deviceId().equals(output.deviceId())) {
+            trace.addCompletePath(ImmutableList.of(trace.getInitialConnectPoint(), output));
+            return true;
+        }
+
+        traverseList.addAll(completePath);
+        if (output != null && !completePath.contains(output)) {
+            traverseList.add(output);
+        }
+        if (!trace.getCompletePaths().contains(traverseList)) {
+            trace.addCompletePath(ImmutableList.copyOf(traverseList));
+            return true;
+        }
+        return false;
     }
+
 }
diff --git a/app/src/main/java/org/onosproject/t3/rest/T3WebResource.java b/app/src/main/java/org/onosproject/t3/rest/T3WebResource.java
index 275be8c..571979f 100644
--- a/app/src/main/java/org/onosproject/t3/rest/T3WebResource.java
+++ b/app/src/main/java/org/onosproject/t3/rest/T3WebResource.java
@@ -23,6 +23,7 @@
 import org.onlab.packet.EthType;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.VlanId;
+import org.onlab.util.Generator;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
 import org.onosproject.net.flow.FlowEntry;
@@ -47,6 +48,8 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -153,13 +156,16 @@
         final ObjectNode nodeOutput = mapper.createObjectNode();
         nodeOutput.put("title", "Tracing all Multicast routes in the System");
 
-        //Create the generator for the list of traces.
-        List<Set<StaticPacketTrace>> generator = troubleshootService.getMulitcastTrace(VlanId.vlanId("None"));
+        // FIXME Vlan as argument and use None if not present
+        // Create the generator for the list of traces.
+        Generator<Set<StaticPacketTrace>> gen = troubleshootService.traceMcast(VlanId.NONE);
+        List<Set<StaticPacketTrace>> multicastTraceList = StreamSupport.stream(gen.spliterator(),
+                false).collect(Collectors.toList());
         int totalTraces = 0;
         List<StaticPacketTrace> failedTraces = new ArrayList<>();
         StaticPacketTrace previousTrace = null;
         ArrayNode traceArray = mapper.createArrayNode();
-        for (Set<StaticPacketTrace> traces : generator) {
+        for (Set<StaticPacketTrace> traces : multicastTraceList) {
             ObjectNode genNode = mapper.createObjectNode();
             totalTraces++;
             //Print also Route if possible or packet
@@ -321,6 +327,10 @@
      * @param trace the trace
      * @return a json representing the trace.
      */
+    // FIXME This part is completely broken, it will work only if the path size is 1
+    // Plan is to redefine the data model and uses arrays when printing each hop.
+    // In general there is a lot of confusion and the data model is not very clear
+    // and not easy to parse.
     private ObjectNode getTrace(StaticPacketTrace trace, boolean verbose) {
 
         ObjectNode nodeOutput = mapper.createObjectNode();
@@ -378,6 +388,7 @@
     }
 
     //Return groups Object for a given trace and a specified level of verbosity
+    // FIXME Double check
     private ObjectNode getGroupObj(ConnectPoint connectPoint, GroupsInDevice output, boolean verbose) {
         ArrayNode groupArray = mapper.createArrayNode();
         ObjectNode groupsObj = mapper.createObjectNode();
@@ -402,6 +413,7 @@
     }
 
     //Return flows Object for a given trace and a specified level of verbosity
+    // FIXME Double check
     private ArrayNode getFlowArray(StaticPacketTrace trace, ConnectPoint connectPoint, boolean verbose) {
         ArrayNode flowArray = mapper.createArrayNode();
         trace.getFlowsForDevice(connectPoint.deviceId()).forEach(f -> {
diff --git a/app/src/test/java/org/onosproject/t3/impl/T3TestObjects.java b/app/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
index 2ec39b4..2a8d1bf 100644
--- a/app/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
+++ b/app/src/test/java/org/onosproject/t3/impl/T3TestObjects.java
@@ -113,6 +113,10 @@
     //ARP
     static final DeviceId ARP_FLOW_DEVICE = DeviceId.deviceId("ArpDevice");
 
+    private static final TrafficSelector PORT_SELECTOR = DefaultTrafficSelector.builder()
+            .matchInPort(PortNumber.portNumber(1))
+            .build();
+
     private static final TrafficSelector ARP_FLOW_SELECTOR = DefaultTrafficSelector.builder()
             .matchInPort(PortNumber.portNumber(1))
             .matchEthType(EthType.EtherType.ARP.ethType().toShort())
@@ -121,7 +125,7 @@
     private static final TrafficTreatment ARP_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
             .setOutput(PortNumber.CONTROLLER).build();
     private static final FlowRule ARP_FLOW = DefaultFlowEntry.builder().forDevice(ARP_FLOW_DEVICE)
-            .forTable(0)
+            .forTable(10)
             .withPriority(100)
             .withSelector(ARP_FLOW_SELECTOR)
             .withTreatment(ARP_FLOW_TREATMENT)
@@ -132,13 +136,28 @@
 
     static final ConnectPoint ARP_FLOW_CP = ConnectPoint.deviceConnectPoint(ARP_FLOW_DEVICE + "/" + 1);
 
+    // ARP Vlan test
+    static final DeviceId ARP_FLOW_VLAN_DEVICE = DeviceId.deviceId("ArpDeviceVlan");
 
-    //Dual Flow Test
-    static final DeviceId DUAL_FLOW_DEVICE = DeviceId.deviceId("DualFlowDevice");
     private static final TrafficTreatment TRANSITION_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
             .setVlanId(VlanId.vlanId((short) 100))
             .transition(10)
             .build();
+    private static final FlowRule ARP_FLOW_VLAN = DefaultFlowEntry.builder().forDevice(ARP_FLOW_VLAN_DEVICE)
+            .forTable(0)
+            .withPriority(100)
+            .withSelector(PORT_SELECTOR)
+            .withTreatment(TRANSITION_FLOW_TREATMENT)
+            .fromApp(new DefaultApplicationId(0, "TestApp"))
+            .makePermanent()
+            .build();
+    static final FlowEntry ARP_FLOW_VLAN_ENTRY = new DefaultFlowEntry(ARP_FLOW_VLAN);
+
+    static final ConnectPoint ARP_FLOW_VLAN_CP = ConnectPoint.deviceConnectPoint(ARP_FLOW_VLAN_DEVICE + "/" + 1);
+
+    //Dual Flow Test
+    static final DeviceId DUAL_FLOW_DEVICE = DeviceId.deviceId("DualFlowDevice");
+
     private static final TrafficSelector VLAN_FLOW_SELECTOR = DefaultTrafficSelector.builder()
             .matchVlanId(VlanId.vlanId((short) 100))
             .build();
@@ -199,39 +218,6 @@
 
     static final ConnectPoint GROUP_FLOW_OUT_CP = ConnectPoint.deviceConnectPoint(GROUP_FLOW_DEVICE + "/" + 2);
 
-    // Group multiple action order test
-    static final DeviceId ACTION_ORDER_DEVICE = DeviceId.deviceId("ActionOrderDevice");
-    private static final VlanId ACTION_ORDER_VLAN_ID = VlanId.vlanId("999");
-    static final MplsLabel ACTION_ORDER_MPLS_LABEL = MplsLabel.mplsLabel("999");
-    private static final TrafficTreatment ACTION_ORDER_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
-            .pushVlan()
-            .setVlanId(ACTION_ORDER_VLAN_ID)
-            .group(GROUP_ID)
-            .build();
-    private static final FlowRule ACTION_ORDER_FLOW = DefaultFlowEntry.builder().forDevice(ACTION_ORDER_DEVICE)
-            .forTable(0)
-            .withPriority(100)
-            .withSelector(SINGLE_FLOW_SELECTOR)
-            .withTreatment(ACTION_ORDER_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-    static final FlowEntry ACTION_ORDER_FLOW_ENTRY = new DefaultFlowEntry(ACTION_ORDER_FLOW);
-    private static final TrafficTreatment ACTION_ORDER_GROUP_TREATMENT = DefaultTrafficTreatment.builder()
-            // make lower order actions come first
-            .setOutput(PortNumber.portNumber(2))
-            .setMpls(ACTION_ORDER_MPLS_LABEL)
-            .pushMpls()
-            .popVlan()
-            .build();
-    private static final GroupBucket ACTION_ORDER_BUCKET = DefaultGroupBucket
-            .createSelectGroupBucket(ACTION_ORDER_GROUP_TREATMENT);
-    private static final GroupBuckets ACTION_ORDER_BUCKETS = new GroupBuckets(ImmutableList.of(ACTION_ORDER_BUCKET));
-    static final Group ACTION_ORDER_GROUP = new DefaultGroup(
-            GROUP_ID, ACTION_ORDER_DEVICE, Group.Type.SELECT, ACTION_ORDER_BUCKETS);
-    static final ConnectPoint ACTION_ORDER_IN_CP = ConnectPoint.deviceConnectPoint(ACTION_ORDER_DEVICE + "/" + 1);
-    static final ConnectPoint ACTION_ORDER_OUT_CP = ConnectPoint.deviceConnectPoint(ACTION_ORDER_DEVICE + "/" + 2);
-
     //topology
 
     static final DeviceId TOPO_FLOW_DEVICE = DeviceId.deviceId("SingleFlowDevice1");
@@ -332,116 +318,6 @@
 
     static final ConnectPoint TOPO_FLOW_4_OUT_CP = ConnectPoint.deviceConnectPoint(TOPO_FLOW_4_DEVICE + "/" + 2);
 
-
-    //hardware
-
-    static final DeviceId HARDWARE_DEVICE = DeviceId.deviceId("HardwareDevice");
-
-    static final ConnectPoint HARDWARE_DEVICE_IN_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE + "/" + 1);
-
-    static final ConnectPoint HARDWARE_DEVICE_OUT_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE + "/" + 2);
-
-    private static final TrafficSelector HARDWARE_FLOW_SELECTOR = DefaultTrafficSelector.builder()
-            .matchInPort(PortNumber.portNumber(1))
-            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
-            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
-            .build();
-
-    private static final TrafficTreatment HW_TRANSITION_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
-            .pushMpls()
-            .transition(27)
-            .build();
-
-    private static final FlowRule HARDWARE_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
-            .forTable(0)
-            .withPriority(100)
-            .withSelector(HARDWARE_FLOW_SELECTOR)
-            .withTreatment(HW_TRANSITION_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-
-    static final FlowEntry HARDWARE_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_FLOW);
-
-    private static final TrafficSelector HARDWARE_ETH_FLOW_SELECTOR = DefaultTrafficSelector.builder()
-            .matchInPort(PortNumber.portNumber(1))
-            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
-            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
-            .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
-            .build();
-
-    private static final FlowRule HARDWARE_ETH_FLOW = DefaultFlowEntry.builder().forDevice(TOPO_FLOW_3_DEVICE)
-            .forTable(30)
-            .withPriority(100)
-            .withSelector(HARDWARE_ETH_FLOW_SELECTOR)
-            .withTreatment(OUTPUT_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-
-    static final FlowEntry HARDWARE_ETH_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_ETH_FLOW);
-
-    //HW Double Rule on 10
-
-    static final DeviceId HARDWARE_DEVICE_10 = DeviceId.deviceId("HardwareDevice10");
-
-    static final ConnectPoint HARDWARE_DEVICE_10_IN_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE_10 + "/" + 1);
-
-    static final ConnectPoint HARDWARE_DEVICE_10_OUT_CP = ConnectPoint.deviceConnectPoint(HARDWARE_DEVICE_10 + "/" + 2);
-
-    private static final TrafficSelector HARDWARE_10_FLOW_SELECTOR = DefaultTrafficSelector.builder()
-            .matchInPort(PortNumber.portNumber(1))
-            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
-            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
-            .matchVlanId(VlanId.NONE)
-            .build();
-
-    private static final TrafficTreatment HARDWARE_10_TRANSITION_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
-            .setVlanId(VlanId.vlanId("10"))
-            .transition(20)
-            .build();
-
-    private static final FlowRule HARDWARE_DEVICE_10_FLOW = DefaultFlowEntry.builder().forDevice(HARDWARE_DEVICE_10)
-            .forTable(10)
-            .withPriority(100)
-            .withSelector(HARDWARE_10_FLOW_SELECTOR)
-            .withTreatment(HARDWARE_10_TRANSITION_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-
-    static final FlowEntry HARDWARE_10_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_DEVICE_10_FLOW);
-
-    private static final TrafficSelector HARDWARE_10_SECOND_SELECTOR = DefaultTrafficSelector.builder()
-            .matchInPort(PortNumber.portNumber(1))
-            .matchVlanId(VlanId.vlanId("10"))
-            .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
-            .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
-            .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
-            .build();
-
-    private static final FlowRule HARDWARE_10_SECOND_FLOW = DefaultFlowEntry.builder().forDevice(HARDWARE_DEVICE_10)
-            .forTable(10)
-            .withPriority(100)
-            .withSelector(HARDWARE_10_SECOND_SELECTOR)
-            .withTreatment(HARDWARE_10_TRANSITION_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-
-    static final FlowEntry HARDWARE_10_SECOND_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_10_SECOND_FLOW);
-
-    private static final FlowRule HARDWARE_10_OUTPUT_FLOW = DefaultFlowEntry.builder().forDevice(HARDWARE_DEVICE_10)
-            .forTable(20)
-            .withPriority(100)
-            .withSelector(SINGLE_FLOW_SELECTOR)
-            .withTreatment(OUTPUT_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-
-    static final FlowEntry HARDWARE_10_OUTPUT_FLOW_ENTRY = new DefaultFlowEntry(HARDWARE_10_OUTPUT_FLOW);
-
     //Dual Links
     // - (1) Device 1 (2-3) - (1-4) Device 2 (2-3) - (1-2) Device 3 (3) -
     static final DeviceId DUAL_LINK_1 = DeviceId.deviceId("DualLink1");
@@ -538,29 +414,6 @@
 
     static final Group DUAL_LINK_GROUP = new DefaultGroup(GROUP_ID, DUAL_LINK_1, Group.Type.SELECT, BUCKETS_DUAL);
 
-    //Clear Deferred
-    static final DeviceId DEFERRED_1 = DeviceId.deviceId("Deferred");
-
-    static final ConnectPoint DEFERRED_CP_1_IN = ConnectPoint.deviceConnectPoint(DEFERRED_1 + "/" + 1);
-    static final ConnectPoint DEFERRED_CP_2_OUT = ConnectPoint.deviceConnectPoint(DEFERRED_1 + "/" + 2);
-
-    //match on port 1 and apply deferred actions
-    private static final TrafficTreatment DEFERRED_1_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
-            .transition(10)
-            .deferred()
-            .pushMpls()
-            .setMpls(MplsLabel.mplsLabel(100))
-            .build();
-    private static final FlowRule DEFERRED_FLOW = DefaultFlowEntry.builder().forDevice(DEFERRED_1)
-            .forTable(0)
-            .withPriority(100)
-            .withSelector(SINGLE_FLOW_SELECTOR)
-            .withTreatment(DEFERRED_1_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-    static final FlowEntry DEFERRED_FLOW_ENTRY = new DefaultFlowEntry(DEFERRED_FLOW);
-
     //Multicast Flow and Group Test
     static final DeviceId MULTICAST_GROUP_FLOW_DEVICE = DeviceId.deviceId("MulticastGroupFlowDevice");
 
@@ -592,21 +445,6 @@
     static final ConnectPoint MULTICAST_OUT_CP_2 =
             ConnectPoint.deviceConnectPoint(MULTICAST_GROUP_FLOW_DEVICE + "/" + 2);
 
-    //match on port 1, clear deferred actions and output
-    private static final TrafficTreatment DEFERRED_CLEAR_1_FLOW_TREATMENT = DefaultTrafficTreatment.builder()
-            .wipeDeferred()
-            .setOutput(PortNumber.portNumber(2))
-            .build();
-    private static final FlowRule DEFERRED_CLEAR_FLOW = DefaultFlowEntry.builder().forDevice(DEFERRED_1)
-            .forTable(10)
-            .withPriority(100)
-            .withSelector(SINGLE_FLOW_SELECTOR)
-            .withTreatment(DEFERRED_CLEAR_1_FLOW_TREATMENT)
-            .fromApp(new DefaultApplicationId(0, "TestApp"))
-            .makePermanent()
-            .build();
-    static final FlowEntry DEFERRED_CLEAR_FLOW_ENTRY = new DefaultFlowEntry(DEFERRED_CLEAR_FLOW);
-
     //LLDP
 
     static final DeviceId LLDP_FLOW_DEVICE = DeviceId.deviceId("LldpDevice");
@@ -752,6 +590,7 @@
             .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
             .matchIPDst(IpPrefix.valueOf("127.0.0.2/32"))
             .matchVlanId(VlanId.NONE)
+            .matchMetadata(EthType.EtherType.IPV4.ethType().toShort())
             .build();
 
     static final TrafficSelector PACKET_OK_TOPO = DefaultTrafficSelector.builder()
@@ -764,6 +603,7 @@
             .matchInPort(PortNumber.portNumber(1))
             .matchIPDst(IpPrefix.valueOf("255.255.255.255/32"))
             .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+            .matchVlanId(VlanId.NONE)
             .build();
 
     static final TrafficSelector PACKET_LLDP = DefaultTrafficSelector.builder()
@@ -790,4 +630,11 @@
             .matchIPSrc(IpPrefix.valueOf("127.0.0.1/32"))
             .matchIPDst(IpPrefix.valueOf("127.0.0.99/32"))
             .build();
+
+    static final String OFDPA_DRIVER = "ofdpa";
+    static final String MANUFACTURER = "test";
+    static final String HW_VERSION = "test";
+    static final String SW_VERSION = "test";
+    static final String SERIAL_NUMBER = "test";
+
 }
diff --git a/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java b/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
index d1eba21..3ab8f91 100644
--- a/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
+++ b/app/src/test/java/org/onosproject/t3/impl/TroubleshootManagerTest.java
@@ -17,6 +17,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.ChassisId;
@@ -25,7 +26,12 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.cluster.NodeId;
+import org.onosproject.driver.pipeline.ofdpa.Ofdpa2Pipeline;
+import org.onosproject.driver.traceable.OfdpaPipelineTraceable;
+import org.onosproject.net.AbstractProjectableModel;
+import org.onosproject.net.AnnotationKeys;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DataPlaneEntity;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DefaultDevice;
 import org.onosproject.net.DefaultLink;
@@ -36,11 +42,19 @@
 import org.onosproject.net.Link;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.behaviour.PipelineTraceable;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverAdapter;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.driver.DriverServiceAdapter;
+import org.onosproject.net.driver.HandlerBehaviour;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.criteria.Criterion;
 import org.onosproject.net.flow.criteria.EthTypeCriterion;
-import org.onosproject.net.flow.criteria.MplsCriterion;
 import org.onosproject.net.flow.criteria.VlanIdCriterion;
 import org.onosproject.net.group.Group;
 import org.onosproject.net.provider.ProviderId;
@@ -57,6 +71,7 @@
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.slf4j.Logger;
 
+import java.util.List;
 import java.util.Optional;
 import java.util.Set;
 
@@ -75,11 +90,15 @@
 public class TroubleshootManagerTest {
 
     private static final Logger log = getLogger(TroubleshootManager.class);
-
     private TroubleshootManager mngr;
+    private Driver baseDriver = new TestDriver();
 
     @Before
     public void setUp() throws Exception {
+        // Setup step for the device
+        DriverService testDeviceService = new TestDriverService();
+        AbstractProjectableModel.setDriverService(null, testDeviceService);
+
         mngr = new TroubleshootManager();
 
         mngr.flowNib = new TestFlowRuleService();
@@ -87,7 +106,7 @@
         mngr.hostNib = new TestHostService();
         mngr.linkNib = new TestLinkService();
         mngr.deviceNib = new TestDeviceService();
-        mngr.driverNib = new TestDriverService();
+        mngr.driverNib = new TestDriverNib();
         mngr.mastershipNib = new TestMastershipService();
         mngr.edgePortNib = new TestEdgePortService();
         mngr.routeNib = new TestRouteService();
@@ -111,6 +130,7 @@
     @Test(expected = NullPointerException.class)
     public void nonExistentDevice() {
         StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint("nonexistent" + "/1"));
+        log.info("trace {}", traceFail.resultMessage());
     }
 
     /**
@@ -120,7 +140,10 @@
     public void offlineDevice() {
         StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint(OFFLINE_DEVICE + "/1"));
         assertNotNull("Trace should not be null", traceFail);
-        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(SINGLE_FLOW_DEVICE));
+        assertTrue("Device should be offline",
+                traceFail.resultMessage().contains("Device is offline"));
+        assertNull("Trace should have 0 output", traceFail.getHitChains(SINGLE_FLOW_DEVICE));
+        log.info("trace {}", traceFail.resultMessage());
     }
 
     /**
@@ -146,13 +169,32 @@
                 traceSuccess.resultMessage().contains(PACKET_TO_CONTROLLER));
         assertTrue("Master should be Master1",
                 traceSuccess.resultMessage().contains(MASTER_1));
-        ConnectPoint connectPoint = traceSuccess.getGroupOuputs(ARP_FLOW_DEVICE).get(0).getOutput();
+        ConnectPoint connectPoint = traceSuccess.getHitChains(ARP_FLOW_DEVICE).get(0).getOutputPort();
         assertEquals("Packet Should go to CONTROLLER", PortNumber.CONTROLLER, connectPoint.port());
-        assertNull("VlanId should be null", traceSuccess.getGroupOuputs(ARP_FLOW_DEVICE).get(0)
-                .getFinalPacket().getCriterion(Criterion.Type.VLAN_VID));
+        VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) traceSuccess.getHitChains(ARP_FLOW_DEVICE).get(0)
+                .getEgressPacket().getCriterion(Criterion.Type.VLAN_VID);
+        assertEquals("VlanId should be None", VlanId.NONE, vlanIdCriterion.vlanId());
         log.info("trace {}", traceSuccess.resultMessage());
     }
 
+    /**
+     * Tests ARP to controller and Vlan id removal.
+     */
+    @Test
+    public void arpToControllerVlan() {
+        StaticPacketTrace traceSuccess = mngr.trace(PACKET_ARP, ARP_FLOW_VLAN_CP);
+        assertNotNull("Trace should not be null", traceSuccess);
+        assertTrue("Trace should be successful",
+                traceSuccess.resultMessage().contains(PACKET_TO_CONTROLLER));
+        assertTrue("Master should be Master1",
+                traceSuccess.resultMessage().contains(MASTER_1));
+        ConnectPoint connectPoint = traceSuccess.getHitChains(ARP_FLOW_VLAN_DEVICE).get(0).getOutputPort();
+        assertEquals("Packet Should go to CONTROLLER", PortNumber.CONTROLLER, connectPoint.port());
+        VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) traceSuccess.getHitChains(ARP_FLOW_VLAN_DEVICE).get(0)
+                .getEgressPacket().getCriterion(Criterion.Type.VLAN_VID);
+        assertEquals("VlanId should be None", VlanId.NONE, vlanIdCriterion.vlanId());
+        log.info("trace {}", traceSuccess.resultMessage());
+    }
 
     /**
      * Tests failure on device with no flows.
@@ -161,7 +203,7 @@
     public void noFlows() {
         StaticPacketTrace traceFail = mngr.trace(PACKET_OK, ConnectPoint.deviceConnectPoint("test/1"));
         assertNotNull("Trace should not be null", traceFail);
-        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(SINGLE_FLOW_DEVICE));
+        assertNull("Trace should have 0 output", traceFail.getHitChains(SINGLE_FLOW_DEVICE));
         log.info("trace {}", traceFail.resultMessage());
     }
 
@@ -169,14 +211,12 @@
      * Test group with no buckets.
      */
     @Test
-    public void noBucketsTest() throws Exception {
-
+    public void noBucketsTest() {
         StaticPacketTrace traceFail = mngr.trace(PACKET_OK, NO_BUCKET_CP);
         assertNotNull("Trace should not be null", traceFail);
         assertTrue("Trace should be unsuccessful",
                 traceFail.resultMessage().contains("no buckets"));
         log.info("trace {}", traceFail.resultMessage());
-
     }
 
     /**
@@ -184,10 +224,10 @@
      */
     @Test
     public void testSingleFlowRule() {
-
+        // Happy ending
         testSuccess(PACKET_OK, SINGLE_FLOW_IN_CP, SINGLE_FLOW_DEVICE, SINGLE_FLOW_OUT_CP, 1, 1);
-
-        testFailure(PACKET_FAIL, SINGLE_FLOW_IN_CP, SINGLE_FLOW_DEVICE);
+        // Failure scenario
+        testFailure(PACKET_FAIL, SINGLE_FLOW_IN_CP, SINGLE_FLOW_DEVICE, 1);
     }
 
     /**
@@ -195,161 +235,95 @@
      */
     @Test
     public void testDualFlowRule() {
-
-        //Test Success
-
+        // Test Success
         StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, DUAL_FLOW_IN_CP, DUAL_FLOW_DEVICE,
                 DUAL_FLOW_OUT_CP, 1, 1);
-
-        //Testing Vlan
-        Criterion criterion = traceSuccess.getGroupOuputs(DUAL_FLOW_DEVICE).get(0).
-                getFinalPacket().getCriterion(Criterion.Type.VLAN_VID);
+        // Verifying Vlan
+        Criterion criterion = traceSuccess.getHitChains(DUAL_FLOW_DEVICE).get(0).
+                getEgressPacket().getCriterion(Criterion.Type.VLAN_VID);
         assertNotNull("Packet Should have Vlan", criterion);
-
         VlanIdCriterion vlanIdCriterion = (VlanIdCriterion) criterion;
-
         assertEquals("Vlan should be 100", VlanId.vlanId((short) 100), vlanIdCriterion.vlanId());
 
-        //Test Faliure
-        testFailure(PACKET_FAIL, DUAL_FLOW_IN_CP, DUAL_FLOW_DEVICE);
-
+        // Test Failure
+        testFailure(PACKET_FAIL, DUAL_FLOW_IN_CP, DUAL_FLOW_DEVICE, 1);
     }
 
     /**
      * Test a single flow rule that points to a group with output port in it.
      */
     @Test
-    public void flowAndGroup() throws Exception {
-
+    public void flowAndGroup() {
+        // Test Success
         StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, GROUP_FLOW_IN_CP, GROUP_FLOW_DEVICE,
                 GROUP_FLOW_OUT_CP, 1, 1);
-
-        assertTrue("Wrong Output Group", traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
-                .get(0).getGroups().contains(GROUP));
+        // Verify the output of the test
+        assertTrue("Wrong Output Group", traceSuccess.getHitChains(GROUP_FLOW_DEVICE)
+                .get(0).getHitChain().contains(new DataPlaneEntity(GROUP)));
         assertEquals("Packet should not have MPLS Label", EthType.EtherType.IPV4.ethType(),
-                ((EthTypeCriterion) traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
-                        .get(0).getFinalPacket().getCriterion(Criterion.Type.ETH_TYPE)).ethType());
-        assertNull("Packet should not have MPLS Label", traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
-                .get(0).getFinalPacket().getCriterion(Criterion.Type.MPLS_LABEL));
-        assertNull("Packet should not have MPLS Label", traceSuccess.getGroupOuputs(GROUP_FLOW_DEVICE)
-                .get(0).getFinalPacket().getCriterion(Criterion.Type.MPLS_BOS));
-
-    }
-
-    /**
-     * Test a single flow rule that points to a group with multiple actions
-     * that need to be executed in the order specified in the OpenFlow spec.
-     */
-    @Test
-    public void testGroupMultipleActionsOrdered() {
-
-        StaticPacketTrace traceSuccess = testSuccess(
-                PACKET_OK, ACTION_ORDER_IN_CP, ACTION_ORDER_DEVICE, ACTION_ORDER_OUT_CP, 1, 1);
-
-        assertEquals("Packet should not have VLAN ID",
-                VlanId.NONE,
-                ((VlanIdCriterion) traceSuccess.getGroupOuputs(ACTION_ORDER_DEVICE)
-                        .get(0).getFinalPacket().getCriterion(Criterion.Type.VLAN_VID)).vlanId());
-        assertEquals("Packet should have MPLS label",
-                ACTION_ORDER_MPLS_LABEL,
-                ((MplsCriterion) traceSuccess.getGroupOuputs(ACTION_ORDER_DEVICE)
-                        .get(0).getFinalPacket().getCriterion(Criterion.Type.MPLS_LABEL)).label());
-
+                ((EthTypeCriterion) traceSuccess.getHitChains(GROUP_FLOW_DEVICE)
+                        .get(0).getEgressPacket().getCriterion(Criterion.Type.ETH_TYPE)).ethType());
+        assertNull("Packet should not have MPLS Label", traceSuccess.getHitChains(GROUP_FLOW_DEVICE)
+                .get(0).getEgressPacket().getCriterion(Criterion.Type.MPLS_LABEL));
+        assertNull("Packet should not have MPLS BoS", traceSuccess.getHitChains(GROUP_FLOW_DEVICE)
+                .get(0).getEgressPacket().getCriterion(Criterion.Type.MPLS_BOS));
     }
 
     /**
      * Test path through a 3 device topology.
      */
     @Test
-    public void singlePathTopology() throws Exception {
-
+    public void singlePathTopology() {
+        // Test success
         StaticPacketTrace traceSuccess = testSuccess(PACKET_OK_TOPO, TOPO_FLOW_1_IN_CP,
                 TOPO_FLOW_3_DEVICE, TOPO_FLOW_3_OUT_CP, 1, 1);
-
-        assertTrue("Incorrect path",
-                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_2_IN_CP));
-        assertTrue("Incorrect path",
-                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_2_OUT_CP));
-        assertTrue("Incorrect path",
-                traceSuccess.getCompletePaths().get(0).contains(TOPO_FLOW_3_IN_CP));
-
+        // Verify that the complete path contains all the traversed connect points
+        List<ConnectPoint> path = Lists.newArrayList(TOPO_FLOW_1_IN_CP, TOPO_FLOW_1_OUT_CP,
+                TOPO_FLOW_2_IN_CP, TOPO_FLOW_2_OUT_CP, TOPO_FLOW_3_IN_CP, TOPO_FLOW_3_OUT_CP);
+        assertEquals(path, traceSuccess.getCompletePaths().get(0));
     }
 
     /**
      * Test path through a 4 device topology with first device that has groups with multiple output buckets.
      */
     @Test
-    public void testGroupTopo() throws Exception {
-
+    public void testGroupTopo() {
+        // Test success
         StaticPacketTrace traceSuccess = testSuccess(PACKET_OK_TOPO, TOPO_FLOW_IN_CP,
                 TOPO_FLOW_3_DEVICE, TOPO_FLOW_3_OUT_CP, 2, 1);
-
-        log.info("{}", traceSuccess);
-
+        // Verify the multiple output actions
         assertTrue("Incorrect groups",
-                traceSuccess.getGroupOuputs(TOPO_GROUP_FLOW_DEVICE).get(0).getGroups().contains(TOPO_GROUP));
+                traceSuccess.getHitChains(TOPO_GROUP_FLOW_DEVICE).get(0).getHitChain()
+                        .contains(new DataPlaneEntity(TOPO_GROUP)));
         assertTrue("Incorrect bucket",
-                traceSuccess.getGroupOuputs(TOPO_GROUP_FLOW_DEVICE).get(1).getGroups().contains(TOPO_GROUP));
-    }
-
-    /**
-     * Test HW support in a single device with 2 flow rules to check hit of static HW rules.
-     */
-    @Test
-    public void hardwareTest() throws Exception {
-
-        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, HARDWARE_DEVICE_IN_CP,
-                HARDWARE_DEVICE, HARDWARE_DEVICE_OUT_CP, 1, 1);
-
-        assertEquals("wrong ETH type", EthType.EtherType.IPV4.ethType(),
-                ((EthTypeCriterion) traceSuccess.getGroupOuputs(HARDWARE_DEVICE).get(0).getFinalPacket()
-                        .getCriterion(Criterion.Type.ETH_TYPE)).ethType());
-
-    }
-
-    /**
-     * Test that HW has two rules on table 10 for untagged packets.
-     */
-    @Test
-    public void hardwareTable10Test() throws Exception {
-
-        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, HARDWARE_DEVICE_10_IN_CP,
-                HARDWARE_DEVICE_10, HARDWARE_DEVICE_10_OUT_CP, 1, 1);
-
-        assertTrue("Second flow rule is absent", traceSuccess.getFlowsForDevice(HARDWARE_DEVICE_10)
-                .contains(HARDWARE_10_SECOND_FLOW_ENTRY));
-
+                traceSuccess.getHitChains(TOPO_GROUP_FLOW_DEVICE).get(1).getHitChain()
+                        .contains(new DataPlaneEntity(TOPO_GROUP)));
     }
 
     /**
      * Test dual links between 3 topology elements.
      */
     @Test
-    public void dualLinks() throws Exception {
-
+    public void dualLinks() {
+        // Success
         StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, DUAL_LINK_1_CP_1_IN,
                 DUAL_LINK_3, DUAL_LINK_3_CP_3_OUT, 4, 1);
-
-        //TODO tests
-
+        // Verify that the complete path contains all the traversed connect points
+        List<ConnectPoint> path = Lists.newArrayList(DUAL_LINK_1_CP_1_IN, DUAL_LINK_1_CP_2_OUT,
+                DUAL_LINK_2_CP_1_IN, DUAL_LINK_2_CP_2_OUT, DUAL_LINK_3_CP_1_IN, DUAL_LINK_3_CP_3_OUT);
+        assertTrue(traceSuccess.getCompletePaths().contains(path));
+        path = Lists.newArrayList(DUAL_LINK_1_CP_1_IN, DUAL_LINK_1_CP_2_OUT,
+                DUAL_LINK_2_CP_1_IN, DUAL_LINK_2_CP_3_OUT, DUAL_LINK_3_CP_2_IN, DUAL_LINK_3_CP_3_OUT);
+        assertTrue(traceSuccess.getCompletePaths().contains(path));
+        path = Lists.newArrayList(DUAL_LINK_1_CP_1_IN, DUAL_LINK_1_CP_3_OUT,
+                DUAL_LINK_2_CP_4_IN, DUAL_LINK_2_CP_2_OUT, DUAL_LINK_3_CP_1_IN, DUAL_LINK_3_CP_3_OUT);
+        assertTrue(traceSuccess.getCompletePaths().contains(path));
+        path = Lists.newArrayList(DUAL_LINK_1_CP_1_IN, DUAL_LINK_1_CP_3_OUT,
+                DUAL_LINK_2_CP_4_IN, DUAL_LINK_2_CP_3_OUT, DUAL_LINK_3_CP_2_IN, DUAL_LINK_3_CP_3_OUT);
+        assertTrue(traceSuccess.getCompletePaths().contains(path));
     }
 
     /**
-     * Test proper clear deferred behaviour.
-     */
-    @Test
-    public void clearDeferred() throws Exception {
-
-        StaticPacketTrace traceSuccess = testSuccess(PACKET_OK, DEFERRED_CP_1_IN,
-                DEFERRED_1, DEFERRED_CP_2_OUT, 1, 1);
-
-        assertNull("MPLS should have been not applied due to clear deferred", traceSuccess
-                .getGroupOuputs(DEFERRED_1).get(0).getFinalPacket().getCriterion(Criterion.Type.MPLS_LABEL));
-
-    }
-
-
-    /**
      * Test LLDP output to controller.
      */
     @Test
@@ -360,7 +334,7 @@
                 traceSuccess.resultMessage().contains("Packet goes to the controller"));
         assertTrue("Master should be Master1",
                 traceSuccess.resultMessage().contains(MASTER_1));
-        ConnectPoint connectPoint = traceSuccess.getGroupOuputs(LLDP_FLOW_DEVICE).get(0).getOutput();
+        ConnectPoint connectPoint = traceSuccess.getHitChains(LLDP_FLOW_DEVICE).get(0).getOutputPort();
         assertEquals("Packet Should go to CONTROLLER", PortNumber.CONTROLLER, connectPoint.port());
         log.info("trace {}", traceSuccess.resultMessage());
     }
@@ -369,37 +343,39 @@
      * Test multicast in single device.
      */
     @Test
-    public void multicastTest() throws Exception {
-
+    public void multicastTest() {
+        // Test success
         StaticPacketTrace traceSuccess = mngr.trace(PACKET_OK_MULTICAST, MULTICAST_IN_CP);
-
         log.info("trace {}", traceSuccess);
-
         log.info("trace {}", traceSuccess.resultMessage());
 
+        // Verify some conditions on the test
         assertNotNull("trace should not be null", traceSuccess);
-        assertEquals("Trace should have " + 2 + " output", 2,
-                traceSuccess.getGroupOuputs(MULTICAST_GROUP_FLOW_DEVICE).size());
-        assertEquals("Trace should only have " + 2 + "output", 2,
+        assertEquals("Trace should have " + 2 + " hitchains", 2,
+                traceSuccess.getHitChains(MULTICAST_GROUP_FLOW_DEVICE).size());
+        assertEquals("Trace should only have " + 2 + "paths", 2,
                 traceSuccess.getCompletePaths().size());
         assertTrue("Trace should be successful",
                 traceSuccess.resultMessage().contains("reached output"));
         assertEquals("Incorrect Output CP", MULTICAST_OUT_CP_2,
-                traceSuccess.getGroupOuputs(MULTICAST_GROUP_FLOW_DEVICE).get(0).getOutput());
+                traceSuccess.getHitChains(MULTICAST_GROUP_FLOW_DEVICE).get(0).getOutputPort());
         assertEquals("Incorrect Output CP", MULTICAST_OUT_CP,
-                traceSuccess.getGroupOuputs(MULTICAST_GROUP_FLOW_DEVICE).get(1).getOutput());
-
+                traceSuccess.getHitChains(MULTICAST_GROUP_FLOW_DEVICE).get(1).getOutputPort());
     }
 
     /**
      * Tests dual homing of a host.
      */
     @Test
-    public void dualhomedTest() throws Exception {
+    public void dualhomedTest() {
+        // Test success
         StaticPacketTrace traceSuccess = mngr.trace(PACKET_DUAL_HOME, DUAL_HOME_CP_1_1);
+        log.info("trace {}", traceSuccess);
+        log.info("trace {}", traceSuccess.resultMessage());
 
+        // Verify paths
         assertNotNull("trace should not be null", traceSuccess);
-        assertTrue("Should have 2 output paths", traceSuccess.getCompletePaths().size() == 2);
+        assertEquals("Should have 2 output paths", 2, traceSuccess.getCompletePaths().size());
         assertTrue("Should contain proper path", traceSuccess.getCompletePaths()
                 .contains(ImmutableList.of(DUAL_HOME_CP_1_1, DUAL_HOME_CP_1_2, DUAL_HOME_CP_2_1, DUAL_HOME_CP_2_2)));
         assertTrue("Should contain proper path", traceSuccess.getCompletePaths()
@@ -407,37 +383,36 @@
 
     }
 
-
-    private StaticPacketTrace testSuccess(TrafficSelector packet, ConnectPoint in, DeviceId deviceId, ConnectPoint out,
-                                          int paths, int outputs) {
+    private StaticPacketTrace testSuccess(TrafficSelector packet, ConnectPoint in, DeviceId deviceId,
+                                          ConnectPoint out, int paths, int hitchains) {
         StaticPacketTrace traceSuccess = mngr.trace(packet, in);
-
         log.info("trace {}", traceSuccess);
-
         log.info("trace {}", traceSuccess.resultMessage());
 
         assertNotNull("trace should not be null", traceSuccess);
-        assertEquals("Trace should have " + outputs + " output", outputs,
-                traceSuccess.getGroupOuputs(deviceId).size());
+        assertEquals("Trace should have " + hitchains + " hitchains", hitchains,
+                traceSuccess.getHitChains(deviceId).size());
         assertEquals("Trace should only have " + paths + "output", paths, traceSuccess.getCompletePaths().size());
         assertTrue("Trace should be successful",
                 traceSuccess.resultMessage().contains("Reached required destination Host"));
         assertEquals("Incorrect Output CP", out,
-                traceSuccess.getGroupOuputs(deviceId).get(0).getOutput());
+                traceSuccess.getHitChains(deviceId).get(0).getOutputPort());
 
         return traceSuccess;
     }
 
-    private void testFailure(TrafficSelector packet, ConnectPoint in, DeviceId deviceId) {
+    private void testFailure(TrafficSelector packet, ConnectPoint in, DeviceId deviceId,
+                             int hitchains) {
         StaticPacketTrace traceFail = mngr.trace(packet, in);
-
         log.info("trace {}", traceFail.resultMessage());
 
         assertNotNull("Trace should not be null", traceFail);
-        assertNull("Trace should have 0 output", traceFail.getGroupOuputs(deviceId));
+        assertEquals("Trace should have " + hitchains + " hitchains", hitchains,
+                traceFail.getHitChains(deviceId).size());
     }
 
-    private class TestFlowRuleService extends FlowNib {
+    private static class TestFlowRuleService extends FlowNib {
+
         @Override
         public Iterable<FlowEntry> getFlowEntriesByState(DeviceId deviceId, FlowEntry.FlowEntryState state) {
             if (deviceId.equals(SINGLE_FLOW_DEVICE)) {
@@ -453,23 +428,18 @@
                 return ImmutableList.of(TOPO_SINGLE_FLOW_ENTRY, TOPO_SECOND_INPUT_FLOW_ENTRY);
             } else if (deviceId.equals(TOPO_GROUP_FLOW_DEVICE)) {
                 return ImmutableList.of(TOPO_GROUP_FLOW_ENTRY);
-            } else if (deviceId.equals(HARDWARE_DEVICE)) {
-                return ImmutableList.of(HARDWARE_ETH_FLOW_ENTRY, HARDWARE_FLOW_ENTRY);
             } else if (deviceId.equals(SAME_OUTPUT_FLOW_DEVICE)) {
                 return ImmutableList.of(SAME_OUTPUT_FLOW_ENTRY);
             } else if (deviceId.equals(ARP_FLOW_DEVICE)) {
-                return ImmutableList.of(ARP_FLOW_ENTRY);
+                    return ImmutableList.of(ARP_FLOW_ENTRY);
+            } else if (deviceId.equals(ARP_FLOW_VLAN_DEVICE)) {
+                return ImmutableList.of(ARP_FLOW_VLAN_ENTRY, ARP_FLOW_ENTRY);
             } else if (deviceId.equals(DUAL_LINK_1)) {
                 return ImmutableList.of(DUAL_LINK_1_GROUP_FLOW_ENTRY);
             } else if (deviceId.equals(DUAL_LINK_2)) {
                 return ImmutableList.of(DUAL_LINK_1_GROUP_FLOW_ENTRY, DUAL_LINK_2_GROUP_FLOW_ENTRY);
             } else if (deviceId.equals(DUAL_LINK_3)) {
                 return ImmutableList.of(DUAL_LINK_3_FLOW_ENTRY, DUAL_LINK_3_FLOW_ENTRY_2);
-            } else if (deviceId.equals(DEFERRED_1)) {
-                return ImmutableList.of(DEFERRED_FLOW_ENTRY, DEFERRED_CLEAR_FLOW_ENTRY);
-            } else if (deviceId.equals(HARDWARE_DEVICE_10)) {
-                return ImmutableList.of(HARDWARE_10_FLOW_ENTRY, HARDWARE_10_SECOND_FLOW_ENTRY,
-                        HARDWARE_10_OUTPUT_FLOW_ENTRY);
             } else if (deviceId.equals(LLDP_FLOW_DEVICE)) {
                 return ImmutableList.of(LLDP_FLOW_ENTRY);
             } else if (deviceId.equals(MULTICAST_GROUP_FLOW_DEVICE)) {
@@ -480,16 +450,15 @@
                 return ImmutableList.of(DUAL_HOME_FLOW_ENTRY);
             } else if (deviceId.equals(DUAL_HOME_DEVICE_2) || deviceId.equals(DUAL_HOME_DEVICE_3)) {
                 return ImmutableList.of(DUAL_HOME_OUT_FLOW_ENTRY);
-            } else if (deviceId.equals(ACTION_ORDER_DEVICE)) {
-                return ImmutableList.of(ACTION_ORDER_FLOW_ENTRY);
             }
             return ImmutableList.of();
         }
     }
 
-    private class TestGroupService extends GroupNib {
+    private static class TestGroupService extends GroupNib {
+
         @Override
-        public Iterable<Group> getGroups(DeviceId deviceId) {
+        public Iterable<Group> getGroupsByState(DeviceId deviceId, Group.GroupState groupState) {
             if (deviceId.equals(GROUP_FLOW_DEVICE)) {
                 return ImmutableList.of(GROUP);
             } else if (deviceId.equals(TOPO_GROUP_FLOW_DEVICE)) {
@@ -502,14 +471,12 @@
                 return ImmutableList.of(NO_BUCKET_GROUP);
             } else if (deviceId.equals(DUAL_HOME_DEVICE_1)) {
                 return ImmutableList.of(DUAL_HOME_GROUP);
-            } else if (deviceId.equals(ACTION_ORDER_DEVICE)) {
-                return ImmutableList.of(ACTION_ORDER_GROUP);
             }
             return ImmutableList.of();
         }
     }
 
-    private class TestHostService extends HostNib {
+    private static class TestHostService extends HostNib {
         @Override
         public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
             if (connectPoint.equals(TOPO_FLOW_3_OUT_CP)) {
@@ -521,11 +488,7 @@
             if (connectPoint.equals(SINGLE_FLOW_OUT_CP) ||
                     connectPoint.equals(DUAL_FLOW_OUT_CP) ||
                     connectPoint.equals(GROUP_FLOW_OUT_CP) ||
-                    connectPoint.equals(HARDWARE_DEVICE_OUT_CP) ||
-                    connectPoint.equals(HARDWARE_DEVICE_10_OUT_CP) ||
-                    connectPoint.equals(DEFERRED_CP_2_OUT) ||
-                    connectPoint.equals(DUAL_LINK_3_CP_3_OUT) ||
-                    connectPoint.equals(ACTION_ORDER_OUT_CP)) {
+                    connectPoint.equals(DUAL_LINK_3_CP_3_OUT)) {
                 return ImmutableSet.of(H1);
             }
             if (connectPoint.equals(DUAL_HOME_CP_2_2) || connectPoint.equals(DUAL_HOME_CP_3_2)) {
@@ -559,7 +522,7 @@
         }
     }
 
-    private class TestLinkService extends LinkNib {
+    private static class TestLinkService extends LinkNib {
         @Override
         public Set<Link> getEgressLinks(ConnectPoint connectPoint) {
             if (connectPoint.equals(TOPO_FLOW_1_OUT_CP)
@@ -638,15 +601,19 @@
         }
     }
 
-    private class TestDeviceService extends DeviceNib {
+    private static class TestDeviceService extends DeviceNib {
         @Override
         public Device getDevice(DeviceId deviceId) {
             if (deviceId.equals(DeviceId.deviceId("nonexistent"))) {
                 return null;
             }
-            return new DefaultDevice(ProviderId.NONE, DeviceId.deviceId("test"), SWITCH,
-                    "test", "test", "test", "test", new ChassisId(),
-                    DefaultAnnotations.builder().set("foo", "bar").build());
+            SparseAnnotations annotations = DefaultAnnotations.builder()
+                    .set("foo", "bar")
+                    .set(AnnotationKeys.DRIVER, OFDPA_DRIVER)
+                    .build();
+            return new DefaultDevice(ProviderId.NONE, deviceId, SWITCH,
+                    MANUFACTURER, HW_VERSION, SW_VERSION, SERIAL_NUMBER, new ChassisId(),
+                    annotations);
         }
 
         @Override
@@ -660,24 +627,21 @@
         }
     }
 
-    private class TestDriverService extends DriverNib {
+    private static class TestDriverNib extends DriverNib {
         @Override
         public String getDriverName(DeviceId deviceId) {
-            if (deviceId.equals(HARDWARE_DEVICE) || deviceId.equals(HARDWARE_DEVICE_10)) {
-                return "ofdpa";
-            }
             return "NotHWDriver";
         }
     }
 
-    private class TestMastershipService extends MastershipNib {
+    private static class TestMastershipService extends MastershipNib {
         @Override
         public NodeId getMasterFor(DeviceId deviceId) {
             return NodeId.nodeId(MASTER_1);
         }
     }
 
-    private class TestEdgePortService extends EdgePortNib {
+    private static class TestEdgePortService extends EdgePortNib {
         @Override
         public boolean isEdgePoint(ConnectPoint point) {
             return point.equals(MULTICAST_OUT_CP) ||
@@ -685,11 +649,58 @@
         }
     }
 
-    private class TestRouteService extends RouteNib {
+    private static class TestRouteService extends RouteNib {
         @Override
         public Optional<ResolvedRoute> longestPrefixLookup(IpAddress ip) {
             return Optional.empty();
         }
     }
 
+    private class TestDriverService extends DriverServiceAdapter {
+        @Override
+        public Driver getDriver(DeviceId deviceId) {
+            return baseDriver;
+        }
+    }
+
+    private static class TestDriver extends DriverAdapter {
+
+        @Override
+        public String manufacturer() {
+            return MANUFACTURER;
+        }
+
+        @Override
+        public String hwVersion() {
+            return HW_VERSION;
+        }
+
+        @Override
+        public String swVersion() {
+            return SW_VERSION;
+        }
+
+        @Override
+        public String name() {
+            return OFDPA_DRIVER;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T extends Behaviour> T createBehaviour(DriverHandler handler, Class<T> behaviourClass) {
+            if (behaviourClass == PipelineTraceable.class) {
+                T behaviour = (T) new OfdpaPipelineTraceable();
+                behaviour.setData(handler.data());
+                ((HandlerBehaviour) behaviour).setHandler(handler);
+                return behaviour;
+            } else {
+                T behaviour = (T) new Ofdpa2Pipeline();
+                behaviour.setData(handler.data());
+                ((HandlerBehaviour) behaviour).setHandler(handler);
+                return behaviour;
+            }
+        }
+
+    }
+
 }
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/t3/impl/TroubleshootUtilsTest.java b/app/src/test/java/org/onosproject/t3/impl/TroubleshootUtilsTest.java
deleted file mode 100644
index c8195c0..0000000
--- a/app/src/test/java/org/onosproject/t3/impl/TroubleshootUtilsTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2018-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.t3.impl;
-
-import org.junit.Test;
-import org.onlab.packet.MacAddress;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * Test for util methods of the Trellis Troubleshoot Toolkit.
- */
-public class TroubleshootUtilsTest {
-
-    @Test
-    public void testMacMatch() {
-
-        MacAddress min = MacAddress.valueOf("01:00:5E:00:00:00");
-        MacAddress mask = MacAddress.valueOf("FF:FF:FF:80:00:00");
-        MacAddress macOk = MacAddress.valueOf("01:00:5E:00:00:01");
-
-        assertTrue("False on correct match", TroubleshootUtils.compareMac(macOk, min, mask));
-
-        MacAddress macWrong = MacAddress.valueOf("01:00:5E:80:00:00");
-
-        assertFalse("True on false match", TroubleshootUtils.compareMac(macWrong, min, mask));
-
-        MacAddress maskEmpty = MacAddress.valueOf("00:00:00:00:00:00");
-
-        assertTrue("False on empty Mask", TroubleshootUtils.compareMac(macOk, min, maskEmpty));
-
-        MacAddress maskFull = MacAddress.valueOf("FF:FF:FF:FF:FF:FF");
-
-        assertFalse("True on full Mask", TroubleshootUtils.compareMac(macOk, min, maskFull));
-
-    }
-}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8a059c7..b20425c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.onosproject</groupId>
         <artifactId>onos-dependencies</artifactId>
-        <version>2.2.4</version>
+        <version>2.2.7-b2</version>
     </parent>
 
     <groupId>org.onosproject</groupId>