Fix: Add inst port to store if host updated with no inst port found

1. Remove purge state CLI and REST API
2. Remove stateful SNAT rules when removing router and ext gateway

Change-Id: I133e9f280690d2da9f65e39cb83618db41b72502
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java
index 82413d4..ba90b7e 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/InstancePortManager.java
@@ -51,10 +51,13 @@
 
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
 import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID;
 import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID;
 import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
@@ -110,6 +113,8 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected OpenstackRouterService routerService;
 
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler"));
     private final InstancePortStoreDelegate
                             delegate = new InternalInstancePortStoreDelegate();
     private final InternalHostListener
@@ -308,22 +313,31 @@
 
             switch (event.type()) {
                 case HOST_UPDATED:
-                    updateInstancePort(instPort);
+                    eventExecutor.execute(() -> processHostUpdate(instPort));
                     break;
                 case HOST_ADDED:
-                    processHostAddition(instPort);
+                    eventExecutor.execute(() -> processHostAddition(instPort));
                     break;
                 case HOST_REMOVED:
-                    processHostRemoval(instPort);
+                    eventExecutor.execute(() -> processHostRemoval(instPort));
                     break;
                 case HOST_MOVED:
-                    processHostMove(event, instPort);
+                    eventExecutor.execute(() -> processHostMove(event, instPort));
                     break;
                 default:
                     break;
             }
         }
 
+        private void processHostUpdate(InstancePort instPort) {
+            InstancePort existingPort = instancePort(instPort.portId());
+            if (existingPort == null) {
+                createInstancePort(instPort);
+            } else {
+                updateInstancePort(instPort);
+            }
+        }
+
         private void processHostAddition(InstancePort instPort) {
             InstancePort existingPort = instancePort(instPort.portId());
             if (existingPort == null) {
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
index c7c0ef3..55b1a41 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -61,6 +61,8 @@
 import org.onosproject.openstacknetworking.api.OpenstackNetwork.Type;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
 import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
 import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
 import org.onosproject.openstacknetworking.api.OpenstackRouterService;
@@ -109,7 +111,6 @@
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SNAT_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_STATEFUL_SNAT_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
-import static org.onosproject.openstacknetworking.api.InstancePort.State.ACTIVE;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.FLAT;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VLAN;
 import static org.onosproject.openstacknetworking.impl.OsgiPropertyConstants.USE_STATEFUL_SNAT;
@@ -144,6 +145,7 @@
     private static final int TP_PORT_MINIMUM_NUM = 1025;
     private static final int TP_PORT_MAXIMUM_NUM = 65535;
     private static final int VM_PREFIX = 32;
+    private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
 
     private static final String MSG_ENABLED = "Enabled ";
     private static final String MSG_DISABLED = "Disabled ";
@@ -206,6 +208,7 @@
     private final InstancePortListener instancePortListener = new InternalInstancePortListener();
     private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
     private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
+    private final OpenstackNetworkListener osNetworkListener = new InternalNetworkEventListener();
 
     private ConsistentMap<Integer, Long> allocatedPortNumMap;
     private DistributedSet<Integer> unUsedPortNumSet;
@@ -236,6 +239,7 @@
         instancePortService.addListener(instancePortListener);
         osRouterService.addListener(osRouterListener);
         osNodeService.addListener(osNodeListener);
+        osNetworkAdminService.addListener(osNetworkListener);
 
         eventExecutor.execute(this::initializeUnusedPortNumSet);
 
@@ -244,6 +248,7 @@
 
     @Deactivate
     protected void deactivate() {
+        osNetworkAdminService.removeListener(osNetworkListener);
         osRouterService.removeListener(osRouterListener);
         osNodeService.removeListener(osNodeListener);
         instancePortService.removeListener(instancePortListener);
@@ -729,8 +734,6 @@
             osNetworkAdminService.deriveExternalPeerRouterMac(exGateway, osRouter, vlanId);
             osRouterService.routerInterfaces(osRouter.getId()).forEach(iface ->
                     setSourceNat(iface, exGateway.isEnableSnat()));
-
-            setStatefulDownstreamRules(osRouter, exGateway.isEnableSnat());
         }
     }
 
@@ -828,39 +831,29 @@
             return;
         }
 
-        String netId = osNetworkAdminService.subnet(routerIface.getSubnetId()).getNetworkId();
-
         Map<OpenstackNode, PortRange> gwPortRangeMap = getAssignedPortsForGateway(
                 ImmutableList.copyOf(osNodeService.nodes(GATEWAY)));
 
