Refactor external peer router store, fix NPE due to MAC is not ready

Change-Id: Id0381d9d1d7e0888dfbf1fc20acdd44d0a303e4c
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
index 5815b7a..84c9b21 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
@@ -32,6 +32,7 @@
     private final Port port;
     private final Subnet subnet;
     private final String securityGroupId;
+    private final ExternalPeerRouter peerRouter;
 
     public enum Type {
         /**
@@ -97,11 +98,32 @@
         /**
          * Signifies that the OpenStack security group rule is removed from a specific port.
          */
-        OPENSTACK_PORT_SECURITY_GROUP_REMOVED
+        OPENSTACK_PORT_SECURITY_GROUP_REMOVED,
+
+        /**
+         * Signifies that the external peer router is created.
+         */
+        EXTERNAL_PEER_ROUTER_CREATED,
+
+        /**
+         * Signifies that the external peer router is updated.
+         */
+        EXTERNAL_PEER_ROUTER_UPDATED,
+
+        /**
+         * Signifies that the external peer router MAC is updated.
+         */
+        EXTERNAL_PEER_ROUTER_MAC_UPDATED,
+
+        /**
+         * Signifies that the external peer router is removed.
+         */
+        EXTERNAL_PEER_ROUTER_REMOVED,
     }
 
     /**
      * Creates an event of a given type for the specified network and the current time.
+     *
      * @param type    openstack network event type
      * @param network openstack network
      */
@@ -110,6 +132,7 @@
         this.port = null;
         this.subnet = null;
         this.securityGroupId = null;
+        this.peerRouter = null;
     }
 
     /**
@@ -125,6 +148,7 @@
         this.port = port;
         this.subnet = null;
         this.securityGroupId = null;
+        this.peerRouter = null;
     }
 
     /**
@@ -140,6 +164,7 @@
         this.port = null;
         this.subnet = subnet;
         this.securityGroupId = null;
+        this.peerRouter = null;
     }
 
     /**
@@ -154,6 +179,21 @@
         this.port = port;
         this.subnet = null;
         this.securityGroupId = securityGroupId;
+        this.peerRouter = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified external peer router.
+     *
+     * @param type openstack network event type
+     * @param peerRouter external peer router
+     */
+    public OpenstackNetworkEvent(Type type, ExternalPeerRouter peerRouter) {
+        super(type, null);
+        this.port = null;
+        this.subnet = null;
+        this.securityGroupId = null;
+        this.peerRouter = peerRouter;
     }
 
     /**
@@ -175,6 +215,15 @@
     }
 
     /**
+     * Returns the external peer router.
+     *
+     * @return external peer router; null if the event is not peer router specific
+     */
+    public ExternalPeerRouter peerRouter() {
+        return peerRouter;
+    }
+
+    /**
      * Returns the security group rule IDs updated.
      *
      * @return openstack security group
@@ -195,6 +244,7 @@
                 .add("port", port)
                 .add("subnet", subnet)
                 .add("security group", securityGroupId())
+                .add("external peer router", peerRouter)
                 .toString();
     }
 }
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
index a584044..f4a5c4c 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
@@ -140,6 +140,43 @@
     Set<Port> ports();
 
     /**
+     * Returns external peer router with the given IP address.
+     *
+     * @param ipAddress IP address
+     * @return external peer router
+     */
+    ExternalPeerRouter externalPeerRouter(String ipAddress);
+
+    /**
+     * Returns all external peer routers.
+     *
+     * @return set of external peer routers
+     */
+    Set<ExternalPeerRouter> externalPeerRouters();
+
+    /**
+     * Creates a new external peer router.
+     *
+     * @param peerRouter the new external peer router
+     */
+    void createExternalPeerRouter(ExternalPeerRouter peerRouter);
+
+    /**
+     * Updates an existing external peer router.
+     *
+     * @param peerRouter the updated external peer router
+     */
+    void updateExternalPeerRouter(ExternalPeerRouter peerRouter);
+
+    /**
+     * Removes an existing external peer router with the given IP address.
+     *
+     * @param ipAddress IP address
+     * @return removed external peer router
+     */
+    ExternalPeerRouter removeExternalPeerRouter(String ipAddress);
+
+    /**
      * Removes the existing network and ports.
      */
     void clear();
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
index c02d964..2cebd2f 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
@@ -17,9 +17,13 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
@@ -54,6 +58,7 @@
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Objects;
@@ -64,6 +69,10 @@
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_CREATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_MAC_UPDATED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_REMOVED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.EXTERNAL_PEER_ROUTER_UPDATED;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_CREATED;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_REMOVED;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_NETWORK_UPDATED;
@@ -112,6 +121,16 @@
             .register(LinkedHashMap.class)
             .build();
 
