Initializes gateway type of kubevirt node.

Change-Id: Ib48f54f60fa82b5fe35f0077687653712fd22803
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNode.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNode.java
index 1d3512a..2b0c8f8 100644
--- a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNode.java
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNode.java
@@ -52,6 +52,7 @@
     private final IpAddress dataIp;
     private final KubevirtNodeState state;
     private final Collection<KubevirtPhyInterface> phyIntfs;
+    private final String gatewayBridgeName;
 
     /**
      * A default constructor of kubevirt node.
@@ -65,12 +66,14 @@
      * @param dataIp            data IP address
      * @param state             node state
      * @param phyIntfs          physical interfaces
+     * @param gatewayBridgeName  gateway bridge name
      */
     protected DefaultKubevirtNode(String clusterName, String hostname, Type type,
                                   DeviceId intgBridge, DeviceId tunBridge,
                                   IpAddress managementIp, IpAddress dataIp,
                                   KubevirtNodeState state,
-                                  Collection<KubevirtPhyInterface> phyIntfs) {
+                                  Collection<KubevirtPhyInterface> phyIntfs,
+                                  String gatewayBridgeName) {
         this.clusterName = clusterName;
         this.hostname = hostname;
         this.type = type;
@@ -80,6 +83,7 @@
         this.dataIp = dataIp;
         this.state = state;
         this.phyIntfs = phyIntfs;
+        this.gatewayBridgeName = gatewayBridgeName;
     }
 
     @Override
@@ -139,6 +143,7 @@
                 .dataIp(dataIp)
                 .state(newState)
                 .phyIntfs(phyIntfs)
+                .gatewayBridgeName(gatewayBridgeName)
                 .build();
     }
 
@@ -154,6 +159,7 @@
                 .dataIp(dataIp)
                 .state(state)
                 .phyIntfs(phyIntfs)
+                .gatewayBridgeName(gatewayBridgeName)
                 .build();
     }
 
@@ -169,6 +175,7 @@
                 .dataIp(dataIp)
                 .state(state)
                 .phyIntfs(phyIntfs)
+                .gatewayBridgeName(gatewayBridgeName)
                 .build();
     }
 
@@ -196,6 +203,11 @@
         return tunnelPort(GENEVE);
     }
 
+    @Override
+    public String gatewayBridgeName() {
+        return gatewayBridgeName;
+    }
+
     private PortNumber tunnelPort(String tunnelType) {
         if (dataIp == null) {
             return null;
@@ -233,7 +245,8 @@
                 .managementIp(node.managementIp())
                 .dataIp(node.dataIp())
                 .state(node.state())
-                .phyIntfs(node.phyIntfs());
+                .phyIntfs(node.phyIntfs())
+                .gatewayBridgeName(node.gatewayBridgeName());
     }
 
     @Override
@@ -272,6 +285,7 @@
                 .add("dataIp", dataIp)
                 .add("state", state)
                 .add("phyIntfs", phyIntfs)
+                .add("gatewayBridgeName", gatewayBridgeName)
                 .toString();
     }
 
@@ -286,6 +300,7 @@
         private IpAddress dataIp;
         private KubevirtNodeState state;
         private Collection<KubevirtPhyInterface> phyIntfs;
+        private String gatewayBridgeName;
 
         // private constructor not intended to use from external
         private Builder() {
@@ -311,7 +326,8 @@
                     managementIp,
                     dataIp,
                     state,
-                    phyIntfs
+                    phyIntfs,
+                    gatewayBridgeName
             );
         }
 
@@ -368,5 +384,11 @@
             this.state = state;
             return this;
         }
+
+        @Override
+        public Builder gatewayBridgeName(String gatewayBridgeName) {
+            this.gatewayBridgeName = gatewayBridgeName;
+            return this;
+        }
     }
 }
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNode.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNode.java
index 382c662..90d3400 100644
--- a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNode.java
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNode.java
@@ -39,6 +39,11 @@
          * Signifies that this is a kubevirt worker node.
          */
         WORKER,