-        osNodeService.completeNodes(GATEWAY)
-                .forEach(gwNode -> {
-                    instancePortService.instancePorts(netId)
-                            .stream()
-                            .filter(port -> port.state() == ACTIVE)
-                            .forEach(port -> setGatewayToInstanceDownstreamRule(
-                                    gwNode, port, install));
+        osNodeService.completeNodes(GATEWAY).forEach(gwNode -> {
+            if (install) {
+                PortRange gwPortRange = gwPortRangeMap.get(gwNode);
 
-                    if (install) {
-                        PortRange gwPortRange = gwPortRangeMap.get(gwNode);
+                Map<String, PortRange> netPortRangeMap =
+                        getAssignedPortsForNet(getNetIdByRouterId(routerIface.getId()),
+                                gwPortRange.min(), gwPortRange.max());
 
-                        Map<String, PortRange> netPortRangeMap =
-                                getAssignedPortsForNet(getNetIdByRouterId(routerIface.getId()),
-                                        gwPortRange.min(), gwPortRange.max());
+                PortRange netPortRange = netPortRangeMap.get(osNet.getId());
 
-                        PortRange netPortRange = netPortRangeMap.get(osNet.getId());
-
-                        setStatefulSnatUpstreamRule(gwNode, natAddress,
-                                Long.parseLong(osNet.getProviderSegID()),
-                                externalPeerRouter, netPortRange.min(),
-                                netPortRange.max(), install);
-                    } else {
-                        setStatefulSnatUpstreamRule(gwNode, natAddress,
-                                Long.parseLong(osNet.getProviderSegID()),
-                                externalPeerRouter, 0, 0, install);
-                    }
-
-                });
+                setStatefulSnatUpstreamRule(gwNode, natAddress,
+                        Long.parseLong(osNet.getProviderSegID()),
+                        externalPeerRouter, netPortRange.min(),
+                        netPortRange.max(), install);
+            } else {
+                setStatefulSnatUpstreamRule(gwNode, natAddress,
+                        Long.parseLong(osNet.getProviderSegID()),
+                        externalPeerRouter, 0, 0, install);
+            }
+        });
     }
 
     private void setStatefulDownstreamRules(Router osRouter, boolean install) {
@@ -874,11 +867,15 @@
             return;
         }
 
+        setStatefulDownstreamRules(natAddress, install);
+    }
+
+    private void setStatefulDownstreamRules(IpAddress natAddress, boolean install) {
         osNodeService.completeNodes(GATEWAY)
                 .forEach(gwNode -> {
                     setStatefulSnatDownstreamRule(gwNode.intgBridge(),
                             IpPrefix.valueOf(natAddress, VM_PREFIX), install);
-        });
+                });
     }
 
     private List<String> getNetIdByRouterId(String routerId) {
@@ -1234,6 +1231,67 @@
         }
     }
 
+    private class InternalNetworkEventListener implements OpenstackNetworkListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNetworkEvent event) {
+            Port osPort = event.port();
+            if (osPort == null || osPort.getFixedIps() == null) {
+                return false;
+            }
+
+            return DEVICE_OWNER_ROUTER_GW.equals(osPort.getDeviceOwner()) &&
+                    getStatefulSnatFlag();
+        }
+
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+        }
+
+        @Override
+        public void event(OpenstackNetworkEvent event) {
+            IpAddress ipAddress = externalIp(event.port());
+            switch (event.type()) {
+                case OPENSTACK_PORT_CREATED:
+                case OPENSTACK_PORT_UPDATED:
+                    eventExecutor.execute(() -> processPortCreation(ipAddress));
+                    break;
+                case OPENSTACK_PORT_REMOVED:
+                    eventExecutor.execute(() -> processPortRemoval(ipAddress));
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+
+        private void processPortCreation(IpAddress ipAddress) {
+            if (!isRelevantHelper() || ipAddress == null) {
+                return;
+            }
+
+            setStatefulDownstreamRules(ipAddress, true);
+        }
+
+        private void processPortRemoval(IpAddress ipAddress) {
+            if (!isRelevantHelper() || ipAddress == null) {
+                return;
+            }
+
+            setStatefulDownstreamRules(ipAddress, false);
+        }
+
+        private IpAddress externalIp(Port port) {
+            IP ip = port.getFixedIps().stream().findAny().orElse(null);
+
+            if (ip != null && ip.getIpAddress() != null) {
+                return IpAddress.valueOf(ip.getIpAddress());
+            }
+
+            return null;
+        }
+    }
+
     private class InternalRouterEventListener implements OpenstackRouterListener {
 
         private boolean isRelevantHelper() {
@@ -1255,12 +1313,6 @@
                 case OPENSTACK_ROUTER_INTERFACE_REMOVED:
                     eventExecutor.execute(() -> processRouterIntfRemoval(event));
                     break;
-                case OPENSTACK_ROUTER_GATEWAY_ADDED:
-                    eventExecutor.execute(() -> processRouterGatewayAddition(event));
-                    break;
-                case OPENSTACK_ROUTER_GATEWAY_REMOVED:
-                    eventExecutor.execute(() -> processRouterGatewayRemoval(event));
-                    break;
                 default:
                     break;
             }
@@ -1313,28 +1365,6 @@
 
             routerIfaceRemoved(event.subject(), event.routerIface());
         }
-
-        private void processRouterGatewayAddition(OpenstackRouterEvent event) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            log.debug("Router external gateway {} added",
-                    event.externalGateway().getNetworkId());
-
-            setStatefulDownstreamRules(event.subject(), true);
-        }
-
-        private void processRouterGatewayRemoval(OpenstackRouterEvent event) {
-            if (!isRelevantHelper()) {
-                return;
-            }
-
-            log.debug("Router external gateway {} removed",
-                    event.externalGateway().getNetworkId());
-
-            setStatefulDownstreamRules(event.subject(), false);
-        }
     }
 
     private class InternalPacketProcessor implements PacketProcessor {
@@ -1442,6 +1472,8 @@
                 osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
                     routerIfaceAdded(osRouter, iface);
                 });
+
+                setStatefulDownstreamRules(osRouter, true);
             });
             log.info("Reconfigure routers for {}", osNode.hostname());
         }