Dynamically add or remove gateway node according to GW annotation

Change-Id: Ic7ac799eda0c1d028e934cc1bfd07af34e714e18
(cherry picked from commit 91358d6126e13adcc4e4b8c7bbd46179fb4c2913)
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 26e27ff..96d111c 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
@@ -37,6 +37,8 @@
 import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
 import org.onosproject.kubevirtnetworking.util.RulePopulatorUtil;
 import org.onosproject.kubevirtnode.api.KubevirtNode;
+import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
+import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
 import org.onosproject.kubevirtnode.api.KubevirtNodeService;
 import org.onosproject.net.Device;
 import org.onosproject.net.PortNumber;
@@ -118,15 +120,19 @@
     private ApplicationId appId;
     private NodeId localNodeId;
 
-    private final InternalRouterEventListener kubevirtRouterlistener =
+    private final InternalRouterEventListener kubevirtRouterListener =
             new InternalRouterEventListener();
 
+    private final InternalNodeEventListener kubevirtNodeListener =
+            new InternalNodeEventListener();
+
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(KUBEVIRT_NETWORKING_APP_ID);
         localNodeId = clusterService.getLocalNode().id();
         leadershipService.runForLeadership(appId.name());
-        kubevirtRouterService.addListener(kubevirtRouterlistener);
+        kubevirtRouterService.addListener(kubevirtRouterListener);
+        kubevirtNodeService.addListener(kubevirtNodeListener);
 
         log.info("Started");
     }
@@ -134,7 +140,8 @@
     @Deactivate
     protected void deactivate() {
         leadershipService.withdraw(appId.name());
-        kubevirtRouterService.removeListener(kubevirtRouterlistener);
+        kubevirtRouterService.removeListener(kubevirtRouterListener);
+        kubevirtNodeService.removeListener(kubevirtNodeListener);
 
         eventExecutor.shutdown();
 
@@ -204,6 +211,7 @@
                 PRE_FLAT_TABLE,
                 install);
     }
+
     private KubevirtPort getKubevirtPort(KubevirtFloatingIp floatingIp) {
 
         return kubevirtPortService.ports().stream()
@@ -352,7 +360,6 @@
             return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
         }
 
-
         @Override
         public void event(KubevirtRouterEvent event) {
             switch (event.type()) {
@@ -385,4 +392,44 @@
             setFloatingIpRules(router, floatingIp, false);
         }
     }
+
+    private class InternalNodeEventListener implements KubevirtNodeListener {
+
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+        }
+
+        @Override
+        public void event(KubevirtNodeEvent event) {
+            switch (event.type()) {
+                case KUBEVIRT_NODE_COMPLETE:
+                    eventExecutor.execute(() -> processNodeCompletion(event.subject()));
+                    break;
+                case KUBEVIRT_NODE_REMOVED:
+                case KUBEVIRT_NODE_INCOMPLETE:
+                default:
+                    break;
+            }
+        }
+
+        private void processNodeCompletion(KubevirtNode node) {
+            if (!isRelevantHelper()) {
+                return;
+            }
+
+            kubevirtRouterService.floatingIps().forEach(fip -> {
+                KubevirtRouter router = kubevirtRouterService.router(fip.routerName());
+                if (router != null) {
+                    KubevirtNode electedGw = gatewayNodeForSpecifiedRouter(kubevirtNodeService, router);
+                    if (electedGw == null) {
+                        return;
+                    }
+
+                    if (electedGw.hostname().equals(node.hostname())) {
+                        setFloatingIpRules(router, fip, true);
+                    }
+                }
+            });
+        }
+    }
 }
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
index f1b41bc..e682029 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
@@ -347,7 +347,7 @@
             return;
         }
 
-        setDefaulGatewayRuleToWorkerNodeTunBridge(router, network, electedGw.intgBridge(), node, true);
+        setDefaultGatewayRuleToWorkerNodeTunBridge(router, network, electedGw.intgBridge(), node, true);
     }
 
     private void setDefaultRulesForTenantNetwork(KubevirtNode node, KubevirtNetwork network) {
@@ -428,7 +428,7 @@
                     setGatewayIcmpRuleForTenantInternalNetwork(router, network, TENANT_ICMP_TABLE,
                             electedGateway.intgBridge(),
                             network.tenantDeviceId(node.hostname()), install);
-                    setDefaulGatewayRuleToWorkerNodeTunBridge(router, network,
+                    setDefaultGatewayRuleToWorkerNodeTunBridge(router, network,
                             electedGateway.intgBridge(), node, install);
                 });
                 break;
@@ -446,11 +446,11 @@
         }
     }
 
