[ONOS-7752] check single VM-to-VM east-west reachability

Change-Id: I0f27cefb1c70a0b05c4e71e156d81bc041c1d885
diff --git a/apps/openstacktroubleshoot/api/BUCK b/apps/openstacktroubleshoot/api/BUCK
index 520bddf..27cd4a2 100644
--- a/apps/openstacktroubleshoot/api/BUCK
+++ b/apps/openstacktroubleshoot/api/BUCK
@@ -2,6 +2,7 @@
     '//lib:CORE_DEPS',
     '//lib:org.apache.karaf.shell.console',
     '//cli:onos-cli',
+    '//apps/openstacknetworking/api:onos-apps-openstacknetworking-api',
 ]
 
 TEST_DEPS = [
diff --git a/apps/openstacktroubleshoot/api/BUILD b/apps/openstacktroubleshoot/api/BUILD
index b22d9f6..7c159a7 100644
--- a/apps/openstacktroubleshoot/api/BUILD
+++ b/apps/openstacktroubleshoot/api/BUILD
@@ -1,4 +1,6 @@
-COMPILE_DEPS = CORE_DEPS + CLI
+COMPILE_DEPS = CORE_DEPS + CLI + [
+    "//apps/openstacknetworking/api:onos-apps-openstacknetworking-api",
+]
 
 TEST_DEPS = TEST_ADAPTERS + [
     "//core/api:onos-api-tests",
diff --git a/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java b/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java
index 4ad6cfd..272e257 100644
--- a/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java
+++ b/apps/openstacktroubleshoot/api/src/main/java/org/onosproject/openstacktroubleshoot/api/OpenstackTroubleshootService.java
@@ -16,6 +16,7 @@
 package org.onosproject.openstacktroubleshoot.api;
 
 import org.onlab.packet.IpAddress;
+import org.onosproject.openstacknetworking.api.InstancePort;
 
 import java.util.Map;
 
@@ -34,14 +35,12 @@
     /**
      * Checks a single VM-to-Vm connectivity.
      *
-     * @param srcNetId  source network ID
-     * @param srcIp     source IP address
-     * @param dstNetId  destination network ID
-     * @param dstIp     destination IP address
+     * @param srcInstancePort source instance port
+     * @param dstInstancePort destination instance port
      * @return reachability
      */
-    Reachability probeEastWest(String srcNetId, IpAddress srcIp,
-                               String dstNetId, IpAddress dstIp);
+    Reachability probeEastWest(InstancePort srcInstancePort,
+                               InstancePort dstInstancePort);
 
     /**
      * Checks all north-south router to VMs' connectivity.
diff --git a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/ActiveVmIpCompleter.java b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/ActiveVmIpCompleter.java
new file mode 100644
index 0000000..326b041
--- /dev/null
+++ b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/ActiveVmIpCompleter.java
@@ -0,0 +1,48 @@
+/*
+ * 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.openstacktroubleshoot.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+
+import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.stream.Collectors;
+
+/**
+ * Active VM IP address completer.
+ */
+public class ActiveVmIpCompleter implements Completer {
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        StringsCompleter delegate = new StringsCompleter();
+        InstancePortService service = AbstractShellCommand.get(InstancePortService.class);
+        Set<String> set = service.instancePorts().stream()
+                .filter(p -> p.state() == InstancePort.State.ACTIVE)
+                .map(p -> p.ipAddress().getIp4Address().toString())
+                .collect(Collectors.toSet());
+
+        SortedSet<String> strings = delegate.getStrings();
+        strings.addAll(set);
+
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
diff --git a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java
index f9786ac..696cb11 100644
--- a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java
+++ b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/cli/OpenstackEastWestProbeCommand.java
@@ -15,12 +15,21 @@
  */
 package org.onosproject.openstacktroubleshoot.cli;
 
+import com.google.common.collect.Sets;
+import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.packet.IpAddress;
 import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacktroubleshoot.api.OpenstackTroubleshootService;
 import org.onosproject.openstacktroubleshoot.api.Reachability;
 
 import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Checks the east-west VMs connectivity.
@@ -35,23 +44,90 @@
 
     private static final String FORMAT = "%-20s%-5s%-20s%-20s";
 
+    @Option(name = "-a", aliases = "--all", description = "Apply this command to all VMs",
+            required = false, multiValued = false)
+    private boolean isAll = false;
+
+    @Argument(index = 0, name = "vmIps", description = "VMs' IP addresses",
+            required = false, multiValued = true)
+    private String[] vmIps = null;
+
     @Override
     protected void execute() {
-        OpenstackTroubleshootService troubleshootService =
-                AbstractShellCommand.get(OpenstackTroubleshootService.class);
+        OpenstackTroubleshootService tsService =
+                get(OpenstackTroubleshootService.class);
 
-        if (troubleshootService == null) {
+        InstancePortService instPortService =
+                get(InstancePortService.class);
+
+        if (tsService == null) {
             error("Failed to troubleshoot openstack networking.");
             return;
         }
 
+        if ((!isAll && vmIps == null) || (isAll && vmIps != null)) {
+            print("Please specify one of VM IP address or -a option.");
+            return;
+        }
+
+        if (isAll) {
+            printHeader();
+            Map<String, Reachability> map = tsService.probeEastWestBulk();
+            map.values().forEach(this::printReachability);
+        } else {
+            if (vmIps.length > 2) {
+                print("Too many VM IPs. The number of IP should be limited to 2.");
+                return;
+            }
+
+            IpAddress srcIp = IpAddress.valueOf(vmIps[0]);
+            InstancePort srcPort = instPort(instPortService, srcIp);
+
+            if (srcPort == null) {
+                print("Specified source IP is not existing.");
+                return;
+            }
+
+            final Set<IpAddress> dstIps = Sets.newConcurrentHashSet();
+
+            if (vmIps.length == 2) {
+                dstIps.add(IpAddress.valueOf(vmIps[1]));
+            }
+
+            if (vmIps.length == 1) {
+                dstIps.addAll(instPortService.instancePorts().stream()
+                        .filter(p -> !p.ipAddress().equals(srcIp))
+                        .filter(p -> p.state().equals(InstancePort.State.ACTIVE))
+                        .map(InstancePort::ipAddress)
+                        .collect(Collectors.toSet()));
+            }
+
+            printHeader();
+            dstIps.stream()
+                    .filter(ip -> instPort(instPortService, ip) != null)
+                    .map(ip -> instPort(instPortService, ip))
+                    .forEach(port -> printReachability(tsService.probeEastWest(srcPort, port)));
+        }
+    }
+
+    private InstancePort instPort(InstancePortService service, IpAddress ip) {
+        Optional<InstancePort> port = service.instancePorts().stream()
+                .filter(p -> p.ipAddress().equals(ip)).findFirst();
+
+        if (port.isPresent()) {
+            return port.get();
+        } else {
+            print("Specified destination IP is not existing.");
+            return null;
+        }
+    }
+
+    private void printHeader() {
         print(FORMAT, "Source IP", "", "Destination IP", "Reachability");
+    }
 
-        Map<String, Reachability> map = troubleshootService.probeEastWestBulk();
-
-        map.values().forEach(r -> {
-            String result = r.isReachable() ? REACHABLE : UNREACHABLE;
-            print(FORMAT, r.srcIp().toString(), ARROW, r.dstIp().toString(), result);
-        });
+    private void printReachability(Reachability r) {
+        String result = r.isReachable() ? REACHABLE : UNREACHABLE;
+        print(FORMAT, r.srcIp().toString(), ARROW, r.dstIp().toString(), result);
     }
 }
diff --git a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java
index 3124da6..2354076 100644
--- a/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java
+++ b/apps/openstacktroubleshoot/app/src/main/java/org/onosproject/openstacktroubleshoot/impl/OpenstackTroubleshootManager.java
@@ -257,21 +257,17 @@
     }
 
     @Override
-    public Reachability probeEastWest(String srcNetId, IpAddress srcIp,
-                                      String dstNetId, IpAddress dstIp) {
+    public Reachability probeEastWest(InstancePort srcPort, InstancePort dstPort) {
 
         Reachability.Builder rBuilder = DefaultReachability.builder()
-                                                        .srcIp(srcIp)
-                                                        .dstIp(dstIp);
+                                                        .srcIp(srcPort.ipAddress())
+                                                        .dstIp(dstPort.ipAddress());
 
-        if (srcIp.equals(dstIp)) {
+        if (srcPort.equals(dstPort)) {
             // self probing should always return true
             rBuilder.isReachable(true);
             return rBuilder.build();
         }  else {
-            InstancePort srcPort = instancePortService.instancePort(srcIp, srcNetId);
-            InstancePort dstPort = instancePortService.instancePort(dstIp, dstNetId);
-
             if (srcPort.state() == ACTIVE && dstPort.state() == ACTIVE) {
 
                 // install flow rules to enforce ICMP_REQUEST to be tagged and direct to ACL table
diff --git a/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 67a1364..c7fd715 100644
--- a/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacktroubleshoot/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -1,5 +1,5 @@
 <!--
-~ Copyright 2017-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.
@@ -17,6 +17,11 @@
     <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
         <command>
             <action class="org.onosproject.openstacktroubleshoot.cli.OpenstackEastWestProbeCommand"/>
+            <completers>
+                <ref component-id="activeVmIpCompleter"/>
+            </completers>
         </command>
     </command-bundle>
+
+    <bean id="activeVmIpCompleter" class="org.onosproject.openstacktroubleshoot.cli.ActiveVmIpCompleter"/>
 </blueprint>