Adds a CLI to show/clear cached openstack states with neutron DB
Change-Id: I270c27affc669d10236ce94ec7a546a4f85aea88
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDiffStateCommand.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDiffStateCommand.java
new file mode 100644
index 0000000..7f8b118
--- /dev/null
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackDiffStateCommand.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2019-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.openstacknetworking.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupAdminService;
+import org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.openstack4j.api.OSClient;
+import org.openstack4j.model.common.IdEntity;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.RouterInterface;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onosproject.openstacknetworking.api.Constants.FLOATING_IP_FORMAT;
+import static org.onosproject.openstacknetworking.api.Constants.NETWORK_FORMAT;
+import static org.onosproject.openstacknetworking.api.Constants.PORT_FORMAT;
+import static org.onosproject.openstacknetworking.api.Constants.ROUTER_FORMAT;
+import static org.onosproject.openstacknetworking.api.Constants.ROUTER_INTF_FORMAT;
+import static org.onosproject.openstacknetworking.api.Constants.SECURITY_GROUP_FORMAT;
+import static org.onosproject.openstacknetworking.api.Constants.SUBNET_FORMAT;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.printFloatingIp;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.printNetwork;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.printPort;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.printRouter;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.printRouterIntf;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.printSecurityGroup;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.printSubnet;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.CONTROLLER;
+
+/**
+ * Compares cached network state diff against neutron database.
+ */
+@Service
+@Command(scope = "onos", name = "openstack-diff-state",
+ description = "Compares cached network state with neutron database.")
+public class OpenstackDiffStateCommand extends AbstractShellCommand {
+
+ private static final String HTTP_HEADER_ACCEPT = "accept";
+ private static final String HTTP_HEADER_VALUE_JSON = "application/json";
+
+ @Option(name = "-s", aliases = "--show",
+ description = "Shows the differences between cached network state with neutron database.",
+ required = false, multiValued = false)
+ private boolean show = false;
+
+ @Option(name = "-c", aliases = "--clear",
+ description = "Clears the differences between cached network state with neutron database.",
+ required = false, multiValued = false)
+ private boolean clear = false;
+
+ @Override
+ protected void doExecute() {
+ OpenstackSecurityGroupAdminService osSgService =
+ get(OpenstackSecurityGroupAdminService.class);
+ OpenstackNetworkAdminService osNetService =
+ get(OpenstackNetworkAdminService.class);
+ OpenstackRouterAdminService osRouterService =
+ get(OpenstackRouterAdminService.class);
+ OpenstackNodeService osNodeService = get(OpenstackNodeService.class);
+
+ Map<String, String> headerMap = new HashMap<>();
+ headerMap.put(HTTP_HEADER_ACCEPT, HTTP_HEADER_VALUE_JSON);
+
+ Optional<OpenstackNode> node = osNodeService.nodes(CONTROLLER).stream().findFirst();
+ if (!node.isPresent()) {
+ error("Keystone auth info has not been configured. " +
+ "Please specify auth info via network-cfg.json.");
+ return;
+ }
+
+ OSClient osClient = OpenstackNetworkingUtil.getConnectedClient(node.get());
+
+ if (osClient == null) {
+ return;
+ }
+
+ if (!clear) {
+ show = true;
+ }
+
+ if (show && clear) {
+ error("Either --show (-s) or --clear (-c) option should be specified.");
+ }
+
+ if (show) {
+ print("\nComparing OpenStack floating IPs");
+ } else if (clear) {
+ print("\nClearing OpenStack floating IPs");
+ }
+ Set<String> cachedFips = osRouterService.floatingIps().stream()
+ .map(NetFloatingIP::getId).collect(Collectors.toSet());
+ Set<String> osFips = osClient.headers(headerMap).networking()
+ .floatingip().list().stream().map(NetFloatingIP::getId)
+ .collect(Collectors.toSet());
+
+ print(FLOATING_IP_FORMAT, "ID", "Floating IP", "Fixed IP");
+ getDiff(cachedFips, osFips).forEach(fipId -> {
+ printFloatingIp(osRouterService.floatingIp(fipId));
+ if (clear) {
+ osRouterService.removeFloatingIp(fipId);
+ }
+ });
+
+ Set<String> cachedPorts = osNetService.ports().stream()
+ .map(IdEntity::getId).collect(Collectors.toSet());
+ Set<String> osPorts = osClient.headers(headerMap).networking()
+ .port().list().stream().map(IdEntity::getId)
+ .collect(Collectors.toSet());
+
+ if (show) {
+ print("\nComparing OpenStack router interfaces");
+ } else if (clear) {
+ print("\nClearing OpenStack router interfaces");
+ }
+ print(ROUTER_INTF_FORMAT, "ID", "Tenant ID", "Subnet ID");
+ getDiff(cachedPorts, osPorts).forEach(portId -> {
+ RouterInterface ri = osRouterService.routerInterface(portId);
+ if (ri != null) {
+ printRouterIntf(ri);
+ if (clear) {
+ osRouterService.removeRouterInterface(portId);
+ }
+ }
+ });
+
+ if (show) {
+ print("\nComparing OpenStack ports");
+ } else if (clear) {
+ print("\nClearing OpenStack ports");
+ }
+ print(PORT_FORMAT, "ID", "Network", "MAC", "Fixed IPs");
+ getDiff(cachedPorts, osPorts).forEach(portId -> {
+ printPort(osNetService.port(portId), osNetService);
+ if (clear) {
+ osNetService.removePort(portId);
+ }
+ });
+
+ if (show) {
+ print("\nComparing OpenStack routers");
+ } else if (clear) {
+ print("\nClearing OpenStack routers");
+ }
+ Set<String> cachedRouters = osRouterService.routers().stream()
+ .map(IdEntity::getId).collect(Collectors.toSet());
+ Set<String> osRouters = osClient.headers(headerMap).networking()
+ .router().list().stream().map(IdEntity::getId)
+ .collect(Collectors.toSet());
+
+ print(ROUTER_FORMAT, "ID", "Name", "External", "Internal");
+ getDiff(cachedRouters, osRouters).forEach(routerId -> {
+ printRouter(osRouterService.router(routerId), osNetService);
+ if (clear) {
+ osRouterService.removeRouter(routerId);
+ }
+ });
+
+ if (show) {
+ print("\nComparing OpenStack subnets");
+ } else if (clear) {
+ print("\nClearing OpenStack subnets");
+ }
+ Set<String> cachedSubnets = osNetService.subnets().stream()
+ .map(IdEntity::getId).collect(Collectors.toSet());
+ Set<String> osSubnets = osClient.headers(headerMap).networking()
+ .subnet().list().stream().map(IdEntity::getId)
+ .collect(Collectors.toSet());
+
+ print(SUBNET_FORMAT, "ID", "Network", "CIDR");
+ getDiff(cachedSubnets, osSubnets).forEach(subnetId -> {
+ printSubnet(osNetService.subnet(subnetId), osNetService);
+ if (clear) {
+ osNetService.removeSubnet(subnetId);
+ }
+ });
+
+ if (show) {
+ print("\nComparing OpenStack networks");
+ } else if (clear) {
+ print("\nClearing OpenStack networks");
+ }
+ Set<String> cachedNets = osNetService.networks().stream()
+ .map(IdEntity::getId).collect(Collectors.toSet());
+ Set<String> osNets = osClient.headers(headerMap).networking()
+ .network().list().stream().map(IdEntity::getId)
+ .collect(Collectors.toSet());
+
+ print(NETWORK_FORMAT, "ID", "Name", "VNI", "Subnets");
+ getDiff(cachedNets, osNets).forEach(netId -> {
+ printNetwork(osNetService.network(netId));
+ if (clear) {
+ osNetService.removeNetwork(netId);
+ }
+ });
+
+ if (show) {
+ print("\nComparing OpenStack security groups");
+ } else if (clear) {
+ print("\nClearing OpenStack security groups");
+ }
+ Set<String> cachedSgs = osSgService.securityGroups().stream()
+ .map(IdEntity::getId).collect(Collectors.toSet());
+ Set<String> osSgs = osClient.headers(headerMap).networking()
+ .securitygroup().list().stream().map(IdEntity::getId)
+ .collect(Collectors.toSet());
+
+ print(SECURITY_GROUP_FORMAT, "ID", "Name");
+ getDiff(cachedSgs, osSgs).forEach(sgId -> {
+ printSecurityGroup(osSgService.securityGroup(sgId));
+ if (clear) {
+ osSgService.removeSecurityGroup(sgId);
+ }
+ });
+ }
+
+ private Set<String> getDiff(Set<String> orig, Set<String> comp) {
+ return orig.stream().filter(id -> !comp.contains(id))
+ .collect(Collectors.toSet());
+ }
+}