T3: Implementation of host based, less arguments CLI command

Change-Id: Ie02918b90968632ca56b9588adc6bda881c0c0a7
(cherry picked from commit d711b8068685ab602ac869ee4907dd054c49046f)
diff --git a/apps/t3/BUCK b/apps/t3/BUCK
index a21999d..8841b3f 100644
--- a/apps/t3/BUCK
+++ b/apps/t3/BUCK
@@ -7,6 +7,7 @@
     '//lib:org.apache.karaf.shell.console',
     '//cli:onos-cli',
     '//drivers/default:onos-drivers-default',
+    '//apps/segmentrouting:onos-apps-segmentrouting',
 ]
 
 TEST_DEPS = [
diff --git a/apps/t3/src/main/java/org/onosproject/t3/api/TroubleshootService.java b/apps/t3/src/main/java/org/onosproject/t3/api/TroubleshootService.java
index 9b17382..01bae57 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/api/TroubleshootService.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/api/TroubleshootService.java
@@ -16,7 +16,9 @@
 
 package org.onosproject.t3.api;
 
+import org.onlab.packet.EthType;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.HostId;
 import org.onosproject.net.flow.TrafficSelector;
 
 /**
@@ -25,13 +27,22 @@
  */
 public interface TroubleshootService {
 
+    /**
+     * Requests a static trace be performed between the two hosts in the network, given a type of traffic.
+     *
+     * @param sourceHost      source host
+     * @param destinationHost destination host
+     * @param type            the etherType of the traffic we want to trace.
+     * @return a trace result
+     */
+    StaticPacketTrace trace(HostId sourceHost, HostId destinationHost, EthType.EtherType type);
 
     /**
      * Requests a static trace be performed for the given traffic selector
      * starting at the given connect point.
      *
      * @param packet description of packet
-     * @param in point at which packet starts
+     * @param in     point at which packet starts
      * @return a trace result
      */
     StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in);
diff --git a/apps/t3/src/main/java/org/onosproject/t3/cli/T3CliUtils.java b/apps/t3/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
new file mode 100644
index 0000000..e2375a7
--- /dev/null
+++ b/apps/t3/src/main/java/org/onosproject/t3/cli/T3CliUtils.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2015-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.cli;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.t3.api.GroupsInDevice;
+import org.onosproject.t3.api.StaticPacketTrace;
+
+import java.util.List;
+
+/**
+ * Class containing utility methods for T3 cli.
+ */
+final class T3CliUtils {
+
+    private T3CliUtils() {
+        //banning construction
+    }
+
+    private static final String FLOW_SHORT_FORMAT = "    %s, bytes=%s, packets=%s, "
+            + "table=%s, priority=%s, selector=%s, treatment=%s";
+
+    private static final String GROUP_FORMAT =
+            "   id=0x%s, state=%s, type=%s, bytes=%s, packets=%s, appId=%s, referenceCount=%s";
+    private static final String GROUP_BUCKET_FORMAT =
+            "       id=0x%s, bucket=%s, bytes=%s, packets=%s, actions=%s";
+
+    /**
+     * Builds a string output for the given trace for a specific level of verbosity.
+     *
+     * @param trace      the trace
+     * @param verbosity1 middle verbosity level
+     * @param verbosity2 high verbosity level
+     * @return a string representing the trace.
+     */
+    static String printTrace(StaticPacketTrace trace, boolean verbosity1, boolean verbosity2) {
+        StringBuilder tracePrint = new StringBuilder();
+        //Print based on verbosity
+        if (verbosity1) {
+            tracePrint = printTrace(trace, false, tracePrint);
+        } else if (verbosity2) {
+            tracePrint = printTrace(trace, true, tracePrint);
+        } else {
+            tracePrint.append("Paths");
+            tracePrint.append("\n");
+            List<List<ConnectPoint>> paths = trace.getCompletePaths();
+            for (List<ConnectPoint> path : paths) {
+                tracePrint.append(path);
+                tracePrint.append("\n");
+            }
+        }
+        tracePrint.append("Result: \n" + trace.resultMessage());
+        return tracePrint.toString();
+    }
+
+    //prints the trace
+    private static StringBuilder printTrace(StaticPacketTrace trace, boolean verbose, StringBuilder tracePrint) {
+        List<List<ConnectPoint>> paths = trace.getCompletePaths();
+        for (List<ConnectPoint> path : paths) {
+            tracePrint.append("Path " + path);
+            tracePrint.append("\n");
+            ConnectPoint previous = null;
+            if (path.size() == 1) {
+                ConnectPoint connectPoint = path.get(0);
+                tracePrint.append("Device " + connectPoint.deviceId());
+                tracePrint.append("\n");
+                tracePrint.append("Input from " + connectPoint);
+                tracePrint.append("\n");
+                tracePrint = printFlows(trace, verbose, connectPoint, tracePrint);
+                tracePrint = printGroups(trace, verbose, connectPoint, tracePrint);
+                tracePrint.append("Output through " + connectPoint);
+                tracePrint.append("\n");
+            } else {
+                for (ConnectPoint connectPoint : path) {
+                    if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
+                        tracePrint.append("Device " + connectPoint.deviceId());
+                        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");
+                    }
+                    previous = connectPoint;
+                }
+            }
+            tracePrint.append("---------------------------------------------------------------\n");
+        }
+        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("\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");
+            }
+        });
+        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");
+                }
+            });
+        }
+        return tracePrint;
+    }
+
+    private static String printTreatment(TrafficTreatment treatment) {
+        final String delimiter = ", ";
+        StringBuilder builder = new StringBuilder("[");
+        if (!treatment.immediate().isEmpty()) {
+            builder.append("immediate=" + treatment.immediate() + delimiter);
+        }
+        if (!treatment.deferred().isEmpty()) {
+            builder.append("deferred=" + treatment.deferred() + delimiter);
+        }
+        if (treatment.clearedDeferred()) {
+            builder.append("clearDeferred" + delimiter);
+        }
+        if (treatment.tableTransition() != null) {
+            builder.append("transition=" + treatment.tableTransition() + delimiter);
+        }
+        if (treatment.metered() != null) {
+            builder.append("meter=" + treatment.metered() + delimiter);
+        }
+        if (treatment.writeMetadata() != null) {
+            builder.append("metadata=" + treatment.writeMetadata() + delimiter);
+        }
+        // Chop off last delimiter
+        builder.replace(builder.length() - delimiter.length(), builder.length(), "");
+        builder.append("]");
+        return builder.toString();
+    }
+}
diff --git a/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
new file mode 100644
index 0000000..8c4bd45
--- /dev/null
+++ b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
@@ -0,0 +1,76 @@
+/*
+ * 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.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.HostIdCompleter;
+import org.onosproject.net.HostId;
+import org.onosproject.t3.api.StaticPacketTrace;
+import org.onosproject.t3.api.TroubleshootService;
+
+import static org.onlab.packet.EthType.EtherType;
+
+/**
+ * Starts a Static Packet Trace for a given input and prints the result.
+ */
+@Command(scope = "onos", name = "troubleshoot-simple",
+        description = "Given two hosts troubleshoots flows and groups between them, in case of segment routing")
+public class TroubleshootSimpleTraceCommand extends AbstractShellCommand {
+
+    // OSGi workaround to introduce package dependency
+    HostIdCompleter completer;
+    @Argument(index = 0, name = "one", description = "One host ID",
+            required = true, multiValued = false)
+    String srcHost = null;
+
+    @Argument(index = 1, name = "two", description = "Another host ID",
+            required = true, multiValued = false)
+    String dstHost = null;
+
+    @Option(name = "-v", aliases = "--verbose", description = "Outputs complete path")
+    private boolean verbosity1 = false;
+
+    @Option(name = "-vv", aliases = "--veryverbose", description = "Outputs flows and groups for every device")
+    private boolean verbosity2 = false;
+
+    @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
+    String ethType = "ipv4";
+
+    @Override
+    protected void execute() {
+        TroubleshootService service = get(TroubleshootService.class);
+
+        EtherType type = EtherType.valueOf(ethType.toUpperCase());
+
+        //Printing the created packet
+        print("Tracing between: %s and %s", srcHost, dstHost);
+
+        //Build the trace
+        StaticPacketTrace trace = service.trace(HostId.hostId(srcHost), HostId.hostId(dstHost), type);
+        if (trace.getInitialPacket() != null) {
+            print("%s", T3CliUtils.printTrace(trace, verbosity1, verbosity2));
+        } else {
+            print("Cannot obtain trace between %s and %s", srcHost, dstHost);
+            print("Reason: %s", trace.resultMessage());
+        }
+
+
+    }
+}
diff --git a/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
index ae68d93..755eaaa 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015-present Open Networking Foundation
+ * 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.
@@ -30,14 +30,9 @@
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.group.GroupBucket;
-import org.onosproject.t3.api.GroupsInDevice;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.onosproject.t3.api.TroubleshootService;
 
