Fix: install additional rules on SG and GW to realize VM migration

Change-Id: I221a5c0e8c53273c70f49f606121cd95b8529d87
(cherry picked from commit ef487901f479d6218199e95ee41fd57ac1cc7359)
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java
index 53fefc9..2797f32 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtFloatingIpHandler.java
@@ -30,6 +30,8 @@
 import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
 import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
 import org.onosproject.kubevirtnetworking.api.KubevirtPort;
+import org.onosproject.kubevirtnetworking.api.KubevirtPortEvent;
+import org.onosproject.kubevirtnetworking.api.KubevirtPortListener;
 import org.onosproject.kubevirtnetworking.api.KubevirtPortService;
 import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
 import org.onosproject.kubevirtnetworking.api.KubevirtRouterEvent;
@@ -133,6 +135,9 @@
     private final InternalNodeListener kubevirtNodeListener =
             new InternalNodeListener();
 
+    private final InternalPortListener kubevirtPortListener =
+            new InternalPortListener();
+
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
@@ -140,6 +145,7 @@
         leadershipService.runForLeadership(appId.name());
         kubevirtRouterService.addListener(kubevirtRouterListener);
         kubevirtNodeService.addListener(kubevirtNodeListener);
+        kubevirtPortService.addListener(kubevirtPortListener);
 
         log.info("Started");
     }
@@ -149,6 +155,7 @@
         leadershipService.withdraw(appId.name());
         kubevirtRouterService.removeListener(kubevirtRouterListener);
         kubevirtNodeService.removeListener(kubevirtNodeListener);
+        kubevirtPortService.removeListener(kubevirtPortListener);
 
         eventExecutor.shutdown();
 
@@ -220,6 +227,14 @@
                 .findAny().orElse(null);
     }
 
+    private KubevirtFloatingIp getFloatingIpByKubevirtPort(KubevirtPort port) {
+
+        return kubevirtRouterService.floatingIps().stream()
+                .filter(fip -> port.ipAddress().equals(fip.fixedIp()))
+                .filter(fip -> port.vmName().equals(fip.vmName()))
+                .findAny().orElse(null);
+    }
+
     private void setFloatingIpUpstreamRules(KubevirtRouter router,
                                             KubevirtFloatingIp floatingIp,
                                             KubevirtPort port,
@@ -493,4 +508,53 @@
             }
         }
     }
+
+    private class InternalPortListener implements KubevirtPortListener {
+
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+        }
+
+        @Override
+        public void event(KubevirtPortEvent event) {
+            switch (event.type()) {
+                case KUBEVIRT_PORT_MIGRATED:
+                    eventExecutor.execute(() -> processPortMigration(event.subject()));
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+
+        private void processPortMigration(KubevirtPort port) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
+            KubevirtFloatingIp fip = getFloatingIpByKubevirtPort(port);
+            if (fip == null) {
+                return;
+            }
+
+            KubevirtRouter router = kubevirtRouterService.router(fip.routerName());
+            if (router == null) {
+                log.warn("The router {} is not found", fip.routerName());
+                return;
+            }
+
+            String gateway = router.electedGateway();
+            KubevirtNode node = kubevirtNodeService.node(gateway);
+
+            if (node == null) {
+                log.warn("The gateway node {} is not found", gateway);
+                return;
+            }
+
+            setFloatingIpRulesForFip(router, fip, node, true);
+
+            log.info("Configure floating IP {} on gateway {}",
+                            fip.floatingIp().toString(), node.hostname());
+        }
+    }
 }
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java
index 2c35b74..b51af25 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java
@@ -51,11 +51,8 @@
 import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
 import org.onosproject.kubevirtnode.api.KubevirtNodeService;
 import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -199,9 +196,6 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected KubevirtSecurityGroupService securityGroupService;
 
-    private final DeviceListener deviceListener =
-            new InternalDeviceListener();
-
     private final KubevirtPortListener portListener =
             new InternalKubevirtPortListener();
     private final KubevirtSecurityGroupListener securityGroupListener =
@@ -221,7 +215,6 @@
     protected void activate() {
         appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
         localNodeId = clusterService.getLocalNode().id();
-        deviceService.addListener(deviceListener);
         securityGroupService.addListener(securityGroupListener);
         portService.addListener(portListener);
         networkService.addListener(networkListener);
@@ -238,7 +231,6 @@
         configService.unregisterProperties(getClass(), false);
         nodeService.removeListener(nodeListener);
         networkService.removeListener(networkListener);
-        deviceService.removeListener(deviceListener);
         eventExecutor.shutdown();
 
         log.info("Stopped");
@@ -278,7 +270,7 @@
     }
 
     private void initializeConnTrackTable(DeviceId deviceId, int ctTable,
-                                            int forwardTable, boolean install) {
+                                          int forwardTable, boolean install) {
 
         // table={ACL_INGRESS_TABLE(44)},ip,ct_state=-trk, actions=ct(table:{ACL_CT_TABLE(45)})
         long ctState = computeCtStateFlag(false, false, false);
@@ -304,7 +296,7 @@
     }
 
     private void initializeTenantAclTable(KubevirtNetwork network,
-                                            DeviceId deviceId, boolean install) {
+                                          DeviceId deviceId, boolean install) {
         // FIXME: in bridge initialization phase, some patch ports may not be
         // available until they are created, we wait for a while ensure all
         // patch ports are created via network bootstrap
@@ -313,7 +305,7 @@
                 break;
             } else {
                 log.info("Wait for tenant patch ports creation for device {} " +
-                         "and network {}", deviceId, network.networkId());
+                        "and network {}", deviceId, network.networkId());
                 waitFor(5);
             }
         }
