T3 REST API support for multicast routes

Change-Id: I511fcd0afcbb116a8d647f8dcf0276371687f6b9
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 5b19cc2..a21baf5 100644
--- a/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
+++ b/app/src/main/java/org/onosproject/t3/api/TroubleshootService.java
@@ -75,4 +75,12 @@
      * @return a trace result
      */
     StaticPacketTrace trace(TrafficSelector packet, ConnectPoint in);
+
+    /**
+     * Requests list of static trace to be performed for all mcast routes in the network.
+     *
+     * @param vlanId the vlan id configured for multicast
+     * @return a list of trace result
+     */
+    List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId);
 }
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 3426517..018f6ac 100644
--- a/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/app/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -86,6 +86,7 @@
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
 
 import static org.onlab.packet.EthType.EtherType;
 import static org.onosproject.net.flow.TrafficSelector.Builder;
@@ -390,6 +391,14 @@
         return trace;
     }
 
+    @Override
+    public List<Set<StaticPacketTrace>> getMulitcastTrace(VlanId vlanId) {
+        Generator<Set<StaticPacketTrace>> gen = new McastGenerator(mcastService, 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.
      *
diff --git a/web/src/main/java/org/onosproject/t3/rest/T3WebResource.java b/web/src/main/java/org/onosproject/t3/rest/T3WebResource.java
index 0fe7674..0950c2d 100644
--- a/web/src/main/java/org/onosproject/t3/rest/T3WebResource.java
+++ b/web/src/main/java/org/onosproject/t3/rest/T3WebResource.java
@@ -21,10 +21,15 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 
 import org.onlab.packet.EthType;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
 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.IPCriterion;
 import org.onosproject.net.group.Group;
 import org.onosproject.rest.AbstractWebResource;
 import org.onosproject.t3.api.GroupsInDevice;
@@ -39,6 +44,7 @@
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Set;
 
@@ -100,6 +106,151 @@
     }
 
     /**
+     * Returns the mcast trace non verbose result.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("mcast")
+    public Response getT3Mcast() {
+        ObjectNode node = null;
+        try {
+            node = getT3McastJsonOutput(false);
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException(e);
+        }
+        return Response.status(200).entity(node).build();
+    }
+
+    /**
+     * Returns the mcast trace verbose result.
+     *
+     * @return 200 OK
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("mcast/verbose")
+    public Response getT3McastVerbose() {
+        ObjectNode node;
+        try {
+            node = getT3McastJsonOutput(true);
+
+        } catch (IllegalArgumentException e) {
+            throw new IllegalArgumentException(e);
+        }
+        return Response.status(200).entity(node).build();
+    }
+
+    /**
+     * Returns trace verbose or non verbose json output for the given trace.
+     *
+     * @param verbose based on verbosity level
+     * @return a json representing the trace.
+     */
+    private ObjectNode getT3McastJsonOutput(boolean verbose) {
+        TroubleshootService troubleshootService = get(TroubleshootService.class);
+        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"));
+        int totalTraces = 0;
+        List<StaticPacketTrace> failedTraces = new ArrayList<>();
+        StaticPacketTrace previousTrace = null;
+        ArrayNode traceArray = mapper.createArrayNode();
+        for (Set<StaticPacketTrace> traces : generator) {
+            ObjectNode genNode = mapper.createObjectNode();
+            totalTraces++;
+            //Print also Route if possible or packet
+            ArrayNode traceOutput = mapper.createArrayNode();
+            if (verbose) {
+                traces.forEach(trace -> {
+                    ObjectNode traceObj = mapper.createObjectNode();
+                    ArrayNode node = mapper.createArrayNode();
+                    for (Criterion packet : trace.getInitialPacket().criteria()) {
+                        node.add(packet.toString());
+                    }
+                    traceObj.set("input packet", node);
+                    traceObj.set("trace", getTraceJson(trace, verbose));
+                    traceOutput.add(traceObj);
+                });
+            } else {
+                for (StaticPacketTrace trace : traces) {
+                    ObjectNode traceObject = mapper.createObjectNode();
+                    traceObject.set("trace", traceNode(previousTrace, trace));
+                    if (previousTrace == null || !previousTrace.equals(trace)) {
+                        previousTrace = trace;
+                    }
+                    traceObject.put("result", trace.isSuccess());
+                    if (!trace.isSuccess()) {
+                        traceObject.put("reason", trace.resultMessage());
+                        failedTraces.add(trace);
+                    }
+                    traceOutput.add(traceObject);
+                }
+            }
+            genNode.set("traces", traceOutput);
+            traceArray.add(genNode);
+        }
+        nodeOutput.set("tracing packet", traceArray);
+
+        if (!verbose) {
+            if (failedTraces.size() != 0) {
+                nodeOutput.put("failed traces", failedTraces.size());
+            }
+            previousTrace = null;
+            for (StaticPacketTrace trace : failedTraces) {
+                if (previousTrace == null || !previousTrace.equals(trace)) {
+                    previousTrace = trace;
+                }
+                nodeOutput.set("trace", traceNode(previousTrace, trace));
+                if (previousTrace == null || !previousTrace.equals(trace)) {
+                    previousTrace = trace;
+                }
+                nodeOutput.put("failure", trace.resultMessage());
+            }
+            nodeOutput.put("total traces", totalTraces);
+            nodeOutput.put("errors", failedTraces.size());
+        }
+        return nodeOutput;
+    }
+
+    /**
+     * Returns verbose or non verbose json output for the given trace.      *
+     *
+     * @param previousTrace the trace
+     * @param trace         based on verbosity level
+     * @return a json representing the trace.
+     */
+    private ObjectNode traceNode(StaticPacketTrace previousTrace, StaticPacketTrace trace) {
+        ObjectNode obj = mapper.createObjectNode();
+        if (previousTrace == null || !previousTrace.equals(trace)) {
+            previousTrace = trace;
+            ConnectPoint initialConnectPoint = trace.getInitialConnectPoint();
+            TrafficSelector initialPacket = trace.getInitialPacket();
+            boolean isIPv4 = ((EthTypeCriterion) initialPacket.getCriterion(Criterion.Type.ETH_TYPE))
+                    .ethType().equals(EthType.EtherType.IPV4.ethType()
+                    );
+            IpPrefix group = ((IPCriterion) (isIPv4 ? trace.getInitialPacket()
+                    .getCriterion(Criterion.Type.IPV4_DST) : trace.getInitialPacket()
+                    .getCriterion(Criterion.Type.IPV6_DST))).ip();
+            obj.put("source", initialConnectPoint.toString());
+            obj.put("group", group.toString());
+        }
+        ArrayNode nodePath = mapper.createArrayNode();
+        for (List<ConnectPoint> listPaths : trace.getCompletePaths()) {
+            nodePath.add(listPaths.get(listPaths.size() - 1).toString());
+        }
+        if (trace.getCompletePaths().size() > 1) {
+            obj.set("sinks", nodePath);
+        } else {
+            obj.set("sink", nodePath);
+        }
+        return obj;
+    }
+
+    /**
      * Returns trace verbose or non verbose json output for the given trace.
      *
      * @param srcHost source ip identifier