ONOS-5970 Added CLIs to purge and sync network states from Neutron

Change-Id: Icd1f4c174de20e261229f30ddfd6857c26cb038c
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java
index 5ebb559..f0a2442 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java
@@ -86,4 +86,9 @@
      * @param portId port id
      */
     void removePort(String portId);
+
+    /**
+     * Clears the existing network, subnet and port states.
+     */
+    void clear();
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
index 86f338fe..671b811 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
@@ -138,4 +138,9 @@
      * @return set of ports
      */
     Set<Port> ports();
+
+    /**
+     * Removes the existing network and ports.
+     */
+    void clear();
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java
index 5870eb0..7d4df84 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java
@@ -45,6 +45,7 @@
      */
     void removeRouter(String osRouterId);
 
+    // TODO fix the logic adding a router interface to a router
     /**
      * Adds a new interface to the router.
      *
@@ -86,4 +87,9 @@
      * @param floatingIpId floating ip id
      */
     void removeFloatingIp(String floatingIpId);
+
+    /**
+     * Clears the existing routers and floating IPs.
+     */
+    void clear();
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java
index b0744a0..fa83509 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java
@@ -138,4 +138,9 @@
      * @return openstack floating ip; empty set if no floating ip exists
      */
     Set<NetFloatingIP> floatingIps();