+
+        /**
+         * Signifies that this is a gateway which is running on master node.
+         */
+        GATEWAY,
     }
 
     /**
@@ -157,6 +162,13 @@
     PortNumber genevePort();
 
     /**
+     * Returns the name of the gateway bridge.
+     *
+     * @return gateway bridge name
+     */
+    String gatewayBridgeName();
+
+    /**
      * Builder of new node entity.
      */
     interface Builder {
@@ -238,5 +250,13 @@
          * @return kubevirt node builder
          */
         KubevirtNode.Builder state(KubevirtNodeState state);
+
+        /**
+         * Returns kubevirt node builder with supplied gateway bridge name.
+         *
+         * @param gatewayBridgeName gateway bridge name
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder gatewayBridgeName(String gatewayBridgeName);
     }
 }
diff --git a/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNodeTest.java b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNodeTest.java
index b2537fd..d66528c 100644
--- a/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNodeTest.java
+++ b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNodeTest.java
@@ -26,9 +26,10 @@
 
 import static junit.framework.TestCase.assertEquals;
 import static org.onosproject.kubevirtnode.api.KubevirtNodeState.DEVICE_CREATED;
+import static org.onosproject.kubevirtnode.api.KubevirtNodeState.ON_BOARDED;
 
 /**
- * Unit tests for DefaultKubevirtNode.
+ * Unit tests for Default Kubevirt Node.
  */
 public final class DefaultKubevirtNodeTest extends KubevirtNodeTest {
 
@@ -36,8 +37,10 @@
 
     private static final String HOSTNAME_1 = "hostname_1";
     private static final String HOSTNAME_2 = "hostname_2";
+    private static final String HOSTNAME_3 = "hostname_3";
     private static final Device DEVICE_1 = createDevice(1);
     private static final Device DEVICE_2 = createDevice(2);
+    private static final Device DEVICE_3 = createDevice(3);
 
     private static final IpAddress MANAGEMENT_IP = IpAddress.valueOf("10.10.10.10");
     private static final IpAddress DATA_IP = IpAddress.valueOf("20.20.20.20");
@@ -45,6 +48,8 @@
     private static final String PHY_INTF_NETWORK = "mgmtnetwork";
     private static final String PHY_INTF_NAME = "eth3";
 
+    private static final String GATEWAY_BRIDGE_NAME = "gateway";
+
     private static final List<KubevirtPhyInterface> PHY_INTFS = initPhyIntfs();
 
     private KubevirtNode refNode;
@@ -84,6 +89,7 @@
                 .tunBridge(DEVICE_1.id())
                 .state(KubevirtNodeState.COMPLETE)
                 .phyIntfs(PHY_INTFS)
+                .gatewayBridgeName(GATEWAY_BRIDGE_NAME)
                 .build();
     }
 
@@ -174,6 +180,25 @@
                 .build();
     }
 
+    /**
+     * Checks building a gateway node.
+     */
+    @Test
+    public void testGatewayBuild() {
+        KubevirtNode node = DefaultKubevirtNode.builder()
+                .hostname(HOSTNAME_3)
+                .type(KubevirtNode.Type.GATEWAY)
+                .intgBridge(DEVICE_2.id())
+                .gatewayBridgeName(GATEWAY_BRIDGE_NAME)
+                .managementIp(MANAGEMENT_IP)
+                .dataIp(TEST_IP)
+                .state(ON_BOARDED)
+                .build();
+
+        assertEquals(node.intgBridge(), DEVICE_2.id());
+        assertEquals(node.gatewayBridgeName(), GATEWAY_BRIDGE_NAME);
+    }
+
     private static List<KubevirtPhyInterface> initPhyIntfs() {
         KubevirtPhyInterface phyIntf = DefaultKubevirtPhyInterface.builder()
                 .intf(PHY_INTF_NAME)
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodec.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodec.java
index 4a7e77c..f1ea1c9 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodec.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodec.java
@@ -51,6 +51,7 @@
     private static final String TUNNEL_BRIDGE = "tunnelBridge";
     private static final String STATE = "state";
     private static final String PHYSICAL_INTERFACES = "phyIntfs";
+    private static final String GATEWAY_BRIDGE_NAME = "gatewayBridgeName";
 
     private static final String MISSING_MESSAGE = " is required in OpenstackNode";
 
@@ -90,6 +91,11 @@
             result.set(PHYSICAL_INTERFACES, phyIntfs);
         }
 
+        // serialize external bridge if exist
+        if (node.gatewayBridgeName() != null) {
+            result.put(GATEWAY_BRIDGE_NAME, node.gatewayBridgeName());
+        }
+
         return result;
     }
 
@@ -140,6 +146,11 @@
         }
         nodeBuilder.phyIntfs(phyIntfs);
 
+        JsonNode externalBridgeJson = json.get(GATEWAY_BRIDGE_NAME);
+        if (externalBridgeJson != null) {
+            nodeBuilder.gatewayBridgeName(externalBridgeJson.asText());
+        }
+
         log.trace("node is {}", nodeBuilder.build().toString());
 
         return nodeBuilder.build();
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtApiConfigHandler.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtApiConfigHandler.java
index 7208cd6..27866d4 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtApiConfigHandler.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/impl/DefaultKubevirtApiConfigHandler.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.kubevirtnode.impl;
 
