[CORD-2432][CORD-2431] Initial t3 implementation

Change-Id: I1ed421f82c234bb006ed2cefefea53d773f1efc9
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
new file mode 100644
index 0000000..a42872a
--- /dev/null
+++ b/apps/t3/src/main/java/org/onosproject/t3/cli/TroubleshootTraceCommand.java
@@ -0,0 +1,235 @@
+/*
+ * 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.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.EthType;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+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.StaticPacketTrace;
+import org.onosproject.t3.api.TroubleshootService;
+
+import java.util.List;
+
+/**
+ * Starts a Static Packet Trace for a given input and prints the result.
+ */
+@Command(scope = "onos", name = "troubleshoot",
+        description = "troubleshoots flows and groups between source and destination")
+public class TroubleshootTraceCommand extends AbstractShellCommand {
+
+
+    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";
+
+    @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 = "-s", aliases = "--srcIp", description = "Source IP")
+    String srcIp = null;
+
+    @Option(name = "-sp", aliases = "--srcPort", description = "Source Port", required = true)
+    String srcPort = null;
+
+    @Option(name = "-sm", aliases = "--srcMac", description = "Source MAC")
+    String srcMac = null;
+
+    @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
+    String ethType = "ipv4";
+
+    @Option(name = "-stp", aliases = "--srcTcpPort", description = "Source TCP Port")
+    String srcTcpPort = null;
+
+    @Option(name = "-d", aliases = "--dstIp", description = "Destination IP", valueToShowInHelp = "255.255.255.255")
+    String dstIp = "255.255.255.255";
+
+    @Option(name = "-dm", aliases = "--dstMac", description = "Destination MAC")
+    String dstMac = null;
+
+    @Option(name = "-dtp", aliases = "dstTcpPort", description = "destination TCP Port")
+    String dstTcpPort = null;
+
+    @Option(name = "-vid", aliases = "--vlanId", description = "Vlan of incoming packet", valueToShowInHelp = "None")
+    String vlan = "None";
+
+    @Option(name = "-mb", aliases = "--mplsBos", description = "MPLS BOS", valueToShowInHelp = "True")
+    String mplsBos = "true";
+
+    @Override
+    protected void execute() {
+        TroubleshootService service = get(TroubleshootService.class);
+        ConnectPoint cp = ConnectPoint.deviceConnectPoint(srcPort);
+
+        //Input Port must be specified
+        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
+                .matchInPort(cp.port());
+
+        if (srcIp != null) {
+            selectorBuilder.matchIPSrc(IpAddress.valueOf(srcIp).toIpPrefix());
+        }
+
+        if (srcMac != null) {
+            selectorBuilder.matchEthSrc(MacAddress.valueOf(srcMac));
+        }
+
+        //if EthType option is not specified using IPv4
+        selectorBuilder.matchEthType(EthType.EtherType.valueOf(ethType.toUpperCase()).ethType().toShort());
+
+        if (srcTcpPort != null) {
+            selectorBuilder.matchTcpSrc(TpPort.tpPort(Integer.parseInt(srcTcpPort)));
+        }
+
+        //if destination Ip option is not specified using broadcast 255.255.255.255
+        selectorBuilder.matchIPDst(IpAddress.valueOf(dstIp).toIpPrefix());
+
+        if (dstMac != null) {
+            selectorBuilder.matchEthDst(MacAddress.valueOf(dstMac));
+        }
+        if (dstTcpPort != null) {
+            selectorBuilder.matchTcpDst(TpPort.tpPort(Integer.parseInt(dstTcpPort)));
+        }
+
+        //if vlan option is not specified using NONE
+        selectorBuilder.matchVlanId(VlanId.vlanId(vlan));
+
+        //if mplsBos option is not specified using True
+        selectorBuilder.matchMplsBos(Boolean.valueOf(mplsBos));
+
+        TrafficSelector packet = selectorBuilder.build();
+
+        //Printing the created packet
+        print("Tracing packet: %s", packet.criteria());
+
+        //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;
+            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;
+            }
+        });
+    }
+
+    //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, selector=%s ", f.id(), 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) {
+        print("Groups");
+        trace.getGroupOuputs(connectPoint.deviceId()).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();
+    }
+}