+
+    /**
+     * Clears the existing routers and floating IPs.
+     */
+    void clear();
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPurgeStateCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPurgeStateCommand.java
new file mode 100644
index 0000000..6f28590
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPurgeStateCommand.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2017-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.onosproject.openstacknetworking.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
+
+/**
+ * Purges all existing network states.
+ */
+@Command(scope = "onos", name = "openstack-purge-states",
+        description = "Purges all OpenStack network states")
+public class OpenstackPurgeStateCommand extends AbstractShellCommand {
+
+    @Override
+    protected void execute() {
+        get(OpenstackNetworkAdminService.class).clear();
+        get(OpenstackRouterAdminService.class).clear();
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncStateCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncStateCommand.java
new file mode 100644
index 0000000..d07591e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncStateCommand.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2017-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.onosproject.openstacknetworking.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.base.Strings;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.api.OSClient;
+import org.openstack4j.api.exceptions.AuthenticationException;
+import org.openstack4j.model.identity.Access;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.OSFactory;
+import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.openstack4j.core.transport.ObjectMapperSingleton.getContext;
+
+/**
+ * Synchronizes OpenStack network states.
+ */
+@Command(scope = "onos", name = "openstack-sync-states",
+        description = "Synchronizes all OpenStack network states")
+public class OpenstackSyncStateCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "endpoint", description = "OpenStack service endpoint",
+            required = true, multiValued = false)
+    private String endpoint = null;
+
+    @Argument(index = 1, name = "tenant", description = "OpenStack admin tenant name",
+            required = true, multiValued = false)
+    private String tenant = null;
+
+    @Argument(index = 2, name = "user", description = "OpenStack admin user name",
+            required = true, multiValued = false)
+    private String user = null;
+
+    @Argument(index = 3, name = "password", description = "OpenStack admin user password",
+            required = true, multiValued = false)
+    private String password = null;
+
+    private static final String NETWORK_FORMAT = "%-40s%-20s%-20s%-8s";
+    private static final String SUBNET_FORMAT = "%-40s%-20s%-20s";
+    private static final String PORT_FORMAT = "%-40s%-20s%-20s%-8s";
+    private static final String ROUTER_FORMAT = "%-40s%-20s%-20s%-8s";
+    private static final String FLOATING_IP_FORMAT = "%-40s%-20s%-20s";
+
+    private static final String DEVICE_OWNER_GW = "network:router_gateway";
+    private static final String DEVICE_OWNER_IFACE = "network:router_interface";
+
+    @Override
+    protected void execute() {
+        OpenstackNetworkAdminService osNetAdminService = get(OpenstackNetworkAdminService.class);
+        OpenstackNetworkService osNetService = get(OpenstackNetworkService.class);
+        OpenstackRouterAdminService osRouterAdminService = get(OpenstackRouterAdminService.class);
+        OpenstackRouterService osRouterService = get(OpenstackRouterService.class);
+
+        Access osAccess;
+        try {
+            osAccess = OSFactory.builder()
+                    .endpoint(this.endpoint)
+                    .tenantName(this.tenant)
+                    .credentials(this.user, this.password)
+                    .authenticate()
+                    .getAccess();
+        } catch (AuthenticationException e) {
+            print("Authentication failed");
+            return;
+        } catch (Exception e) {
+            print("Failed to access OpenStack service");
+            return;
+        }
+
+        OSClient osClient = OSFactory.clientFromAccess(osAccess);
+
+        print("Synchronizing OpenStack networks...");
+        print(NETWORK_FORMAT, "ID", "Name", "VNI", "Subnets");
+        osClient.networking().network().list().forEach(osNet -> {
+            if (osNetService.network(osNet.getId()) != null) {
+                osNetAdminService.updateNetwork(osNet);
+            } else {
+                osNetAdminService.createNetwork(osNet);
+            }
+            printNetwork(osNet);
+        });
+
+        print("\nSynchronizing OpenStack subnets...");
+        print(SUBNET_FORMAT, "ID", "Network", "CIDR");
+        osClient.networking().subnet().list().forEach(osSubnet -> {
+            if (osNetService.subnet(osSubnet.getId()) != null) {
+                osNetAdminService.updateSubnet(osSubnet);
+            } else {
+                osNetAdminService.createSubnet(osSubnet);
+            }
+            printSubnet(osSubnet, osNetService);
+        });
+
+        print("\nSynchronizing OpenStack ports...");
+        print(PORT_FORMAT, "ID", "Network", "MAC", "Fixed IPs");
+        osClient.networking().port().list().forEach(osPort -> {
+            if (osNetService.port(osPort.getId()) != null) {
+                osNetAdminService.updatePort(osPort);
+            } else {
+                osNetAdminService.createPort(osPort);
+            }
+            printPort(osPort, osNetService);
+        });
+
+        print("\nSynchronizing OpenStack routers...");
+        print(ROUTER_FORMAT, "ID", "Name", "External", "Internal");
+        osClient.networking().router().list().forEach(osRouter -> {
+            if (osRouterService.router(osRouter.getId()) != null) {
+                osRouterAdminService.updateRouter(osRouter);
+            } else {
+                osRouterAdminService.createRouter(osRouter);
+            }
+
+            // FIXME do we need to manage router interfaces separately?
+            osNetService.ports().stream()
+                    .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
+                            Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
+                    .forEach(osPort -> addRouterIface(osPort, osRouterService,
+                            osRouterAdminService));
+
+            printRouter(osRouter, osNetService);
+        });
+
+        print("\nSynchronizing OpenStack floating IPs...");
+        print(FLOATING_IP_FORMAT, "ID", "Floating IP", "Fixed IP");
+        osClient.networking().floatingip().list().forEach(osFloating -> {
+            if (osRouterService.floatingIp(osFloating.getId()) != null) {
+                osRouterAdminService.updateFloatingIp(osFloating);
+            } else {
+                osRouterAdminService.createFloatingIp(osFloating);
+            }
+            printFloatingIp(osFloating);
+        });
+    }
+
+    // TODO fix the logic to add router interface to router
+    private void addRouterIface(Port osPort, OpenstackRouterService service,
+                                OpenstackRouterAdminService adminService) {
+        osPort.getFixedIps().forEach(p -> {
+            JsonNode jsonTree = mapper().createObjectNode()
+                    .put("id", osPort.getDeviceId())
+                    .put("tenant_id", osPort.getTenantId())
+                    .put("subnet_id", p.getSubnetId())
+                    .put("port_id", osPort.getId());
+            try {
+                RouterInterface rIface = getContext(NeutronRouterInterface.class)
+                        .readerFor(NeutronRouterInterface.class)
+                        .readValue(jsonTree);
+                if (service.routerInterface(rIface.getPortId()) != null) {
+                    adminService.updateRouterInterface(rIface);
+                } else {
+                    adminService.addRouterInterface(rIface);
+                }
+            } catch (IOException ignore) {
+            }
+        });
+    }
+
+    private void printNetwork(Network osNet) {
+        final String strNet = String.format(NETWORK_FORMAT,
+                osNet.getId(),
+                osNet.getName(),
+                osNet.getProviderSegID(),
+                osNet.getSubnets());
+        print(strNet);
+    }
+
+    private void printSubnet(Subnet osSubnet, OpenstackNetworkService osNetService) {
+        final String strSubnet = String.format(SUBNET_FORMAT,
+                osSubnet.getId(),
+                osNetService.network(osSubnet.getNetworkId()).getName(),
+                osSubnet.getCidr());
+        print(strSubnet);
+    }
+
+    private void printPort(Port osPort, OpenstackNetworkService osNetService) {
+        List<String> fixedIps = osPort.getFixedIps().stream()
+                .map(IP::getIpAddress)
+                .collect(Collectors.toList());
+        final String strPort = String.format(PORT_FORMAT,
+                osPort.getId(),
+                osNetService.network(osPort.getNetworkId()).getName(),
+                osPort.getMacAddress(),
+                fixedIps.isEmpty() ? "" : fixedIps);
+        print(strPort);
+    }
+
+    private void printRouter(Router osRouter, OpenstackNetworkService osNetService) {
+        List<String> externals = osNetService.ports().stream()
+                .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
+                        Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_GW))
+                .flatMap(osPort -> osPort.getFixedIps().stream())
+                .map(IP::getIpAddress)
+                .collect(Collectors.toList());
+
+        List<String> internals = osNetService.ports().stream()
+                .filter(osPort -> Objects.equals(osPort.getDeviceId(), osRouter.getId()) &&
+                        Objects.equals(osPort.getDeviceOwner(), DEVICE_OWNER_IFACE))
+                .flatMap(osPort -> osPort.getFixedIps().stream())
+                .map(IP::getIpAddress)
+                .collect(Collectors.toList());
+
+        final String strRouter = String.format(ROUTER_FORMAT,
+                osRouter.getId(),
+                osRouter.getName(),
+                externals.isEmpty() ? "" : externals,
+                internals.isEmpty() ? "" : internals);
+        print(strRouter);
+    }
+
+    private void printFloatingIp(NetFloatingIP floatingIp) {
+        final String strFloating = String.format(FLOATING_IP_FORMAT,
+                floatingIp.getId(),
+                floatingIp.getFixedIpAddress(),
+                Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
+                        "" : floatingIp.getFixedIpAddress());
+        print(strFloating);
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
index 3170a88..453dc01 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
@@ -266,6 +266,13 @@
         return ImmutableSet.copyOf(osPorts);
     }
 