+    private static final KryoNamespace
+            SERIALIZER_EXTERNAL_PEER_ROUTER_MAP = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(ExternalPeerRouter.class)
+            .register(DefaultExternalPeerRouter.class)
+            .register(MacAddress.class)
+            .register(IpAddress.class)
+            .register(VlanId.class)
+            .build();
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
 
@@ -127,7 +146,10 @@
                         subnetMapListener = new OpenstackSubnetMapListener();
     private final MapEventListener<String, Port>
                         portMapListener = new OpenstackPortMapListener();
+    private final MapEventListener<String, ExternalPeerRouter>
+                        peerRouterListener = new ExternalPeerRouterMapListener();
 
+    private ConsistentMap<String, ExternalPeerRouter> externalPeerRouterStore;
     private ConsistentMap<String, Network> osNetworkStore;
     private ConsistentMap<String, Subnet> osSubnetStore;
     private ConsistentMap<String, Port> osPortStore;
@@ -157,6 +179,13 @@
                 .build();
         osPortStore.addListener(portMapListener);
 
+        externalPeerRouterStore = storageService.<String, ExternalPeerRouter>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_EXTERNAL_PEER_ROUTER_MAP))
+                .withName("external-routermap")
+                .withApplicationId(appId)
+                .build();
+        externalPeerRouterStore.addListener(peerRouterListener);
+
         log.info("Started");
     }
 
@@ -165,6 +194,7 @@
         osNetworkStore.removeListener(networkMapListener);
         osSubnetStore.removeListener(subnetMapListener);
         osPortStore.removeListener(portMapListener);
+        externalPeerRouterStore.removeListener(peerRouterListener);
         eventExecutor.shutdown();
 
         log.info("Stopped");
@@ -273,10 +303,48 @@
     }
 
     @Override
+    public ExternalPeerRouter externalPeerRouter(String ipAddress) {
+        return externalPeerRouterStore.asJavaMap().get(ipAddress);
+    }
+
+    @Override
+    public Set<ExternalPeerRouter> externalPeerRouters() {
+        return new HashSet<>(externalPeerRouterStore.asJavaMap().values());
+    }
+
+    @Override
+    public void createExternalPeerRouter(ExternalPeerRouter peerRouter) {
+        externalPeerRouterStore.compute(
+                peerRouter.ipAddress().toString(), (id, existing) -> {
+            final String error = peerRouter.ipAddress().toString() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return peerRouter;
+        });
+    }
+
+    @Override
+    public void updateExternalPeerRouter(ExternalPeerRouter peerRouter) {
+        externalPeerRouterStore.compute(
+                peerRouter.ipAddress().toString(), (id, existing) -> {
+            final String error = peerRouter.ipAddress() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return peerRouter;
+        });
+    }
+
+    @Override
+    public ExternalPeerRouter removeExternalPeerRouter(String ipAddress) {
+        Versioned<ExternalPeerRouter> peerRouter =
+                externalPeerRouterStore.remove(ipAddress);
+        return peerRouter == null ? null : peerRouter.value();
+    }
+
+    @Override
     public void clear() {
         osPortStore.clear();
         osSubnetStore.clear();
         osNetworkStore.clear();
+        externalPeerRouterStore.clear();
     }
 
     private class OpenstackNetworkMapListener
@@ -367,6 +435,62 @@
         }
     }
 
