[CORD-2755] PingAll scenario for Ipv4 and Ipv6 hosts

Change-Id: I8d57568a94580807e62fb5953a881850a73f43ed
(cherry picked from commit fb68135647218cf0ee7142573bdaf5ca271e29a3)
diff --git a/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java b/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
index e56035d..542dcfd 100644
--- a/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
+++ b/src/main/java/org/onosproject/t3/api/StaticPacketTrace.java
@@ -17,8 +17,10 @@
 package org.onosproject.t3.api;
 
 import com.google.common.collect.ImmutableList;
+import org.apache.commons.lang3.tuple.Pair;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
 import org.onosproject.net.flow.FlowEntry;
 import org.onosproject.net.flow.TrafficSelector;
 
@@ -26,6 +28,7 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 /**
  * Encapsulates the result of tracing a packet (traffic selector) through
@@ -39,6 +42,7 @@
     private Map<DeviceId, List<GroupsInDevice>> outputsForDevice;
     private Map<DeviceId, List<FlowEntry>> flowsForDevice;
     private StringBuilder resultMessage;
+    private Pair<Host, Host> hosts;
 
     /**
      * Builds the trace with a given packet and a connect point.
@@ -53,6 +57,24 @@
         outputsForDevice = new HashMap<>();
         flowsForDevice = new HashMap<>();
         resultMessage = new StringBuilder();
+        hosts = null;
+    }
+
+    /**
+     * 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
+     */
+    public StaticPacketTrace(TrafficSelector packet, ConnectPoint in, Pair<Host, Host> hosts) {
+        this.inPacket = packet;
+        this.in = in;
+        completePaths = new ArrayList<>();
+        outputsForDevice = new HashMap<>();
+        flowsForDevice = new HashMap<>();
+        resultMessage = new StringBuilder();
+        this.hosts = hosts;
     }
 
     /**
@@ -155,6 +177,24 @@
         return flowsForDevice.getOrDefault(deviceId, ImmutableList.of());
     }
 
+    /**
+     * Return, if present, the two hosts at the endpoints of this trace.
+     *
+     * @return pair of source and destination hosts
+     */
+    public Optional<Pair<Host, Host>> getEndpointHosts() {
+        return Optional.ofNullable(hosts);
+    }
+
+    /**
+     * Sets the two hosts at the endpoints of this trace.
+     *
+     * @param endpointHosts pair of source and destination hosts
+     */
+    public void addEndpointHosts(Pair<Host, Host> endpointHosts) {
+        hosts = endpointHosts;
+    }
+
     @Override
     public String toString() {
         return "StaticPacketTrace{" +
diff --git a/src/main/java/org/onosproject/t3/api/TroubleshootService.java b/src/main/java/org/onosproject/t3/api/TroubleshootService.java
index 01bae57..028df64 100644
--- a/src/main/java/org/onosproject/t3/api/TroubleshootService.java
+++ b/src/main/java/org/onosproject/t3/api/TroubleshootService.java
@@ -21,6 +21,8 @@
 import org.onosproject.net.HostId;
 import org.onosproject.net.flow.TrafficSelector;
 
+import java.util.List;
+
 /**
  * API for troubleshooting services, providing static analysis of installed
  * flows and groups.
@@ -28,6 +30,14 @@
 public interface TroubleshootService {
 
     /**
+     * Requests a static trace be performed between all hosts in the network, given a type of traffic.
+     *
+     * @param type the etherType of the traffic we want to trace.
+     * @return a trace result
+     */
+    List<StaticPacketTrace> pingAll(EthType.EtherType type);
+
+    /**
      * Requests a static trace be performed between the two hosts in the network, given a type of traffic.
      *
      * @param sourceHost      source host
diff --git a/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllTraceCommand.java b/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllTraceCommand.java
new file mode 100644
index 0000000..8cecc8d
--- /dev/null
+++ b/src/main/java/org/onosproject/t3/cli/TroubleshootPingAllTraceCommand.java
@@ -0,0 +1,110 @@
+/*
+ * 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.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.Host;
+import org.onosproject.t3.api.StaticPacketTrace;
+import org.onosproject.t3.api.TroubleshootService;
+
+import java.util.List;
+
+import static org.onlab.packet.EthType.EtherType;
+
+/**
+ * Starts a Static Packet Trace for a given input and prints the result.
+ */
+@Command(scope = "onos", name = "t3-troubleshoot-pingall",
+        description = "Traces a ping between all hosts in the system of a given ETH type")
+public class TroubleshootPingAllTraceCommand extends AbstractShellCommand {
+
+    private static final String FMT_SHORT =
+            "id=%s, mac=%s, locations=%s, vlan=%s, ip(s)=%s";
+
+    @Option(name = "-et", aliases = "--ethType", description = "ETH Type", valueToShowInHelp = "ipv4")
+    String ethType = "ipv4";
+
+    @Option(name = "-v", aliases = "--verbose", description = "Outputs trace for each host to host combination")
+    private boolean verbosity1 = false;
+
+    @Override
+    protected void execute() {
+        TroubleshootService service = get(TroubleshootService.class);
+
+        EtherType type = EtherType.valueOf(ethType.toUpperCase());
+
+        print("Tracing between all %s hosts", ethType);
+
+        if (!type.equals(EtherType.IPV4) && !type.equals(EtherType.IPV6)) {
+            print("Command only support IPv4 or IPv6");
+        } else {
+            print("--------------------------------------------------------------------------");
+            //Obtain the list of traces
+            List<StaticPacketTrace> traces = service.pingAll(type);
+
+            if (traces.size() == 0) {
+                print("No traces were obtained, please check system configuration");
+            }
+
+            traces.forEach(trace -> {
+                if (trace.getInitialPacket() != null) {
+                    if (verbosity1) {
+                        printVerbose(trace);
+                    } else {
+                        printResultOnly(trace);
+                    }
+                } else {
+                    print("Error in obtaining trace: %s", trace.resultMessage());
+                }
+                print("--------------------------------------------------------------------------");
+            });
+        }
+
+
+    }
+
+    private void printResultOnly(StaticPacketTrace trace) {
+        if (trace.getEndpointHosts().isPresent()) {
+            Host source = trace.getEndpointHosts().get().getLeft();
+            Host destination = trace.getEndpointHosts().get().getRight();
+            print("Source %s --> Destination %s", source.id(), destination.id());
+            print("%s", trace.resultMessage());
+        } else {
+            print("Can't gather host information from trace");
+            print("%s", trace.resultMessage());
+        }
+    }
+
+    private void printVerbose(StaticPacketTrace trace) {
+        if (trace.getEndpointHosts().isPresent()) {
+            Host source = trace.getEndpointHosts().get().getLeft();
+            print("Source host %s", printHost(source));
+            Host destination = trace.getEndpointHosts().get().getRight();
+            print("Destination host %s", printHost(destination));
+        }
+        print("%s", trace.getInitialPacket());
+        print("%s", T3CliUtils.printTrace(trace, false, false));
+    }
+
+    private String printHost(Host host) {
+        return String.format(FMT_SHORT, host.id(), host.mac(),
+                host.locations(),
+                host.vlan(), host.ipAddresses());
+    }
+}
diff --git a/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java b/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
index 997ef42..3674e32 100644
--- a/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
+++ b/src/main/java/org/onosproject/t3/cli/TroubleshootSimpleTraceCommand.java
@@ -59,7 +59,7 @@
 
         EtherType type = EtherType.valueOf(ethType.toUpperCase());
 
-        //Printing the created packet
+        //Printing the traced hosts
         print("Tracing between: %s and %s", srcHost, dstHost);
 
         //Build the trace
diff --git a/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java b/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
index 2c41382..4087ec9 100644
--- a/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
+++ b/src/main/java/org/onosproject/t3/impl/TroubleshootManager.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import org.apache.commons.lang3.tuple.Pair;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
@@ -131,12 +132,29 @@
     protected EdgePortService edgePortService;
 
     @Override
+    public List<StaticPacketTrace> pingAll(EtherType type) {
+        ImmutableList.Builder<StaticPacketTrace> tracesBuilder = ImmutableList.builder();
+        hostService.getHosts().forEach(host -> {
+            List<IpAddress> ipAddresses = getIpAddresses(host, type, false);
+            if (ipAddresses.size() > 0) {
+                hostService.getHosts().forEach(hostToPing -> {
+                    List<IpAddress> ipAddressesToPing = getIpAddresses(hostToPing, type, false);
+                    if (ipAddressesToPing.size() > 0 && !host.equals(hostToPing)) {
+                        tracesBuilder.add(trace(host.id(), hostToPing.id(), type));
+                    }
+                });
+            }
+        });
+        return tracesBuilder.build();
+    }
+
+    @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);
+        //Temporary trace to fail in case we don't have enough information or what is provided is incoherent
+        StaticPacketTrace failTrace = new StaticPacketTrace(null, null, Pair.of(source, destination));
 
         if (source == null) {
             failTrace.addResultMessage("Source Host " + sourceHost + " does not exist");
@@ -160,7 +178,9 @@
             // we are under same leaf so it's L2 Unicast.
             if (areBridged(source, destination)) {
                 selectorBuilder.matchEthDst(destination.mac());
-                return trace(selectorBuilder.build(), source.location());
+                StaticPacketTrace trace = trace(selectorBuilder.build(), source.location());
+                trace.addEndpointHosts(Pair.of(source, destination));
+                return trace;
             }
 
             //handle the IPs for src and dst in case of L3
@@ -191,8 +211,9 @@
                 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());