-import java.util.List;
-
 import static org.onlab.packet.EthType.EtherType;
 
 /**
@@ -191,116 +186,8 @@
 
         //Build the trace
         StaticPacketTrace trace = service.trace(packet, cp);
-        //Print based on verbosity
-        if (verbosity1) {
-            printTrace(trace, false);
-        } else if (verbosity2) {
-            printTrace(trace, true);
-        } else {
-            print("Paths");
-            List<List<ConnectPoint>> paths = trace.getCompletePaths();
-            paths.forEach(path -> print("%s", path));
-        }
-        print("Result: \n%s", trace.resultMessage());
-    }
 
-    //prints the trace
-    private void printTrace(StaticPacketTrace trace, boolean verbose) {
-        List<List<ConnectPoint>> paths = trace.getCompletePaths();
-        paths.forEach(path -> {
-            print("Path %s", path);
-            ConnectPoint previous = null;
-            if (path.size() == 1) {
-                ConnectPoint connectPoint = path.get(0);
-                print("Device %s", connectPoint.deviceId());
-                print("Input from %s", connectPoint);
-                printFlows(trace, verbose, connectPoint);
-                printGroups(trace, verbose, connectPoint);
-                print("Output through %s", connectPoint);
-                print("");
-            } else {
-                for (ConnectPoint connectPoint : path) {
-                    if (previous == null || !previous.deviceId().equals(connectPoint.deviceId())) {
-                        print("Device %s", connectPoint.deviceId());
-                        print("Input from %s", connectPoint);
-                        printFlows(trace, verbose, connectPoint);
-                    } else {
-                        printGroups(trace, verbose, connectPoint);
-                        print("Output through %s", connectPoint);
-                        print("");
-                    }
-                    previous = connectPoint;
-                }
-            }
-            print("---------------------------------------------------------------\n");
-        });
-    }
+        print("%s", T3CliUtils.printTrace(trace, verbosity1, verbosity2));
 
-    //Prints the flows for a given trace and a specified level of verbosity
-    private void printFlows(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
-        print("Flows");
-        trace.getFlowsForDevice(connectPoint.deviceId()).forEach(f -> {
-            if (verbose) {
-                print(FLOW_SHORT_FORMAT, f.state(), f.bytes(), f.packets(),
-                        f.table(), f.priority(), f.selector().criteria(),
-                        printTreatment(f.treatment()));
-            } else {
-                print("   flowId=%s, table=%s, selector=%s", f.id(), f.table(), f.selector().criteria());
-            }
-        });
-    }
-
-    //Prints the groups for a given trace and a specified level of verbosity
-    private void printGroups(StaticPacketTrace trace, boolean verbose, ConnectPoint connectPoint) {
-        List<GroupsInDevice> groupsInDevice = trace.getGroupOuputs(connectPoint.deviceId());
-        if (groupsInDevice != null) {
-            print("Groups");
-            groupsInDevice.forEach(output -> {
-                if (output.getOutput().equals(connectPoint)) {
-                    output.getGroups().forEach(group -> {
-                        if (verbose) {
-                            print(GROUP_FORMAT, Integer.toHexString(group.id().id()), group.state(), group.type(),
-                                    group.bytes(), group.packets(), group.appId().name(), group.referenceCount());
-                            int i = 0;
-                            for (GroupBucket bucket : group.buckets().buckets()) {
-                                print(GROUP_BUCKET_FORMAT, Integer.toHexString(group.id().id()), ++i,
-                                        bucket.bytes(), bucket.packets(),
-                                        bucket.treatment().allInstructions());
-                            }
-                        } else {
-                            print("   groupId=%s", group.id());
-                        }
-                    });
-                    print("Outgoing Packet %s", output.getFinalPacket());
-                }
-            });
-        }
-    }
-
-    private String printTreatment(TrafficTreatment treatment) {
-        final String delimiter = ", ";
-        StringBuilder builder = new StringBuilder("[");
-        if (!treatment.immediate().isEmpty()) {
-            builder.append("immediate=" + treatment.immediate() + delimiter);
-        }
-        if (!treatment.deferred().isEmpty()) {
-            builder.append("deferred=" + treatment.deferred() + delimiter);
-        }
-        if (treatment.clearedDeferred()) {
-            builder.append("clearDeferred" + delimiter);
-        }
-        if (treatment.tableTransition() != null) {
-            builder.append("transition=" + treatment.tableTransition() + delimiter);
-        }
-        if (treatment.metered() != null) {
-            builder.append("meter=" + treatment.metered() + delimiter);
-        }
-        if (treatment.writeMetadata() != null) {
-            builder.append("metadata=" + treatment.writeMetadata() + delimiter);
-        }
-        // Chop off last delimiter
-        builder.replace(builder.length() - delimiter.length(), builder.length(), "");
-        builder.append("]");
-        return builder.toString();
     }
 }
diff --git a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index a1b1d68..135a74d 100644
--- a/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/apps/t3/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -23,15 +23,20 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.cluster.NodeId;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
 import org.onosproject.net.Link;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.ConfigException;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.InterfaceConfig;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -55,7 +60,10 @@
 import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupService;
 import org.onosproject.net.host.HostService;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.intf.Interface;
 import org.onosproject.net.link.LinkService;
+import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
 import org.onosproject.t3.api.GroupsInDevice;
 import org.onosproject.t3.api.StaticPacketTrace;
 import org.onosproject.t3.api.TroubleshootService;
@@ -114,6 +122,158 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected MastershipService mastershipService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigService networkConfigService;
+
+    @Override
+    public StaticPacketTrace trace(HostId sourceHost, HostId destinationHost, EtherType etherType) {
+        Host source = hostService.getHost(sourceHost);
+        Host destination = hostService.getHost(destinationHost);
+
+        //Temporary trace to fail in case we don't have neough information or what is provided is incoherent
+        StaticPacketTrace failTrace = new StaticPacketTrace(null, null);
+
+        if (source == null) {
+            failTrace.addResultMessage("Source Host " + sourceHost + " does not exist");
+            return failTrace;
+        }
+
+        if (destination == null) {
+            failTrace.addResultMessage("Destination Host " + destinationHost + " does not exist");
+            return failTrace;
+        }
+
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
+                .matchInPort(source.location().port())
+                .matchEthType(etherType.ethType().toShort())
+                .matchEthDst(source.mac())
+                .matchVlanId(source.vlan());
+
+
+
+        try {
+            //if the location deviceId is the same, the two hosts are under same subnet and vlan on the interface
+            // we are under same leaf so it's L2 Unicast.
+            if (areBridged(source, destination)) {
+                selectorBuilder.matchEthDst(destination.mac());
+                return trace(selectorBuilder.build(), source.location());
+            }
+
+            //handle the IPs for src and dst in case of L3
+            if (etherType.equals(EtherType.IPV4) || etherType.equals(EtherType.IPV6)) {
+
+                //Match on the source IP
+                if (!matchIP(source, failTrace, selectorBuilder, etherType, true)) {
+                    return failTrace;
+                }
+
+                //Match on destination IP
+                if (!matchIP(destination, failTrace, selectorBuilder, etherType, false)) {
+                    return failTrace;
+                }
+
+            } else {
+                failTrace.addResultMessage("Host based trace supports only IPv4 or IPv6 as EtherType, " +
+                        "please use packet based");
+                return failTrace;
+            }
+
+            //l3 unicast, we get the dst mac of the leaf the source is connected to from netcfg
+            SegmentRoutingDeviceConfig segmentRoutingConfig = networkConfigService.getConfig(source.location()
+                    .deviceId(), SegmentRoutingDeviceConfig.class);
+            if (segmentRoutingConfig != null) {
+                selectorBuilder.matchEthDst(segmentRoutingConfig.routerMac());
+            } else {
+                failTrace.addResultMessage("Can't get " + source.location().deviceId() +
+                        " router MAC from segment routing config can't perform L3 tracing.");
+            }
+
+            return trace(selectorBuilder.build(), source.location());
+
+        } catch (ConfigException e) {
+            failTrace.addResultMessage("Can't get config " + e.getMessage());
+            return failTrace;
+        }
+    }
+
+    /**
+     * Matches src and dst IPs based on host information.
+     *
+     * @param host            the host
+     * @param failTrace       the trace to use in case of failure
+     * @param selectorBuilder the packet we are building to trace
+     * @param etherType       the traffic type
+     * @param src             is this src host or dst host
+     * @return true if properly matched
+     */
+    private boolean matchIP(Host host, StaticPacketTrace failTrace, Builder selectorBuilder,
+                            EtherType etherType, boolean src) {
+        List<IpAddress> ips = host.ipAddresses().stream().filter(ipAddress -> {
+            if (etherType.equals(EtherType.IPV4)) {
+                return ipAddress.isIp4() && !ipAddress.isLinkLocal();
+            } else if (etherType.equals(EtherType.IPV6)) {
+                return ipAddress.isIp6() && !ipAddress.isLinkLocal();
+            }
+            return false;
+        }).collect(Collectors.toList());
+
+        if (ips.size() > 0) {
+            if (src) {
+                selectorBuilder.matchIPSrc(ips.get(0).toIpPrefix());
+            } else {
+                selectorBuilder.matchIPDst(ips.get(0).toIpPrefix());
+            }
+        } else {
+            failTrace.addResultMessage("Host " + host + " has no " + etherType + " address");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks that two hosts are bridged (L2Unicast).
+     *
+     * @param source      the source host
+     * @param destination the destination host
+     * @return true if bridged.
+     * @throws ConfigException if config can't be properly retrieved
+     */
+    private boolean areBridged(Host source, Host destination) throws ConfigException {
+
+        //If the location is not the same we don't even check vlan or subnets
+        if (!source.location().deviceId().equals(destination.location().deviceId())) {
+            return false;
+        }
+
+        InterfaceConfig interfaceCfgH1 = networkConfigService.getConfig(source.location(), InterfaceConfig.class);
+        InterfaceConfig interfaceCfgH2 = networkConfigService.getConfig(destination.location(), InterfaceConfig.class);
+        if (interfaceCfgH1 != null && interfaceCfgH2 != null) {
+
+            //following can be optimized but for clarity is left as is
+            Interface intfH1 = interfaceCfgH1.getInterfaces().stream().findFirst().get();
+            Interface intfH2 = interfaceCfgH2.getInterfaces().stream().findFirst().get();
+
+            if (!intfH1.vlanNative().equals(intfH2.vlanNative())) {
+                return false;
+            }
+
+            if (!intfH1.vlanTagged().equals(intfH2.vlanTagged())) {
+                return false;
+            }
+
+            if (!intfH1.vlanUntagged().equals(intfH2.vlanUntagged())) {
+                return false;
+            }
+
+            List<InterfaceIpAddress> intersection = new ArrayList<>(intfH1.ipAddressesList());
+            intersection.retainAll(intfH2.ipAddressesList());
+            if (intersection.size() == 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     @Override
     public StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in) {
         log.info("Tracing packet {} coming in through {}", packet, in);
diff --git a/apps/t3/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/t3/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 7da87bc..7444994 100644
--- a/apps/t3/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/t3/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -19,8 +19,20 @@
         <command>
             <action class="org.onosproject.t3.cli.TroubleshootTraceCommand"/>
         </command>
+        <command>
+            <action class="org.onosproject.t3.cli.TroubleshootSimpleTraceCommand"/>
+            <completers>
+                <ref component-id="hostIdCompleter"/>
+            </completers>
+            <optional-completers>
+                <entry key="-et" value-ref="ethTypeCompleter"/>
+            </optional-completers>
+        </command>
     </command-bundle>
 
+    <bean id="hostIdCompleter" class="org.onosproject.cli.net.HostIdCompleter"/>
+    <bean id="ethTypeCompleter" class="org.onosproject.cli.net.EthTypeCompleter"/>
+
 </blueprint>