+    private class ExternalPeerRouterMapListener
+            implements MapEventListener<String, ExternalPeerRouter> {
+
+        @Override
+        public void event(MapEvent<String, ExternalPeerRouter> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    eventExecutor.execute(() -> processPeerRouterUpdate(event));
+                    break;
+                case INSERT:
+                    eventExecutor.execute(() -> processPeerRouterInsertion(event));
+                    break;
+                case REMOVE:
+                    eventExecutor.execute(() -> processPeerRouterRemoval(event));
+                    break;
+                default:
+                    log.error("Unsupported external peer router event type");
+                    break;
+            }
+        }
+
+        private void processPeerRouterUpdate(
+                MapEvent<String, ExternalPeerRouter> event) {
+            log.debug("External peer router updated");
+            notifyDelegate(new OpenstackNetworkEvent(
+                    EXTERNAL_PEER_ROUTER_UPDATED, event.newValue().value()));
+
+            processPeerRouterMacUpdated(event);
+        }
+
+        private void processPeerRouterInsertion(
+                MapEvent<String, ExternalPeerRouter> event) {
+            log.debug("External peer router inserted");
+            notifyDelegate(new OpenstackNetworkEvent(
+                    EXTERNAL_PEER_ROUTER_CREATED, event.newValue().value()));
+        }
+
+        private void processPeerRouterRemoval(
+                MapEvent<String, ExternalPeerRouter> event) {
+            log.debug("External peer router removed");
+            notifyDelegate(new OpenstackNetworkEvent(
+                    EXTERNAL_PEER_ROUTER_REMOVED, event.oldValue().value()));
+        }
+
+        private void processPeerRouterMacUpdated(
+                MapEvent<String, ExternalPeerRouter> event) {
+            ExternalPeerRouter oldPeerRouter = event.oldValue().value();
+            ExternalPeerRouter newPeerRouter = event.newValue().value();
+
+            if (!Objects.equals(oldPeerRouter.macAddress(), newPeerRouter.macAddress())) {
+                notifyDelegate(new OpenstackNetworkEvent(
+                        EXTERNAL_PEER_ROUTER_MAC_UPDATED, newPeerRouter));
+            }
+        }
+     }
+
     private class OpenstackPortMapListener implements MapEventListener<String, Port> {
 
         @Override
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
index d3e36bf..c79f12c 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -132,6 +132,12 @@
                                 "OpenStack port ID cannot be null";
     private static final String ERR_NULL_PORT_NET_ID =
                                 "OpenStack port network ID cannot be null";
+    private static final String ERR_NULL_PEER_ROUTER =
+                                "External peer router cannot be null";
+    private static final String ERR_NULL_PEER_ROUTER_IP =
+                                "External peer router IP cannot be null";
+    private static final String ERR_NULL_PEER_ROUTER_MAC =
+                                "External peer router MAC cannot be null";
 
     private static final String ERR_IN_USE = " still in use";
 
@@ -159,20 +165,9 @@
     private final OpenstackNetworkStoreDelegate
                                 delegate = new InternalNetworkStoreDelegate();
 
-    private ConsistentMap<String, ExternalPeerRouter> externalPeerRouterMap;
     private ConsistentMap<String, OpenstackNetwork> augmentedNetworkMap;
 
     private static final KryoNamespace
-            SERIALIZER_EXTERNAL_PEER_ROUTER_MAP = KryoNamespace.newBuilder()
-            .register(KryoNamespaces.API)
-            .register(ExternalPeerRouter.class)
-            .register(DefaultExternalPeerRouter.class)
-            .register(MacAddress.class)
-            .register(IpAddress.class)
-            .register(VlanId.class)
-            .build();
-
-    private static final KryoNamespace
             SERIALIZER_AUGMENTED_NETWORK_MAP = KryoNamespace.newBuilder()
             .register(KryoNamespaces.API)
             .register(OpenstackNetwork.Type.class)
@@ -182,7 +177,6 @@
 
     private ApplicationId appId;
 
-
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
@@ -190,12 +184,6 @@
         osNetworkStore.setDelegate(delegate);
         log.info("Started");
 
-        externalPeerRouterMap = storageService.<String, ExternalPeerRouter>consistentMapBuilder()
-                .withSerializer(Serializer.using(SERIALIZER_EXTERNAL_PEER_ROUTER_MAP))
-                .withName("external-routermap")
-                .withApplicationId(appId)
-                .build();
-
         augmentedNetworkMap = storageService.<String, OpenstackNetwork>consistentMapBuilder()
                 .withSerializer(Serializer.using(SERIALIZER_AUGMENTED_NETWORK_MAP))
                 .withName("augmented-networkmap")
@@ -340,7 +328,6 @@
     public void clear() {
         osNetworkStore.clear();
         augmentedNetworkMap.clear();
-        externalPeerRouterMap.clear();
     }
 
     @Override
@@ -481,10 +468,7 @@
 
     @Override
     public ExternalPeerRouter externalPeerRouter(IpAddress ipAddress) {
-        if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
-            return externalPeerRouterMap.get(ipAddress.toString()).value();
-        }
-        return null;
+        return osNetworkStore.externalPeerRouter(ipAddress.toString());
     }
 
     @Override