-import io.fabric8.kubernetes.api.model.Node;
 import io.fabric8.kubernetes.client.KubernetesClient;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
@@ -26,7 +25,6 @@
 import org.onosproject.kubevirtnode.api.KubevirtApiConfigAdminService;
 import org.onosproject.kubevirtnode.api.KubevirtApiConfigEvent;
 import org.onosproject.kubevirtnode.api.KubevirtApiConfigListener;
-import org.onosproject.kubevirtnode.api.KubevirtNode;
 import org.onosproject.kubevirtnode.api.KubevirtNodeAdminService;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -42,8 +40,6 @@
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.kubevirtnode.api.KubevirtApiConfig.State.CONNECTED;
 import static org.onosproject.kubevirtnode.api.KubevirtApiConfigService.APP_ID;
-import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER;
-import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.buildKubevirtNode;
 import static org.onosproject.kubevirtnode.util.KubevirtNodeUtil.k8sClient;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -110,26 +106,6 @@
         return k8sClient != null && k8sClient.getApiVersion() != null;
     }
 
-    private void bootstrapKubevirtNodes(KubevirtApiConfig config) {
-        KubernetesClient k8sClient = k8sClient(config);
-
-        if (k8sClient == null) {
-            log.warn("Failed to connect to kubernetes API server");
-            return;
-        }
-
-        for (Node node : k8sClient.nodes().list().getItems()) {
-            KubevirtNode kubevirtNode = buildKubevirtNode(node);
-            // we always provision VMs to worker nodes, so only need to install
-            // flow rules in worker nodes
-            if (kubevirtNode.type() == WORKER) {
-                if (!nodeAdminService.hasNode(kubevirtNode.hostname())) {
-                    nodeAdminService.createNode(kubevirtNode);
-                }
-            }
-        }
-    }
-
     private class InternalKubevirtApiConfigListener implements KubevirtApiConfigListener {
 
         private boolean isRelevantHelper() {
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 89086bf..4ac793d 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
@@ -43,6 +43,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.WORKER;
 import static org.onosproject.kubevirtnode.api.KubevirtNodeService.APP_ID;
 import static org.onosproject.kubevirtnode.api.KubevirtNodeState.INIT;
@@ -187,7 +188,7 @@
                     node.getMetadata().getName());
 
             KubevirtNode kubevirtNode = buildKubevirtNode(node);
-            if (kubevirtNode.type() == WORKER) {
+            if (kubevirtNode.type() == WORKER || kubevirtNode.type() == GATEWAY) {
                 if (!kubevirtNodeAdminService.hasNode(kubevirtNode.hostname())) {
                     kubevirtNodeAdminService.createNode(kubevirtNode);
                 }
diff --git a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java
index 06ccc31..f11547f 100644
--- a/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java
+++ b/apps/kubevirt-node/app/src/main/java/org/onosproject/kubevirtnode/util/KubevirtNodeUtil.java
@@ -15,6 +15,9 @@
  */
 package org.onosproject.kubevirtnode.util;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.base.Strings;
 import io.fabric8.kubernetes.api.model.Node;
@@ -53,6 +56,7 @@
 
 import static org.onlab.util.Tools.get;
 import static org.onosproject.kubevirtnode.api.Constants.SONA_PROJECT_DOMAIN;
+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;
 
@@ -73,6 +77,8 @@
     private static final String K8S_ROLE = "node-role.kubernetes.io";
     private static final String PHYSNET_CONFIG_KEY = SONA_PROJECT_DOMAIN + "/physnet-config";
     private static final String DATA_IP_KEY = SONA_PROJECT_DOMAIN + "/data-ip";
+    private static final String GATEWAY_CONFIG_KEY = SONA_PROJECT_DOMAIN + "/gateway-config";
+    private static final String GATEWAY_BRIDGE_NAME = "gatewayBridgeName";
     private static final String NETWORK_KEY = "network";
     private static final String INTERFACE_KEY = "interface";
 
@@ -336,8 +342,10 @@
         // start to parse kubernetes annotation
         Map<String, String> annots = node.getMetadata().getAnnotations();
         String physnetConfig = annots.get(PHYSNET_CONFIG_KEY);
+        String gatewayConfig = annots.get(GATEWAY_CONFIG_KEY);
         String dataIpStr = annots.get(DATA_IP_KEY);
         Set<KubevirtPhyInterface> phys = new HashSet<>();
+        String gatewayBridgeName = null;
         try {
             if (physnetConfig != null) {
                 JSONArray configJson = new JSONArray(physnetConfig);
@@ -351,15 +359,25 @@
                         phys.add(DefaultKubevirtPhyInterface.builder()
                                 .network(network).intf(intf).build());
                     }
-
                 }
             }
 
             if (dataIpStr != null) {
                 dataIp = IpAddress.valueOf(dataIpStr);
             }
+
+            if (gatewayConfig != null) {
+                JsonNode jsonNode = new ObjectMapper().readTree(gatewayConfig);
+
+                nodeType = GATEWAY;
+                gatewayBridgeName = jsonNode.get(GATEWAY_BRIDGE_NAME).asText();
+            }
         } catch (JSONException e) {
-            log.error("Failed to parse network status object", e);
+            log.error("Failed to parse physnet config or gateway config object because of{}", e);
+        } catch (JsonMappingException e) {
+            log.error("Failed to parse physnet config or gateway config object because of{}", e);
+        } catch (JsonProcessingException e) {
+            log.error("Failed to parse physnet config or gateway config object because of{}", e);
         }
 
         return DefaultKubevirtNode.builder()
@@ -369,6 +387,7 @@
                 .type(nodeType)
                 .state(KubevirtNodeState.ON_BOARDED)
                 .phyIntfs(phys)
+                .gatewayBridgeName(gatewayBridgeName)
                 .build();
     }
 }
