Fix: remove FixedIpAddress associated with FloatingIp when purge VM

Change-Id: Ie48a0ec2694302e900755cccece9181f52386bc9
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 e938a90..3667d7c 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
@@ -144,10 +144,10 @@
     protected OpenstackNetworkService osNetworkService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ComponentConfigService configService;
+    protected HostService hostService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
+    protected ComponentConfigService configService;
 
     // TODO: need to find a way to unify aprMode and gatewayMac variables with
     // that in SwitchingArpHandler
@@ -163,7 +163,8 @@
 
     private ApplicationId appId;
     private NodeId localNodeId;
-    private Map<String, String> floatingIpMacMap = Maps.newConcurrentMap();
+    private Map<String, MacAddress> floatingIpMacMap = Maps.newConcurrentMap();
+    private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
 
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
@@ -175,8 +176,8 @@
         appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
         configService.registerProperties(getClass());
         localNodeId = clusterService.getLocalNode().id();
-        osRouterService.addListener(osRouterListener);
         hostService.addListener(hostListener);
+        osRouterService.addListener(osRouterListener);
         osNodeService.addListener(osNodeListener);
         leadershipService.runForLeadership(appId.name());
         packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
@@ -342,13 +343,13 @@
                 .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
     }
 
-    // FIXME: need to find a way to invoke this method during node initialization
     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(), port.getMacAddress());
+                    floatingIpMacMap.put(f.getFloatingIpAddress(),
+                            MacAddress.valueOf(port.getMacAddress()));
                 }
             }
         });
@@ -429,23 +430,30 @@
                 return;
             }
 
-            String macString;
+            MacAddress targetMac;
+            InstancePort instPort;
 
             if (install) {
                 if (fip.getPortId() != null) {
-                    macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
-                    floatingIpMacMap.put(fip.getFloatingIpAddress(), macString);
+                    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 {
-                macString = floatingIpMacMap.get(fip.getFloatingIpAddress());
+                targetMac = floatingIpMacMap.get(fip.getFloatingIpAddress());
             }
 
-            MacAddress targetMac = MacAddress.valueOf(macString);
-            InstancePort instPort = instancePortService.instancePort(targetMac);
+            instPort = instancePortService.instancePort(targetMac);
+
+            // in VM purge case, we will have null instance port
+            if (instPort == null) {
+                instPort = removedPorts.get(targetMac);
+                removedPorts.remove(targetMac);
+            }
 
             OpenstackNode gw = getGwByInstancePort(gateways, instPort);
 
@@ -652,7 +660,7 @@
             InstancePort instPort = HostBasedInstancePort.of(event.subject());
             switch (event.type()) {
                 case HOST_REMOVED:
-                    removeArpRuleByInstancePort(instPort);
+                    storeTempInstPort(instPort);
                     break;
                 case HOST_UPDATED:
                 case HOST_ADDED:
@@ -661,7 +669,7 @@
             }
         }
 
-        private void removeArpRuleByInstancePort(InstancePort port) {
+        private void storeTempInstPort(InstancePort port) {
             Set<NetFloatingIP> ips = osRouterService.floatingIps();
             for (NetFloatingIP fip : ips) {
                 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
@@ -671,10 +679,7 @@
                     continue;
                 }
                 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
-                    eventExecutor.execute(() ->
-                        setFloatingIpArpRule(fip,
-                                osNodeService.completeNodes(GATEWAY), false)
-                    );
+                    removedPorts.put(port.macAddress(), port);
                 }
             }
         }
@@ -704,6 +709,9 @@
                     setDefaultArpRule(osNode, true);
                     setFloatingIpArpRuleForGateway(osNode, true);
 
+                    // initialize FloatingIp to Mac map
+                    initFloatingIpMacMap();
+
                     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 6493fc8..3976e5c 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
@@ -49,9 +49,9 @@
 import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
 import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
-import org.onosproject.openstacknetworking.api.OpenstackRouterService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeEvent;
 import org.onosproject.openstacknode.api.OpenstackNodeListener;
@@ -64,6 +64,7 @@
 import org.openstack4j.model.network.Router;
 import org.openstack4j.model.network.RouterInterface;
 import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -119,7 +120,7 @@
     protected InstancePortService instancePortService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackRouterService osRouterService;
+    protected OpenstackRouterAdminService osRouterAdminService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackNetworkService osNetworkService;
@@ -143,7 +144,7 @@
         localNodeId = clusterService.getLocalNode().id();
         leadershipService.runForLeadership(appId.name());
         hostService.addListener(hostListener);
-        osRouterService.addListener(floatingIpLisener);
+        osRouterAdminService.addListener(floatingIpLisener);
         osNodeService.addListener(osNodeListener);
 
         log.info("Started");
@@ -153,7 +154,7 @@
     protected void deactivate() {
         hostService.removeListener(hostListener);
         osNodeService.removeListener(osNodeListener);
-        osRouterService.removeListener(floatingIpLisener);
+        osRouterAdminService.removeListener(floatingIpLisener);
         leadershipService.withdraw(appId.name());
         eventExecutor.shutdown();
 
@@ -564,14 +565,14 @@
             return null;
         }
 
-        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+        RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
                 .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
                 .findAny().orElse(null);
         if (osRouterIface == null) {
             return null;
         }
 
-        Router osRouter = osRouterService.router(osRouterIface.getId());
+        Router osRouter = osRouterAdminService.router(osRouterIface.getId());
         if (osRouter == null) {
             return null;
         }
@@ -692,7 +693,7 @@
             switch (event.type()) {
                 case OPENSTACK_NODE_COMPLETE:
                     eventExecutor.execute(() -> {
-                        for (NetFloatingIP fip : osRouterService.floatingIps()) {
+                        for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
                             if (Strings.isNullOrEmpty(fip.getPortId())) {
                                 continue;
                             }
@@ -707,7 +708,7 @@
                     break;
                 case OPENSTACK_NODE_INCOMPLETE:
                     eventExecutor.execute(() -> {
-                        for (NetFloatingIP fip : osRouterService.floatingIps()) {
+                        for (NetFloatingIP fip : osRouterAdminService.floatingIps()) {
                             if (Strings.isNullOrEmpty(fip.getPortId())) {
                                 continue;
                             }
@@ -781,7 +782,7 @@
         }
 
         private void storeTempInstPort(InstancePort port) {
-            Set<NetFloatingIP> ips = osRouterService.floatingIps();
+            Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
             for (NetFloatingIP fip : ips) {
                 if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
                     continue;
@@ -791,11 +792,13 @@
                 }
                 if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
                     removedPorts.put(port.macAddress(), port);
-                    eventExecutor.execute(() -> {
-                        disassociateFloatingIp(fip, port.portId());
-                        log.info("Disassociated floating IP {}:{}",
-                                fip.getFloatingIpAddress(), fip.getFixedIpAddress());
-                    });
+                    NeutronFloatingIP neutronFip = (NeutronFloatingIP) fip;
+                    // invalidate bound fixed IP and port
+                    neutronFip.setFixedIpAddress(null);
+                    neutronFip.setPortId(null);
+                    osRouterAdminService.updateFloatingIp(neutronFip);
+                    log.info("Updated floating IP {}, due to host removal",
+                            neutronFip.getFloatingIpAddress());
                 }
             }
         }