@@ -495,11 +479,7 @@
             return null;
         }
 
-        if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
-            return externalPeerRouterMap.get(ipAddress.toString()).value();
-        } else {
-            return null;
-        }
+        return externalPeerRouter(ipAddress);
     }
 
     @Override
@@ -516,9 +496,11 @@
             return;
         }
 
-        if (externalPeerRouterMap.containsKey(targetIp.toString()) &&
-                !externalPeerRouterMap.get(
-                        targetIp.toString()).value().macAddress().equals(MacAddress.NONE)) {
+        ExternalPeerRouter peerRouter = osNetworkStore.externalPeerRouter(targetIp.toString());
+
+        // if peer router's MAC address is not NONE, we assume that peer router's
+        // MAC address has been derived
+        if (peerRouter != null && !peerRouter.macAddress().equals(MacAddress.NONE)) {
             return;
         }
 
@@ -555,13 +537,12 @@
                 treatment,
                 ByteBuffer.wrap(ethRequest.serialize())));
 
-        externalPeerRouterMap.put(targetIp.toString(),
-                DefaultExternalPeerRouter.builder()
-                        .ipAddress(targetIp)
-                        .macAddress(MacAddress.NONE)
-                        .vlanId(vlanId)
-                        .build());
-
+        ExternalPeerRouter derivedRouter = DefaultExternalPeerRouter.builder()
+                .ipAddress(targetIp)
+                .macAddress(MacAddress.NONE)
+                .vlanId(vlanId)
+                .build();
+        osNetworkStore.createExternalPeerRouter(derivedRouter);
         log.info("Initializes external peer router map with peer router IP {}",
                                                             targetIp.toString());
     }
@@ -573,59 +554,45 @@
         }
 
         IpAddress targetIp = getExternalPeerRouterIp(externalGateway);