-    private void setDefaulGatewayRuleToWorkerNodeTunBridge(KubevirtRouter router,
-                                                           KubevirtNetwork network,
-                                                           DeviceId gwDeviceId,
-                                                           KubevirtNode workerNode,
-                                                           boolean install) {
+    private void setDefaultGatewayRuleToWorkerNodeTunBridge(KubevirtRouter router,
+                                                            KubevirtNetwork network,
+                                                            DeviceId gwDeviceId,
+                                                            KubevirtNode workerNode,
+                                                            boolean install) {
         MacAddress routerMacAddress = getRouterMacAddress(router);
 
         if (routerMacAddress == null) {
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingSnatHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingSnatHandler.java
index 264017a..20fa0cb 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingSnatHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingSnatHandler.java
@@ -42,6 +42,8 @@
 import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
 import org.onosproject.kubevirtnetworking.util.RulePopulatorUtil;
 import org.onosproject.kubevirtnode.api.KubevirtNode;
+import org.onosproject.kubevirtnode.api.KubevirtNodeEvent;
+import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
 import org.onosproject.kubevirtnode.api.KubevirtNodeService;
 import org.onosproject.net.Device;
 import org.onosproject.net.PortNumber;
@@ -140,6 +142,9 @@
     private final InternalRouterEventListener kubevirtRouterlistener =
             new InternalRouterEventListener();
 
+    private final InternalNodeEventListener kubevirtNodeListener =
+            new InternalNodeEventListener();
+
     private ApplicationId appId;
     private NodeId localNodeId;
 
@@ -151,6 +156,7 @@
 
         kubevirtPortService.addListener(kubevirtPortListener);
         kubevirtRouterService.addListener(kubevirtRouterlistener);
+        kubevirtNodeService.addListener(kubevirtNodeListener);
 
         log.info("Started");
     }
@@ -158,6 +164,7 @@
     @Deactivate
     protected void deactivate() {
         leadershipService.withdraw(appId.name());
+        kubevirtNodeService.removeListener(kubevirtNodeListener);
         kubevirtPortService.removeListener(kubevirtPortListener);
         kubevirtRouterService.removeListener(kubevirtRouterlistener);
 
@@ -720,4 +727,12 @@
             }
         }
     }
+
+    private class InternalNodeEventListener implements KubevirtNodeListener {
+
+        @Override
+        public void event(KubevirtNodeEvent event) {
+
+        }
+    }
 }
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java
index f224dbc..b9c0880 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtNodeHandler.java
@@ -601,13 +601,21 @@
                 log.info("Creating physnet bridge {}", bridgeName);
                 log.info("Creating patch ports for physnet {}", bridgeName);
             } else {
-                // in case physical bridge exists, but patch port is missing on br-int,
-                // we will add patch port to connect br-int with physical bridge
+                // in case physical bridge exists, but patch port is missing,
+                // we will add patch port to connect br-physnet with physical bridge
                 if (!hasPhyPatchPort(node, patchPortName)) {
                     createPhysicalPatchPorts(node, pi);
 
                     log.info("Creating patch ports for physnet {}", bridgeName);
                 }
+
+                // in case physical bridge exists, but physnet interface is missing,
+                // we will add the physnet interface to connect br-physnet to the external
+                if (!hasPhyIntf(node, pi.intf())) {
+                    attachPhysicalPort(node, pi);
+
+                    log.info("Attaching external ports for physnet {}", bridgeName);
+                }
             }
         });
     }
@@ -979,6 +987,9 @@
                         if (!isRelevantHelper()) {
                             return;
                         }
+                        if (event.subject() == null) {
+                            return;
+                        }
                         bootstrapNode(event.subject());
                     });
                     break;
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/KubevirtNodeWatcher.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/KubevirtNodeWatcher.java
index 4ac793d..96662c4 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/KubevirtNodeWatcher.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/KubevirtNodeWatcher.java
@@ -44,6 +44,7 @@
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.GATEWAY;
+import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.MASTER;
 import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER;
 import static org.onosproject.kubevirtnode.api.KubevirtNodeService.APP_ID;
 import static org.onosproject.kubevirtnode.api.KubevirtNodeState.INIT;
@@ -203,18 +204,29 @@
             log.trace("Process node {} updating event from API server.",
                     node.getMetadata().getName());
 
+            KubevirtNode original = buildKubevirtNode(node);
             KubevirtNode existing = kubevirtNodeAdminService.node(node.getMetadata().getName());
 
-            if (existing != null) {
-                KubevirtNode kubevirtNode = buildKubevirtNode(node);
+            // if a master node is annotated as a gateway node, we simply add
+            // the node into the cluster
+            if (original.type() == GATEWAY && existing == null) {
+                kubevirtNodeAdminService.createNode(original);
+            }
 
+            // if a gateway annotation removed from the master node, we simply remove
+            // the node from the cluster
+            if (original.type() == MASTER && existing != null && existing.type() == GATEWAY) {
+                kubevirtNodeAdminService.removeNode(original.hostname());
+            }
+
+            if (existing != null) {
                 // we update the kubevirt node and re-run bootstrapping,
-                // only if the updated node has different phyInts and data IP
+                // if the updated node has different phyInts and data IP
                 // this means we assume that the node's hostname, type and mgmt IP
                 // are immutable
-                if (!kubevirtNode.phyIntfs().equals(existing.phyIntfs()) ||
-                        !kubevirtNode.dataIp().equals(existing.dataIp())) {
-                    kubevirtNodeAdminService.updateNode(kubevirtNode.updateState(INIT));
+                if (!original.phyIntfs().equals(existing.phyIntfs()) ||
+                        !original.dataIp().equals(existing.dataIp())) {
+                    kubevirtNodeAdminService.updateNode(original.updateState(INIT));
                 }
             }
         }