CORD-535 Added purge and sync states CLIs

And improved result format of the existing CLIs

Change-Id: I49476ab52f77f6fadddf00dfc113c51153b300ed
diff --git a/src/main/java/org/opencord/cordvtn/api/net/VtnPort.java b/src/main/java/org/opencord/cordvtn/api/net/VtnPort.java
index 0ec1078..3ba4192 100644
--- a/src/main/java/org/opencord/cordvtn/api/net/VtnPort.java
+++ b/src/main/java/org/opencord/cordvtn/api/net/VtnPort.java
@@ -23,6 +23,7 @@
 import org.onlab.packet.VlanId;
 import org.openstack4j.model.network.Port;
 
+import java.util.Comparator;
 import java.util.Objects;
 import java.util.Set;
 
@@ -38,7 +39,7 @@
  */
 public final class VtnPort extends ServicePort {
 
-    private static final String ERR_IP_MISSING = "VTN port IP adderess is missing";
+    private static final String ERR_IP_MISSING = "VTN port IP address is missing";
 
     private final NetworkId netId;
     private final MacAddress mac;
@@ -56,6 +57,9 @@
         this.ip = ip;
     }
 
+    public static final Comparator<VtnPort> VTN_PORT_COMPARATOR =
+            (port1, port2) -> port1.netId().id().compareTo(port2.netId().id());
+
     /**
      * Returns the network ID of this port.
      *
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnNetworkListCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnNetworkListCommand.java
new file mode 100644
index 0000000..a97a7ae
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnNetworkListCommand.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.opencord.cordvtn.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.opencord.cordvtn.api.core.CordVtnService;
+import org.opencord.cordvtn.api.net.VtnNetwork;
+
+import java.util.Set;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists VTN networks.
+ */
+@Command(scope = "onos", name = "cordvtn-networks",
+        description = "Lists all VTN networks")
+public class CordVtnNetworkListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-8s%-20s%s";
+
+    @Override
+    protected void execute() {
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+        Set<VtnNetwork> networks = service.vtnNetworks();
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(networks)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list networks in JSON format");
+            }
+        } else {
+            print(FORMAT, "ID", "Type", "VNI", "Subnet", "Service IP");
+            for (VtnNetwork net: networks) {
+                print(FORMAT, net.id(),
+                      net.type(),
+                      net.segmentId(),
+                      net.subnet(),
+                      net.serviceIp());
+            }
+        }
+    }
+
+    private JsonNode json(Set<VtnNetwork> networks) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (VtnNetwork net: networks) {
+            ArrayNode providers = mapper().createArrayNode();
+            net.providers().forEach(provider -> providers.add(
+                    mapper().createObjectNode()
+                            .put("networkId", provider.id().id())
+                            .put("type", provider.type().name())));
+
+            result.add(mapper().createObjectNode()
+                               .put("id", net.id().id())
+                               .put("type", net.type().name())
+                               .put("vni", net.segmentId().id())
+                               .put("subnet", net.subnet().toString())
+                               .put("serviceIp", net.serviceIp().toString())
+                               .set("providers", providers));
+        }
+        return result;
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeCheckCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeCheckCommand.java
index 17dbb45..9e988ca 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeCheckCommand.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeCheckCommand.java
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.opencord.cordvtn.cli;
 
 import com.jcraft.jsch.Session;
@@ -45,6 +44,11 @@
             required = true, multiValued = false)
     private String hostname = null;
 
+    private static final String COMPLETE = "COMPLETE";
+    private static final String INCOMPLETE = "INCOMPLETE";
+    private static final String HINT = "hint: try init again if the state is INCOMPLETE" +
+            " but all settings OK";
+
     @Override
     protected void execute() {
         CordVtnNodeManager nodeManager = AbstractShellCommand.get(CordVtnNodeManager.class);
@@ -60,7 +64,7 @@
             print("Cannot find %s from registered nodes", hostname);
             return;
         }
-
+        print("Current state: %s (%s)", getState(nodeManager, node), HINT);
         print("%n[Integration Bridge Status]");
         Device device = deviceService.getDevice(node.integrationBridgeId());
         if (device != null) {
@@ -131,4 +135,8 @@
               isUp ? Boolean.TRUE : Boolean.FALSE,
               isIp ? Boolean.TRUE : Boolean.FALSE);
     }
+
+    private String getState(CordVtnNodeManager nodeManager, CordVtnNode node) {
+        return nodeManager.isNodeInitComplete(node) ? COMPLETE : INCOMPLETE;
+    }
 }
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeListCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeListCommand.java
index 1a40397..5c995f1 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeListCommand.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnNodeListCommand.java
@@ -13,11 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.opencord.cordvtn.cli;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
@@ -27,6 +26,8 @@
 import java.util.Collections;
 import java.util.List;
 
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
 /**
  * Lists all nodes registered to the service.
  */