-        if (targetIp == null) {
-            return;
-        }
-
-        if (externalPeerRouterMap.containsKey(targetIp.toString())) {
-            externalPeerRouterMap.remove(targetIp.toString());
-        }
+        deleteExternalPeerRouter(targetIp.toString());
     }
 
     @Override
     public void deleteExternalPeerRouter(String ipAddress) {
-        if (ipAddress == null) {
-            return;
-        }
-
-        if (externalPeerRouterMap.containsKey(ipAddress)) {
-            externalPeerRouterMap.remove(ipAddress);
-        }
-
+        osNetworkStore.removeExternalPeerRouter(ipAddress);
     }
 
     @Override
     public void updateExternalPeerRouterMac(IpAddress ipAddress,
                                             MacAddress macAddress) {
-        try {
-            externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
-                    DefaultExternalPeerRouter.builder()
-                            .ipAddress(ipAddress)
-                            .macAddress(macAddress)
-                            .vlanId(existing.vlanId())
-                            .build());
-
-            log.info("Updated external peer router map {}",
-                    externalPeerRouterMap.get(ipAddress.toString()).value().toString());
-        } catch (Exception e) {
-            log.error("Exception occurred because of {}", e);
-        }
+        updateExternalPeerRouter(ipAddress, macAddress, null);
     }
 
     @Override
     public void updateExternalPeerRouter(IpAddress ipAddress,
                                          MacAddress macAddress,
                                          VlanId vlanId) {
-        try {
-            externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
-                    DefaultExternalPeerRouter.builder()
-                            .ipAddress(ipAddress)
-                            .macAddress(macAddress)
-                            .vlanId(vlanId)
-                            .build());
+        checkNotNull(ipAddress, ERR_NULL_PEER_ROUTER_IP);
 
-        } catch (Exception e) {
-            log.error("Exception occurred because of {}", e);
+        ExternalPeerRouter existingPeerRouter =
+                osNetworkStore.externalPeerRouter(ipAddress.toString());
+
+        if (existingPeerRouter != null) {
+            ExternalPeerRouter.Builder urBuilder = DefaultExternalPeerRouter.builder()
+                    .ipAddress(ipAddress);
+
+            if (macAddress == null) {
+                urBuilder.macAddress(existingPeerRouter.macAddress());
+            } else {
+                urBuilder.macAddress(macAddress);
+            }
+
+            if (vlanId == null) {
+                urBuilder.vlanId(existingPeerRouter.vlanId());
+            } else {
+                urBuilder.vlanId(vlanId);
+            }
+            osNetworkStore.updateExternalPeerRouter(urBuilder.build());
         }
     }
 
@@ -636,31 +603,25 @@
         if (ipAddress == null) {
             return null;
         }
-        if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
-            return externalPeerRouterMap.get(ipAddress.toString()).value().macAddress();
-        } else {
+
+        ExternalPeerRouter peerRouter =
+                osNetworkStore.externalPeerRouter(ipAddress.toString());
+
+        if (peerRouter == null) {
             throw new NoSuchElementException();
+        } else {
+            return peerRouter.macAddress();
         }
     }
 
     @Override
     public void updateExternalPeerRouterVlan(IpAddress ipAddress, VlanId vlanId) {
-
-        try {
-            externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
-                    DefaultExternalPeerRouter.builder()
-                            .ipAddress(ipAddress)
-                            .macAddress(existing.macAddress())
-                            .vlanId(vlanId).build());
-
-        } catch (Exception e) {
-            log.error("Exception occurred because of {}", e);
-        }
+        updateExternalPeerRouter(ipAddress, null, vlanId);
     }
 
     @Override
     public Set<ExternalPeerRouter> externalPeerRouters() {
-        return ImmutableSet.copyOf(externalPeerRouterMap.asJavaMap().values());
+        return ImmutableSet.copyOf(osNetworkStore.externalPeerRouters());
     }
 
     @Override
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 a9eef09..ce04d69 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
@@ -80,10 +80,10 @@
 import static org.onosproject.openstacknetworking.api.InstancePort.State.REMOVE_PENDING;
 import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_ENDED;
 import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED;
+import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.GENEVE;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.GRE;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VLAN;
 import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.VXLAN;
-import static org.onosproject.openstacknetworking.api.OpenstackNetwork.Type.GENEVE;
 import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.OPENSTACK_PORT_PRE_REMOVE;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
 import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.externalPeerRouterForNetwork;
@@ -186,8 +186,11 @@
         log.info("Stopped");
     }
 