@@ -355,7 +347,7 @@
     }
 
     private void initializeEgressTable(DeviceId deviceId, int egressTable,
-                                        int forwardTable, boolean install) {
+                                       int forwardTable, boolean install) {
         if (install) {
             flowRuleService.setUpTableMissEntry(deviceId, TENANT_ACL_EGRESS_TABLE);
         } else {
@@ -534,21 +526,21 @@
         });
 
         TrafficSelector tSelector = DefaultTrafficSelector.builder()
-                        .matchEthType(Ethernet.TYPE_IPV4)
-                        .matchEthDst(port.macAddress())
-                        .matchIPDst(IpPrefix.valueOf(port.ipAddress(), 32))
-                        .build();
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchEthDst(port.macAddress())
+                .matchIPDst(IpPrefix.valueOf(port.ipAddress(), 32))
+                .build();
         TrafficTreatment tTreatment = DefaultTrafficTreatment.builder()
-                        .transition(TENANT_ACL_INGRESS_TABLE)
-                        .build();
+                .transition(TENANT_ACL_INGRESS_TABLE)
+                .build();
 
         flowRuleService.setRule(appId,
-                    deviceId,
-                    tSelector,
-                    tTreatment,
-                    PRIORITY_ACL_RULE,
-                    TENANT_ACL_RECIRC_TABLE,
-                    install);
+                deviceId,
+                tSelector,
+                tTreatment,
+                PRIORITY_ACL_RULE,
+                TENANT_ACL_RECIRC_TABLE,
+                install);
     }
 
     /**
@@ -920,6 +912,10 @@
                 case KUBEVIRT_PORT_DEVICE_ADDED:
                     eventExecutor.execute(() -> processPortDeviceAdded(event));
                     break;
+                case KUBEVIRT_PORT_MIGRATED:
+                    eventExecutor.execute(() -> processPortDeviceAdded(event));
+                    eventExecutor.execute(() -> processOldPortRemove(event));
+                    break;
                 default:
                     // do nothing for the other events
                     break;
@@ -932,7 +928,7 @@
             }
 
             if (event.securityGroupId() == null ||
-                securityGroupService.securityGroup(event.securityGroupId()) == null) {
+                    securityGroupService.securityGroup(event.securityGroupId()) == null) {
                 return;
             }
 
@@ -952,7 +948,7 @@
             }
 
             if (event.securityGroupId() == null ||
-                securityGroupService.securityGroup(event.securityGroupId()) == null) {
+                    securityGroupService.securityGroup(event.securityGroupId()) == null) {
                 return;
             }
 
@@ -978,7 +974,23 @@
                     updateSecurityGroupRule(port, sgRule, false);
                 });
                 log.info("Removed security group {} from port {}",
-                                        sgStr, event.subject().macAddress());
+                        sgStr, event.subject().macAddress());
+            }
+        }
+
+        private void processOldPortRemove(KubevirtPortEvent event) {
+            if (!isRelevantHelper(event)) {
+                return;
+            }
+
+            KubevirtPort oldPort = event.oldSubject();
+            for (String sgStr : oldPort.securityGroups()) {
+                KubevirtSecurityGroup sg = securityGroupService.securityGroup(sgStr);
+                sg.rules().forEach(sgRule -> {
+                    updateSecurityGroupRule(oldPort, sgRule, false);
+                });
+                log.info("Removed security group {} from port {}",
+                        sgStr, event.subject().macAddress());
             }
         }
 
@@ -1120,53 +1132,6 @@
         }
     }
 
-    /**
-     * An internal OVS listener. This listener is used for listening the network
-     * facing events from OVS device. If a new OVS device is detected, discovered,
-     * ONOS tries to install device related rules into the target kubernetes node.
-     */
-    private class InternalDeviceListener implements DeviceListener {
-
-        @Override
-        public boolean isRelevant(DeviceEvent event) {
-            return event.subject().type() == Device.Type.SWITCH;
-        }
-
-        private boolean isRelevantHelper() {
-            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
-        }
-
-        @Override
-        public void event(DeviceEvent event) {
-            Device device = event.subject();
-
-            switch (event.type()) {
-                case DEVICE_AVAILABILITY_CHANGED:
-                case DEVICE_ADDED:
-                    eventExecutor.execute(() -> {
-                        if (!isRelevantHelper()) {
-                            return;
-                        }
-
-                        KubevirtNode node = nodeService.node(device.id());
-
-                        if (node == null) {
-                            return;
-                        }
-
-                        if (deviceService.isAvailable(device.id())) {
-                            resetSecurityGroupRulesByNode(node);
-                        }
-                    });
-                    break;
-                case DEVICE_REMOVED:
-                default:
-                    // do nothing
-                    break;
-            }
-        }
-    }
-
     private void resetSecurityGroupRulesByNode(KubevirtNode node) {
         if (getUseSecurityGroupFlag()) {
             initializeProviderPipeline(node, true);
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtVmiWatcher.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtVmiWatcher.java
index 414fa9e..4e1121a 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtVmiWatcher.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtVmiWatcher.java
@@ -226,7 +226,7 @@
 
                 if (existing != null) {
                     if (port.deviceId() != null) {
-                        if (existing.deviceId() == null || existing.deviceId() != port.deviceId()) {
+                        if (existing.deviceId() == null || !existing.deviceId().equals(port.deviceId())) {
                             KubevirtPort updated = existing.updateDeviceId(port.deviceId());
                             // internally we update device ID of kubevirt port
                             portAdminService.updatePort(updated);