Purge internal map variables by leveraging persistent store

Change-Id: If347de18803a6f84a220e1ffdee26b9e1c28634b
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java
index 5247174..83c0741 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java
@@ -136,13 +136,13 @@
     InstancePort updateState(State newState);
 
     /**
-     * Returns new instance port instance with given prev data.
+     * Returns new instance port instance with the given prev location data.
      *
      * @param oldDeviceId       old device ID
      * @param oldPortNumber     old port number
      * @return updated instance port
      */
-    InstancePort updatePrevData(DeviceId oldDeviceId, PortNumber oldPortNumber);
+    InstancePort updatePrevLocation(DeviceId oldDeviceId, PortNumber oldPortNumber);
 
     /**
      * Builder of new instance port.
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultInstancePort.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultInstancePort.java
index 0a53885..23dc684 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultInstancePort.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DefaultInstancePort.java
@@ -179,8 +179,8 @@
     }
 
     @Override
-    public InstancePort updatePrevData(DeviceId oldDeviceId,
-                                       PortNumber oldPortNumber) {
+    public InstancePort updatePrevLocation(DeviceId oldDeviceId,
+                                           PortNumber oldPortNumber) {
         return new Builder()
                 .networkId(networkId)
                 .portId(portId)
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 7ad2b02..2383e62 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
@@ -247,8 +247,8 @@
                         createInstancePort(instPort);
                     } else {
                         // the instance was restarted
-                        if (existingPort.state() == InstancePort.State.INACTIVE) {
-                            updateInstancePort(existingPort.updateState(ACTIVE));
+                        if (existingPort.state() == INACTIVE) {
+                            updateInstancePort(instPort);
                         }
                     }
                     break;
@@ -276,7 +276,7 @@
 
                         if (location != null) {
                             InstancePort updated = instPort.updateState(MIGRATED);
-                            updateInstancePort(updated.updatePrevData(
+                            updateInstancePort(updated.updatePrevLocation(
                                         location.deviceId(), location.port()));
                         }
                     }
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 1f73566..f1440c1 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -17,7 +17,6 @@
 
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -69,13 +68,11 @@
 import org.openstack4j.model.network.ExternalGateway;
 import org.openstack4j.model.network.IP;
 import org.openstack4j.model.network.NetFloatingIP;
-import org.openstack4j.model.network.Port;
 import org.openstack4j.model.network.Router;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
@@ -158,11 +155,6 @@
     private ApplicationId appId;
     private NodeId localNodeId;
 
-    private final Map<String, MacAddress> floatingIpMacMap = Maps.newConcurrentMap();
-
-    // TODO: pendingInstPortIds should be purged later
-    private final Map<String, NetFloatingIP> pendingInstPortIds = Maps.newConcurrentMap();
-
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
 
@@ -336,31 +328,6 @@
                 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
     }
 
-    private void initFloatingIpMacMap() {
-        osRouterService.floatingIps().forEach(f -> {
-            if (f.getPortId() != null && f.getFloatingIpAddress() != null) {
-                Port port = osNetworkAdminService.port(f.getPortId());
-                if (port != null && port.getMacAddress() != null) {
-                    floatingIpMacMap.put(f.getFloatingIpAddress(),
-                            MacAddress.valueOf(port.getMacAddress()));
-                }
-            }
-        });
-    }
-
-    private void initPendingInstPorts() {
-        osRouterService.floatingIps().forEach(f -> {
-            if (f.getPortId() != null) {
-                Port port = osNetworkAdminService.port(f.getPortId());
-                if (port != null) {
-                    if (!Strings.isNullOrEmpty(port.getDeviceId())) {
-                        pendingInstPortIds.put(f.getPortId(), f);
-                    }
-                }
-            }
-        });
-    }
-
     /**
      * Installs static ARP rules used in ARP BROAD_CAST mode.
      *
@@ -463,24 +430,14 @@
                 return;
             }
 
-            MacAddress targetMac;
-            InstancePort instPort;
-
-            if (install) {
-                if (fip.getPortId() != null) {
-                    String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
-                    targetMac = MacAddress.valueOf(macString);
-                    floatingIpMacMap.put(fip.getFloatingIpAddress(), targetMac);
-                } else {
-                    log.trace("Unknown target ARP request for {}, ignore it",
-                            fip.getFloatingIpAddress());
-                    return;
-                }
-            } else {
-                targetMac = floatingIpMacMap.get(fip.getFloatingIpAddress());
+            if (fip.getPortId() == null) {
+                log.trace("Unknown target ARP request for {}, ignore it",
+                                                    fip.getFloatingIpAddress());
+                return;
             }
 
-            instPort = instancePortService.instancePort(targetMac);
+            InstancePort instPort = instancePortService.instancePort(fip.getPortId());
+            MacAddress targetMac = instPort.macAddress();
 
             OpenstackNode gw = getGwByInstancePort(gateways, instPort);
 
@@ -690,13 +647,10 @@
             switch (event.type()) {
                 case OPENSTACK_INSTANCE_PORT_DETECTED:
 
-                    if (pendingInstPortIds.containsKey(instPort.portId())) {
-                        Set<OpenstackNode> completedGws =
-                                osNodeService.completeNodes(GATEWAY);
-                        setFloatingIpArpRule(pendingInstPortIds.get(instPort.portId()),
-                                completedGws, true);
-                        pendingInstPortIds.remove(instPort.portId());
-                    }
+                    osRouterService.floatingIps().stream()
+                            .filter(f -> f.getPortId() != null)
+                            .filter(f -> f.getPortId().equals(instPort.portId()))
+                            .forEach(f -> setFloatingIpArpRule(f, gateways, true));
 
                     break;
                 case OPENSTACK_INSTANCE_MIGRATION_STARTED:
@@ -759,13 +713,6 @@
                 case OPENSTACK_NODE_COMPLETE:
                     setDefaultArpRule(osNode, true);
                     setFloatingIpArpRuleForGateway(osNode, true);
-
-                    // initialize FloatingIp to Mac map
-                    initFloatingIpMacMap();
-
-                    // initialize pendingInstPorts
-                    initPendingInstPorts();
-
                     break;
                 case OPENSTACK_NODE_INCOMPLETE:
                     setDefaultArpRule(osNode, false);
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
index 5be8b87..2c20f6a 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -140,7 +140,6 @@
     private final InstancePortListener instPortListener = new InternalInstancePortListener();
 
     private Map<String, Port> terminatedOsPorts = Maps.newConcurrentMap();
-    private Map<String, NetFloatingIP> pendingInstPortIds = Maps.newConcurrentMap();
 
     private ApplicationId appId;
     private NodeId localNodeId;
@@ -606,9 +605,6 @@
         if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
 
             if (instancePortService.instancePort(osPort.getId()) == null) {
-                log.info("Try to associate the fip {} with a terminated VM",
-                        osFip.getFloatingIpAddress());
-                pendingInstPortIds.put(osPort.getId(), osFip);
                 return;
             }
 
@@ -634,15 +630,7 @@
         if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
 
             if (instancePortService.instancePort(osPort.getId()) == null) {
-
-                // in case there is pending instance port, we simply remove that
-                // port, otherwise, we directly go with rule removal
-                if (pendingInstPortIds.containsKey(osPort.getId())) {
-                    log.info("Try to disassociate the fip {} with a terminated VM",
-                            osFip.getFloatingIpAddress());
-                    pendingInstPortIds.remove(osPort.getId());
-                    return;
-                }
+                return;
             }
 
             setFloatingIpRules(osFip, osPort, null, false);
@@ -742,12 +730,7 @@
                                 continue;
                             }
 
-                            // Note that, at OPENSTACK_INSTANCE_PORT_DETECTED phase,
-                            // we will install floating IP related rules by
-                            // referring to the key and value stored in pending map
-                            if (!Strings.isNullOrEmpty(osPort.getDeviceId()) &&
-                                    instancePortService.instancePort(fip.getPortId()) == null) {
-                                pendingInstPortIds.put(fip.getPortId(), fip);
+                            if (instancePortService.instancePort(fip.getPortId()) == null) {
                                 continue;
                             }
 
@@ -848,11 +831,10 @@
 
                         Port port = osNetworkService.port(portId);
 
-                        if (pendingInstPortIds.containsKey(portId) && port != null) {
-                            setFloatingIpRules(pendingInstPortIds.get(portId),
-                                    port, null, true);
-                            pendingInstPortIds.remove(portId);
-                        }
+                        osRouterAdminService.floatingIps().stream()
+                                .filter(f -> f.getPortId() != null)
+                                .filter(f -> f.getPortId().equals(instPort.portId()))
+                                .forEach(f -> setFloatingIpRules(f, port, null, true));
                     }
 
                     break;
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
index 75cdea1..5519f37 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
@@ -611,6 +611,7 @@
             switch (event.type()) {
                 case OPENSTACK_INSTANCE_PORT_DETECTED:
                 case OPENSTACK_INSTANCE_PORT_UPDATED:
+                case OPENSTACK_INSTANCE_RESTARTED:
                     log.info("SwitchingHandler: Instance port detected MAC:{} IP:{}",
                                                         instPort.macAddress(),
                                                         instPort.ipAddress());
@@ -618,6 +619,13 @@
                     eventExecutor.execute(() -> instPortDetected(instPort));
 
                     break;
+                case OPENSTACK_INSTANCE_TERMINATED:
+                    log.info("SwitchingHandler: Instance port terminated MAC:{} IP:{}",
+                                                        instPort.macAddress(),
+                                                        instPort.ipAddress());
+                    eventExecutor.execute(() -> removeVportRules(instPort));
+
+                    break;
                 case OPENSTACK_INSTANCE_PORT_VANISHED:
                     log.info("SwitchingHandler: Instance port vanished MAC:{} IP:{}",
                                                         instPort.macAddress(),
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
index b2af7b3..de9da5d 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
@@ -75,7 +75,8 @@
 
 @Service
 @Component(immediate = true)
-public final class OpenstackSwitchingHostProvider extends AbstractProvider implements HostProvider {
+public final class OpenstackSwitchingHostProvider
+        extends AbstractProvider implements HostProvider {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -108,9 +109,11 @@
     protected InstancePortAdminService instancePortAdminService;
 
     private final ExecutorService deviceEventExecutor =
-            Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
-    private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
-    private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
+            Executors.newSingleThreadExecutor(groupedThreads(this.getClass().getSimpleName(), "device-event"));
+    private final InternalDeviceListener internalDeviceListener =
+            new InternalDeviceListener();
+    private final InternalOpenstackNodeListener internalNodeListener =
+            new InternalOpenstackNodeListener();
 
     private HostProviderService hostProvider;
 
@@ -185,8 +188,8 @@
         // TODO: we need to find a way to bind multiple ports from multiple
         // openstack networks into one host sooner or later
         Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
-                                    .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
-                                    .collect(Collectors.toSet());
+                .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
+                .collect(Collectors.toSet());
 
         // connect point is the combination of switch ID with port number where
         // the host is attached to
@@ -196,33 +199,45 @@
 
         // we check whether the host already attached to some locations
         Host host = hostService.getHost(hostId);
+
+        // build host annotations to include a set of meta info from neutron
+        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+                .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
+                .set(ANNOTATION_PORT_ID, osPort.getId())
+                .set(ANNOTATION_CREATE_TIME, String.valueOf(createTime));
+
+        // FLAT does not require segment ID
+        if (osNet.getNetworkType() != NetworkType.FLAT) {
+            annotations.set(ANNOTATION_SEGMENT_ID, osNet.getProviderSegID());
+        }
+
+        // build host description object
+        HostDescription hostDesc = new DefaultHostDescription(
+                mac,
+                VlanId.NONE,
+                new HostLocation(connectPoint, createTime),
+                fixedIps,
+                annotations.build());
+
         if (host != null) {
             Set<HostLocation> locations = host.locations().stream()
                     .filter(l -> l.deviceId().equals(connectPoint.deviceId()))
                     .filter(l -> l.port().equals(connectPoint.port()))
                     .collect(Collectors.toSet());
+
+            // newly added location is not in the existing location list,
+            // therefore, we simply add this into the location list
             if (locations.size() == 0) {
                 hostProvider.addLocationToHost(hostId,
-                                    new HostLocation(connectPoint, createTime));
+                        new HostLocation(connectPoint, createTime));
+            }
+
+            // newly added location is in the existing location list,
+            // the hostDetected method invocation in turn triggers host Update event
+            if (locations.size() == 1) {
+                hostProvider.hostDetected(hostId, hostDesc, false);
             }
         } else {
-
-            DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                    .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
-                    .set(ANNOTATION_PORT_ID, osPort.getId())
-                    .set(ANNOTATION_CREATE_TIME, String.valueOf(createTime));
-
-            // FLAT does not require segment ID
-            if (osNet.getNetworkType() != NetworkType.FLAT) {
-                annotations.set(ANNOTATION_SEGMENT_ID, osNet.getProviderSegID());
-            }
-
-            HostDescription hostDesc = new DefaultHostDescription(
-                    mac,
-                    VlanId.NONE,
-                    new HostLocation(connectPoint, createTime),
-                    fixedIps,
-                    annotations.build());
             hostProvider.hostDetected(hostId, hostDesc, false);
         }
     }
@@ -388,8 +403,8 @@
                             port.isEnabled())
                     .forEach(port -> {
                         log.debug("Instance port {} is detected from {}",
-                                  port.annotations().value(PORT_NAME),
-                                  osNode.hostname());
+                                port.annotations().value(PORT_NAME),
+                                osNode.hostname());
                         processPortAdded(port,
                                 deviceService.getDevice(osNode.intgBridge()));
                     });
@@ -415,7 +430,7 @@
                     .forEach(host -> {
                         log.info("Remove stale host {}", host.id());
                         hostProvider.hostVanished(host.id());
-                });
+                    });
         }
     }
 }
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
index 53cba29..2d17534 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandlerTest.java
@@ -248,7 +248,7 @@
         }
 
         @Override
-        public InstancePort updatePrevData(DeviceId oldDeviceId, PortNumber oldPortNumber) {
+        public InstancePort updatePrevLocation(DeviceId oldDeviceId, PortNumber oldPortNumber) {
             return null;
         }
     }
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandlerTest.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandlerTest.java
index 530d947..fed268f 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandlerTest.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandlerTest.java
@@ -570,7 +570,7 @@
         }
 
         @Override
-        public InstancePort updatePrevData(DeviceId oldDeviceId, PortNumber oldPortNumber) {
+        public InstancePort updatePrevLocation(DeviceId oldDeviceId, PortNumber oldPortNumber) {
             return null;
         }
     }