diff --git a/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodecTest.java b/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodecTest.java
index 93d53af..eee6bf7 100644
--- a/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodecTest.java
+++ b/apps/kubevirt-node/app/src/test/java/org/onosproject/kubevirtnode/codec/KubevirtNodeCodecTest.java
@@ -142,6 +142,44 @@
     }
 
     /**
+     * Tests the kubevirt gateway node encoding.
+     */
+    @Test
+    public void testKubevirtGatweayNodeEncode() {
+        KubevirtNode node = DefaultKubevirtNode.builder()
+                .hostname("gateway")
+                .type(KubevirtNode.Type.GATEWAY)
+                .state(KubevirtNodeState.INIT)
+                .managementIp(IpAddress.valueOf("10.10.10.1"))
+                .intgBridge(DeviceId.deviceId("br-int"))
+                .tunBridge(DeviceId.deviceId("br-tun"))
+                .dataIp(IpAddress.valueOf("20.20.20.2"))
+                .gatewayBridgeName("gateway")
+                .build();
+
+        ObjectNode nodeJson = kubevirtNodeCodec.encode(node, context);
+        assertThat(nodeJson, matchesKubevirtNode(node));
+    }
+
+    /**
+     * Tests the kubevirt gateway node decoding.
+     *
+     * @throws IOException io exception
+     */
+    @Test
+    public void testKubevirtGatewayNodeDecode() throws IOException {
+        KubevirtNode node = getKubevirtNode("KubevirtGatewayNode.json");
+
+        assertThat(node.hostname(), is("gateway-01"));
+        assertThat(node.type().name(), is("GATEWAY"));
+        assertThat(node.managementIp().toString(), is("172.16.130.4"));
+        assertThat(node.dataIp().toString(), is("172.16.130.4"));
+        assertThat(node.intgBridge().toString(), is("of:00000000000000a1"));
+        assertThat(node.tunBridge().toString(), is("of:00000000000000a2"));
+        assertThat(node.gatewayBridgeName(), is("gateway"));
+    }
+
+    /**
      * Mock codec context for use in codec unit tests.
      */
     private class MockCodecContext implements CodecContext {
diff --git a/apps/kubevirt-node/app/src/test/resources/org/onosproject/kubevirtnode/codec/KubevirtGatewayNode.json b/apps/kubevirt-node/app/src/test/resources/org/onosproject/kubevirtnode/codec/KubevirtGatewayNode.json
new file mode 100644
index 0000000..1284fec
--- /dev/null
+++ b/apps/kubevirt-node/app/src/test/resources/org/onosproject/kubevirtnode/codec/KubevirtGatewayNode.json
@@ -0,0 +1,9 @@
+{
+  "hostname": "gateway-01",
+  "type": "GATEWAY",
+  "managementIp": "172.16.130.4",
+  "dataIp": "172.16.130.4",
+  "integrationBridge": "of:00000000000000a1",
+  "tunnelBridge": "of:00000000000000a2",
+  "gatewayBridgeName": "gateway"
+}
\ No newline at end of file