+            StaticPacketTrace trace = trace(selectorBuilder.build(), source.location());
+            trace.addEndpointHosts(Pair.of(source, destination));
+            return trace;
 
         } catch (ConfigException e) {
             failTrace.addResultMessage("Can't get config " + e.getMessage());
@@ -212,14 +233,7 @@
      */
     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());
+        List<IpAddress> ips = getIpAddresses(host, etherType, true);
 
         if (ips.size() > 0) {
             if (etherType.equals(EtherType.IPV4)) {
@@ -242,6 +256,21 @@
         return true;
     }
 
+    private List<IpAddress> getIpAddresses(Host host, EtherType etherType, boolean checklocal) {
+        return host.ipAddresses().stream().filter(ipAddress -> {
+            boolean correctIp = false;
+            if (etherType.equals(EtherType.IPV4)) {
+                correctIp = ipAddress.isIp4();
+            } else if (etherType.equals(EtherType.IPV6)) {
+                correctIp = ipAddress.isIp6();
+            }
+            if (checklocal) {
+                correctIp = correctIp && !ipAddress.isLinkLocal();
+            }
+            return correctIp;
+        }).collect(Collectors.toList());
+    }
+
     /**
      * Checks that two hosts are bridged (L2Unicast).
      *
diff --git a/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 7444994..8fe5597 100644
--- a/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -28,6 +28,12 @@
                 <entry key="-et" value-ref="ethTypeCompleter"/>
             </optional-completers>
         </command>
+        <command>
+            <action class="org.onosproject.t3.cli.TroubleshootPingAllTraceCommand"/>
+            <optional-completers>
+                <entry key="-et" value-ref="ethTypeCompleter"/>
+            </optional-completers>
+        </command>
     </command-bundle>
 
     <bean id="hostIdCompleter" class="org.onosproject.cli.net.HostIdCompleter"/>