-    private void setFloatingIpRules(NetFloatingIP floatingIp, InstancePort instPort,
-                                    OpenstackNode gateway, boolean install) {
+    private void setFloatingIpRules(NetFloatingIP floatingIp,
+                                    InstancePort instPort,
+                                    OpenstackNode gateway,
+                                    ExternalPeerRouter peerRouter,
+                                    boolean install) {
 
         if (instPort == null) {
             log.debug("No instance port found");
@@ -196,11 +199,14 @@
 
         Network osNet = osNetworkService.network(instPort.networkId());
 
-        ExternalPeerRouter externalPeerRouter = externalPeerRouterForNetwork(osNet,
-                osNetworkService, osRouterAdminService);
+        ExternalPeerRouter externalPeerRouter = peerRouter != null ? peerRouter :
+                externalPeerRouterForNetwork(osNet, osNetworkService, osRouterAdminService);
+
         if (externalPeerRouter == null) {
-            final String errorFormat = ERR_FLOW + NO_EXT_PEER_ROUTER_MSG;
-            throw new IllegalStateException(errorFormat);
+            log.warn("External peer router is not ready for now, " +
+                     "floating IP rules will be installed/uninstalled " +
+                     "when external peer router is available...");
+            return;
         }
 
         if (install) {
@@ -544,7 +550,7 @@
 
         // set floating IP rules only if the port is associated to a VM
         if (!Strings.isNullOrEmpty(instPort.deviceId().toString())) {
-            setFloatingIpRules(osFip, instPort, null, true);
+            setFloatingIpRules(osFip, instPort, null, null, true);
             processGratuitousArpPacket(osFip, instPort);
 
         }
@@ -556,12 +562,11 @@
 
         Network osNet = osNetworkService.network(instancePort.networkId());
 
-
         OpenstackNode selectedGw = getGwByInstancePort(gws, instancePort);
         ExternalPeerRouter externalPeerRouter =
                 externalPeerRouterForNetwork(osNet, osNetworkService, osRouterAdminService);
         if (externalPeerRouter == null) {
-            log.error("Failed to process GARP packet for floating ip {}, because " +
+            log.error("Failed to process GARP packet for floating ip {}, because ",
                                                             NO_EXT_PEER_ROUTER_MSG);
             return;
         }
@@ -582,7 +587,7 @@
 
         // set floating IP rules only if the port is associated to a VM
         if (!Strings.isNullOrEmpty(instPort.deviceId().toString())) {
-            setFloatingIpRules(osFip, instPort, null, false);
+            setFloatingIpRules(osFip, instPort, null, null, false);
         }
     }
 
@@ -734,7 +739,7 @@
                     continue;
                 }
 
-                setFloatingIpRules(fip, instPort, event.subject(), true);
+                setFloatingIpRules(fip, instPort, event.subject(), null, true);
             }
         }
 
@@ -834,7 +839,7 @@
                         .filter(f -> f.getPortId() != null)
                         .filter(f -> f.getPortId().equals(instPort.portId()))
                         .forEach(f -> setFloatingIpRules(f,
-                                instPort, null, true));
+                                instPort, null, null, true));
             }
         }
 
@@ -939,19 +944,37 @@
 
         @Override
         public void event(OpenstackNetworkEvent event) {
-            if (event.type() == OPENSTACK_PORT_PRE_REMOVE) {
-                eventExecutor.execute(() -> {
 
-                    if (!isRelevantHelper()) {
-                        return;
-                    }
-
-                    processPortPreRemoval(event);
-                });
+            switch (event.type()) {
+                case OPENSTACK_PORT_PRE_REMOVE:
+                    eventExecutor.execute(() -> processPortPreRemoval(event));
+                    break;
+                case EXTERNAL_PEER_ROUTER_MAC_UPDATED:
+                    eventExecutor.execute(() -> processExternalPeerRouterMacUpdate(event));
+                    break;
+                default:
+                    break;
             }
         }
 
+        private void processExternalPeerRouterMacUpdate(OpenstackNetworkEvent event) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
+            instancePortService.instancePorts().forEach(instPort ->
+                    osRouterAdminService.floatingIps().stream()
+                    .filter(f -> f.getPortId() != null)
+                    .filter(f -> f.getPortId().equals(instPort.portId()))
+                    .forEach(f -> setFloatingIpRules(f,
+                            instPort, null, event.peerRouter(), true)));
+        }
+
         private void processPortPreRemoval(OpenstackNetworkEvent event) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
             InstancePort instPort = instancePortService.instancePort(
                                                         event.port().getId());
             if (instPort == null) {
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 31882c9..ef1b4d0 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -952,7 +952,8 @@
             return null;
         }
 
-        Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
+        Subnet subnet = osNetworkService.subnets(network.getId())
+                .stream().findAny().orElse(null);
 
         if (subnet == null) {
             return null;