+    @Override
+    public void clear() {
+        osPortStore.clear();
+        osSubnetStore.clear();
+        osNetworkStore.clear();
+    }
+
     private class OpenstackNetworkMapListener implements MapEventListener<String, Network> {
 
         @Override
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
index 3262969..aab6fa3 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
@@ -258,6 +258,13 @@
         return ImmutableSet.copyOf(osFloatingIps);
     }
 
+    @Override
+    public void clear() {
+        osFloatingIpStore.clear();
+        osRouterInterfaceStore.clear();
+        osRouterStore.clear();
+    }
+
     private class OpenstackRouterMapListener implements MapEventListener<String, Router> {
 
         @Override
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
index b486090..3add240 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -208,6 +208,11 @@
     }
 
     @Override
+    public void clear() {
+        osNetworkStore.clear();
+    }
+
+    @Override
     public Network network(String netId) {
         checkArgument(!Strings.isNullOrEmpty(netId), ERR_NULL_NETWORK_ID);
         return osNetworkStore.network(netId);
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java
index 0d74940..3087f8f 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java
@@ -200,6 +200,11 @@
     }
 
     @Override
+    public void clear() {
+        osRouterStore.clear();
+    }
+
+    @Override
     public Router router(String routerId) {
         checkArgument(!Strings.isNullOrEmpty(routerId), ERR_NULL_ROUTER_ID);
         return osRouterStore.router(routerId);
diff --git a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index e14985d..16a8408 100644
--- a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -27,5 +27,11 @@
         <command>
             <action class="org.onosproject.openstacknetworking.cli.OpenstackFloatingIpListCommand"/>
         </command>
+        <command>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackPurgeStateCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackSyncStateCommand"/>
+        </command>
     </command-bundle>
 </blueprint>