@@ -36,6 +37,7 @@
 
     private static final String COMPLETE = "COMPLETE";
     private static final String INCOMPLETE = "INCOMPLETE";
+    private static final String FORMAT = "%-30s%-20s%-20s%-15s%-24s%s";
 
     @Override
     protected void execute() {
@@ -44,15 +46,21 @@
         Collections.sort(nodes, CordVtnNode.CORDVTN_NODE_COMPARATOR);
 
         if (outputJson()) {
-            print("%s", json(nodeManager, nodes));
+            try {
+                print("%s", mapper().writeValueAsString(json(nodeManager, nodes)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list networks in JSON format");
+            }
         } else {
+            print(FORMAT, "Hostname", "Management IP", "Data IP", "Data Iface",
+                  "Br-int", "State");
+
             for (CordVtnNode node : nodes) {
-                print("hostname=%s, hostMgmtIp=%s, dataIp=%s, br-int=%s, dataIface=%s, init=%s",
-                      node.hostname(),
+                print(FORMAT, node.hostname(),
                       node.hostMgmtIp().cidr(),
                       node.dataIp().cidr(),
-                      node.integrationBridgeId().toString(),
                       node.dataIface(),
+                      node.integrationBridgeId().toString(),
                       getState(nodeManager, node));
             }
             print("Total %s nodes", nodeManager.getNodeCount());
@@ -60,16 +68,15 @@
     }
 
     private JsonNode json(CordVtnNodeManager nodeManager, List<CordVtnNode> nodes) {
-        ObjectMapper mapper = new ObjectMapper();
-        ArrayNode result = mapper.createArrayNode();
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
         for (CordVtnNode node : nodes) {
-            result.add(mapper.createObjectNode()
+            result.add(mapper().createObjectNode()
                                .put("hostname", node.hostname())
-                               .put("hostManagementIp", node.hostMgmtIp().cidr())
-                               .put("dataPlaneIp", node.dataIp().cidr())
+                               .put("managementIp", node.hostMgmtIp().cidr())
+                               .put("dataIp", node.dataIp().cidr())
+                               .put("dataInterface", node.dataIface())
                                .put("bridgeId", node.integrationBridgeId().toString())
-                               .put("dataPlaneInterface", node.dataIface())
-                               .put("init", getState(nodeManager, node)));
+                               .put("state", getState(nodeManager, node)));
         }
         return result;
     }
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnPortListCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnPortListCommand.java
new file mode 100644
index 0000000..361b220
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnPortListCommand.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.opencord.cordvtn.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.opencord.cordvtn.api.core.CordVtnService;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.VtnNetwork;
+import org.opencord.cordvtn.api.net.VtnPort;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists VTN networks.
+ */
+@Command(scope = "onos", name = "cordvtn-ports",
+        description = "Lists all VTN ports")
+public class CordVtnPortListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-18s%-6s%s";
+
+    @Argument(name = "networkId", description = "Network ID")
+    private String networkId = null;
+
+    @Override
+    protected void execute() {
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+
+        List<VtnPort> ports;
+        if (Strings.isNullOrEmpty(networkId)) {
+            ports = Lists.newArrayList(service.vtnPorts());
+        } else {
+            VtnNetwork vtnNet = service.vtnNetwork(NetworkId.of(networkId));
+            if (vtnNet == null) {
+                print("Network %s does not exists", networkId);
+                return;
+            }
+            ports = service.vtnPorts().stream()
+                    .filter(p -> p.netId().equals(NetworkId.of(networkId)))
+                    .collect(Collectors.toList());
+            Collections.sort(ports, VtnPort.VTN_PORT_COMPARATOR);
+        }
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(ports)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list networks in JSON format");
+            }
+        } else {
+            print(FORMAT, "ID", "MAC", "IP", "VLAN", "Additional IPs");
+            for (VtnPort port: ports) {
+                List<String> floatingIps = port.addressPairs().stream()
+                        .map(ip -> ip.ip().toString())
+                        .collect(Collectors.toList());
+                print(FORMAT, port.id(),
+                      port.mac(),
+                      port.ip(),
+                      port.vlanId().isPresent() ? port.vlanId().get() : "",
+                      floatingIps.isEmpty() ? "" : floatingIps);
+            }
+        }
+    }
+
+    private JsonNode json(List<VtnPort> ports) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (VtnPort port: ports) {
+            ArrayNode addrPairs = mapper().createArrayNode();
+            port.addressPairs().forEach(pair -> addrPairs.add(
+                    mapper().createObjectNode()
+                            .put("ip", pair.ip().toString())
+                            .put("mac", pair.mac().toString())));
+
+            result.add(mapper().createObjectNode()
+                               .put("id", port.id().id())
+                               .put("networkId", port.netId().id())
+                               .put("mac", port.mac().toString())
+                               .put("ip", port.ip().toString())
+                               .put("vlan", port.vlanId().isPresent() ?
+                                       port.vlanId().get().toString() : null)
+                               .set("addressPairs", addrPairs));
+        }
+        return result;
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeRulesCommand.java
similarity index 79%
rename from src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java
rename to src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeRulesCommand.java
index 3669b48..06f5aee 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeRulesCommand.java
@@ -13,7 +13,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.opencord.cordvtn.cli;
 
 import org.apache.karaf.shell.commands.Command;
@@ -21,16 +20,15 @@
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
 /**
- * Deletes nodes from the service.
+ * Purges all flow rules installed by CORD VTN service.
  */
-@Command(scope = "onos", name = "cordvtn-flush-rules",
-        description = "Flush flow rules installed by CORD VTN")
-public class CordVtnFlushRules extends AbstractShellCommand {
+@Command(scope = "onos", name = "cordvtn-purge-rules",
+        description = "Purges all flow rules installed by CORD VTN")
+public class CordVtnPurgeRulesCommand extends AbstractShellCommand {
 
     @Override
     protected void execute() {
         CordVtnPipeline pipeline = AbstractShellCommand.get(CordVtnPipeline.class);
         pipeline.flushRules();
-        print("Successfully flushed");
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeStatesCommand.java
similarity index 65%
copy from src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java
copy to src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeStatesCommand.java
index 3669b48..b26ec66 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeStatesCommand.java
@@ -13,24 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.opencord.cordvtn.cli;
 
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.opencord.cordvtn.impl.CordVtnPipeline;
+import org.opencord.cordvtn.api.core.CordVtnAdminService;
 
 /**
- * Deletes nodes from the service.
+ * Purges internal network states.
  */
-@Command(scope = "onos", name = "cordvtn-flush-rules",
-        description = "Flush flow rules installed by CORD VTN")
-public class CordVtnFlushRules extends AbstractShellCommand {
+@Command(scope = "onos", name = "cordvtn-purge-states",
+        description = "Purges network states")
+public class CordVtnPurgeStatesCommand extends AbstractShellCommand {
 
     @Override
     protected void execute() {
-        CordVtnPipeline pipeline = AbstractShellCommand.get(CordVtnPipeline.class);
-        pipeline.flushRules();
-        print("Successfully flushed");
+        CordVtnAdminService adminService =
+                AbstractShellCommand.get(CordVtnAdminService.class);
+        adminService.purgeStates();
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncStatesCommand.java
similarity index 63%
copy from src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java
copy to src/main/java/org/opencord/cordvtn/cli/CordVtnSyncStatesCommand.java
index 3669b48..5540391 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnFlushRules.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncStatesCommand.java
@@ -13,24 +13,23 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.opencord.cordvtn.cli;
 
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.opencord.cordvtn.impl.CordVtnPipeline;
+import org.opencord.cordvtn.api.core.CordVtnAdminService;
 
 /**
- * Deletes nodes from the service.
+ * Synchronizes network states with external services.
  */
-@Command(scope = "onos", name = "cordvtn-flush-rules",
-        description = "Flush flow rules installed by CORD VTN")
-public class CordVtnFlushRules extends AbstractShellCommand {
+@Command(scope = "onos", name = "cordvtn-sync-states",
+        description = "Synchronizes network states with external services")
+public class CordVtnSyncStatesCommand extends AbstractShellCommand {
 
     @Override
     protected void execute() {
-        CordVtnPipeline pipeline = AbstractShellCommand.get(CordVtnPipeline.class);
-        pipeline.flushRules();
-        print("Successfully flushed");
+        CordVtnAdminService adminService =
+                AbstractShellCommand.get(CordVtnAdminService.class);
+        adminService.syncStates();
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
index b1858a3..78cc228 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
@@ -289,7 +289,7 @@
      */
     public boolean isNodeInitComplete(CordVtnNode node) {
         checkNotNull(node);
-        return getNodeState(node).equals(NodeState.COMPLETE);
+        return isNodeStateComplete(node);
     }
 
     /**
diff --git a/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 0e717d5..bdb9335 100644
--- a/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -29,7 +29,19 @@
             <action class="org.opencord.cordvtn.cli.CordVtnNodeCheckCommand"/>
         </command>
         <command>
-            <action class="org.opencord.cordvtn.cli.CordVtnFlushRules"/>
+            <action class="org.opencord.cordvtn.cli.CordVtnPurgeRulesCommand"/>
+        </command>
+        <command>
+            <action class="org.opencord.cordvtn.cli.CordVtnPurgeStatesCommand"/>
+        </command>
+        <command>
+            <action class="org.opencord.cordvtn.cli.CordVtnSyncStatesCommand"/>
+        </command>
+        <command>
+            <action class="org.opencord.cordvtn.cli.CordVtnNetworkListCommand"/>
+        </command>
+        <command>
+            <action class="org.opencord.cordvtn.cli.CordVtnPortListCommand"/>
         </command>
     </command-bundle>
 </blueprint>