Initial support for multi kubernetes clusters for k8s nodes

Change-Id: I6ca132898f8e157e0583de38a637fdc135f21d6f
diff --git a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
index d7d7638..2f994da 100644
--- a/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
+++ b/apps/k8s-networking/app/src/main/java/org/onosproject/k8snetworking/impl/K8sSwitchingGatewayHandler.java
@@ -240,7 +240,7 @@
         if (REPLY.equals(type)) {
             sBuilder.matchIPSrc(IpPrefix.valueOf(k8sNetwork.gatewayIp(),
                     HOST_PREFIX));
-            tBuilder.setOutput(k8sNode.localToIntgPatchPortNumber());
+            tBuilder.setOutput(k8sNode.localToIntgPatchPortNum());
         }
 
         k8sFlowRuleService.setRule(
diff --git a/apps/k8s-node/BUILD b/apps/k8s-node/BUILD
index c28c480..96724fb 100644
--- a/apps/k8s-node/BUILD
+++ b/apps/k8s-node/BUILD
@@ -1,6 +1,7 @@
 BUNDLES = JACKSON + [
     "//apps/k8s-node/api:onos-apps-k8s-node-api",
     "//apps/k8s-node/app:onos-apps-k8s-node-app",
+    "@commons_net//jar",
     "@k8s_client//jar",
     "@k8s_model//jar",
     "@okhttp//jar",
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/Constants.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/Constants.java
index 513d21a..c32f766 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/Constants.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/Constants.java
@@ -26,10 +26,16 @@
     public static final String INTEGRATION_BRIDGE = "kbr-int";
     public static final String EXTERNAL_BRIDGE = "kbr-ex";
     public static final String LOCAL_BRIDGE = "kbr-local";
+    public static final String TUNNEL_BRIDGE = "kbr-tun";
+    public static final String EXTERNAL_ROUTER = "kbr-router";
     public static final String INTEGRATION_TO_EXTERNAL_BRIDGE = "kbr-int-ex";
     public static final String PHYSICAL_EXTERNAL_BRIDGE = "phy-kbr-ex";
     public static final String INTEGRATION_TO_LOCAL_BRIDGE = "kbr-int-local";
     public static final String LOCAL_TO_INTEGRATION_BRIDGE = "kbr-local-int";
+    public static final String EXTERNAL_TO_ROUTER = "kbr-ex-router";
+    public static final String ROUTER_TO_EXTERNAL = "kbr-router-ex";
+    public static final String INTEGRATION_TO_TUN_BRIDGE = "kbr-int-tun";
+    public static final String TUN_TO_INTEGRATION_BRIDGE = "kbr-tun-int";
     public static final String VXLAN_TUNNEL = "vxlan";
     public static final String GRE_TUNNEL = "gre";
     public static final String GENEVE_TUNNEL = "geneve";
@@ -37,4 +43,9 @@
     public static final String VXLAN = "vxlan";
     public static final String GRE = "gre";
     public static final String GENEVE = "geneve";
+
+    public static final String DEFAULT_CLUSTER_NAME = "default";
+    public static final String DEFAULT_CONFIG_MODE = "NORMAL";
+
+    public static final int DEFAULT_SEGMENT_ID = 100;
 }
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultHostNodesInfo.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultHostNodesInfo.java
new file mode 100644
index 0000000..94be29e
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultHostNodesInfo.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Default implementation of host to nodes mapping info.
+ */
+public final class DefaultHostNodesInfo implements HostNodesInfo {
+
+    private static final String NOT_NULL_MSG = "HostNodesInfo % cannot be null";
+
+    private final IpAddress hostIp;
+    private final Set<String> nodes;
+
+    private DefaultHostNodesInfo(IpAddress hostIp, Set<String> nodes) {
+        this.hostIp = hostIp;
+        this.nodes = ImmutableSet.copyOf(nodes);
+    }
+
+    @Override
+    public IpAddress hostIp() {
+        return hostIp;
+    }
+
+    @Override
+    public Set<String> nodes() {
+        return ImmutableSet.copyOf(nodes);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultHostNodesInfo that = (DefaultHostNodesInfo) o;
+        return Objects.equals(hostIp, that.hostIp) &&
+                Objects.equals(nodes, that.nodes);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(hostIp, nodes);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("hostIp", hostIp)
+                .add("nodes", nodes)
+                .toString();
+    }
+
+    /**
+     * Returns new builder instance.
+     *
+     * @return HostNodesInfo builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder implements HostNodesInfo.Builder {
+
+        private IpAddress hostIp;
+        private Set<String> nodes;
+
+        @Override
+        public HostNodesInfo build() {
+            checkArgument(hostIp != null, NOT_NULL_MSG, "Host IP address");
+            if (nodes == null) {
+                nodes = ImmutableSet.of();
+            }
+
+            return new DefaultHostNodesInfo(hostIp, nodes);
+        }
+
+        @Override
+        public HostNodesInfo.Builder hostIp(IpAddress hostIp) {
+            this.hostIp = hostIp;
+            return this;
+        }
+
+        @Override
+        public HostNodesInfo.Builder nodes(Set<String> nodes) {
+            this.nodes = nodes;
+            return this;
+        }
+    }
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sApiConfig.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sApiConfig.java
index dc8d030..cbc62e6 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sApiConfig.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sApiConfig.java
@@ -16,11 +16,17 @@
 package org.onosproject.k8snode.api;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import org.apache.commons.lang.StringUtils;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 
 import java.util.Objects;
+import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_CLUSTER_NAME;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.NORMAL;
 import static org.onosproject.k8snode.api.K8sApiConfig.Scheme.HTTPS;
 
 /**
@@ -30,7 +36,13 @@
 
     private static final String NOT_NULL_MSG = "API Config % cannot be null";
 
+    private static final int SHORT_NAME_LENGTH = 10;
+
+    private final String clusterName;
+    private final int segmentId;
+    private final IpPrefix extNetworkCidr;
     private final Scheme scheme;
+    private final Mode mode;
     private final IpAddress ipAddress;
     private final int port;
     private final State state;
@@ -38,18 +50,45 @@
     private final String caCertData;
     private final String clientCertData;
     private final String clientKeyData;
+    private final Set<HostNodesInfo> infos;
 
-    private DefaultK8sApiConfig(Scheme scheme, IpAddress ipAddress, int port,
-                                State state, String token, String caCertData,
-                                String clientCertData, String clientKeyData) {
+    private DefaultK8sApiConfig(String clusterName, int segmentId, IpPrefix extNetworkCidr,
+                                Scheme scheme, IpAddress ipAddress, int port,
+                                Mode mode, State state, String token, String caCertData,
+                                String clientCertData, String clientKeyData, Set<HostNodesInfo> infos) {
+        this.clusterName = clusterName;
+        this.segmentId = segmentId;
+        this.extNetworkCidr = extNetworkCidr;
         this.scheme = scheme;
         this.ipAddress = ipAddress;
         this.port = port;
+        this.mode = mode;
         this.state = state;
         this.token = token;
         this.caCertData = caCertData;
         this.clientCertData = clientCertData;
         this.clientKeyData = clientKeyData;
+        this.infos = infos;
+    }
+
+    @Override
+    public String clusterName() {
+        return clusterName;
+    }
+
+    @Override
+    public String clusterShortName() {
+        return StringUtils.substring(clusterName, 0, SHORT_NAME_LENGTH);
+    }
+
+    @Override
+    public int segmentId() {
+        return segmentId;
+    }
+
+    @Override
+    public IpPrefix extNetworkCidr() {
+        return extNetworkCidr;
     }
 
     @Override
@@ -73,16 +112,26 @@
     }
 
     @Override
+    public Mode mode() {
+        return mode;
+    }
+
+    @Override
     public K8sApiConfig updateState(State newState) {
         return new Builder()
+                .clusterName(clusterName)
+                .segmentId(segmentId)
+                .extNetworkCidr(extNetworkCidr)
                 .scheme(scheme)
                 .ipAddress(ipAddress)
                 .port(port)
                 .state(newState)
+                .mode(mode)
                 .token(token)
                 .caCertData(caCertData)
                 .clientCertData(clientCertData)
                 .clientKeyData(clientKeyData)
+                .infos(infos)
                 .build();
     }
 
@@ -107,6 +156,11 @@
     }
 
     @Override
+    public Set<HostNodesInfo> infos() {
+        return ImmutableSet.copyOf(infos);
+    }
+
+    @Override
     public boolean equals(Object o) {
         if (this == o) {
             return true;
@@ -117,31 +171,41 @@
         DefaultK8sApiConfig that = (DefaultK8sApiConfig) o;
         return port == that.port &&
                 scheme == that.scheme &&
+                clusterName.equals(that.clusterName) &&
+                segmentId == that.segmentId &&
+                extNetworkCidr == that.extNetworkCidr &&
                 ipAddress.equals(that.ipAddress) &&
+                mode == that.mode &&
                 state == that.state &&
                 token.equals(that.token) &&
                 caCertData.equals(that.caCertData) &&
                 clientCertData.equals(that.clientCertData) &&
-                clientKeyData.equals(that.clientKeyData);
+                clientKeyData.equals(that.clientKeyData) &&
+                infos.equals(that.infos);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(scheme, ipAddress, port, state, token, caCertData,
-                clientCertData, clientKeyData);
+        return Objects.hash(clusterName, segmentId, extNetworkCidr, scheme, ipAddress,
+                port, mode, state, token, caCertData, clientCertData, clientKeyData, infos);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
+                .add("clusterName", clusterName)
+                .add("segmentID", segmentId)
+                .add("extNetworkCIDR", extNetworkCidr)
                 .add("scheme", scheme)
                 .add("ipAddress", ipAddress)
                 .add("port", port)
+                .add("mode", mode)
                 .add("state", state)
                 .add("token", token)
                 .add("caCertData", caCertData)
                 .add("clientCertData", clientCertData)
                 .add("clientKeyData", clientKeyData)
+                .add("infos", infos)
                 .toString();
     }
 
@@ -156,7 +220,11 @@
 
     public static final class Builder implements K8sApiConfig.Builder {
 
+        private String clusterName;
+        private int segmentId;
+        private IpPrefix extNetworkCidr;
         private Scheme scheme;
+        private Mode mode;
         private IpAddress ipAddress;
         private int port;
         private State state;
@@ -164,6 +232,7 @@
         private String caCertData;
         private String clientCertData;
         private String clientKeyData;
+        private Set<HostNodesInfo> infos;
 
         @Override
         public K8sApiConfig build() {
@@ -177,8 +246,38 @@
                 checkArgument(clientKeyData != null, NOT_NULL_MSG, "clientKeyData");
             }
 
-            return new DefaultK8sApiConfig(scheme, ipAddress, port, state, token,
-                    caCertData, clientCertData, clientKeyData);
+            if (StringUtils.isEmpty(clusterName)) {
+                clusterName = DEFAULT_CLUSTER_NAME;
+            }
+
+            if (mode == null) {
+                mode = NORMAL;
+            }
+
+            if (infos == null) {
+                infos = ImmutableSet.of();
+            }
+
+            return new DefaultK8sApiConfig(clusterName, segmentId, extNetworkCidr, scheme, ipAddress,
+                    port, mode, state, token, caCertData, clientCertData, clientKeyData, infos);
+        }
+
+        @Override
+        public Builder clusterName(String clusterName) {
+            this.clusterName = clusterName;
+            return this;
+        }
+
+        @Override
+        public Builder segmentId(int segmentId) {
+            this.segmentId = segmentId;
+            return this;
+        }
+
+        @Override
+        public K8sApiConfig.Builder extNetworkCidr(IpPrefix extNetworkCidr) {
+            this.extNetworkCidr = extNetworkCidr;
+            return this;
         }
 
         @Override
@@ -206,6 +305,12 @@
         }
 
         @Override
+        public K8sApiConfig.Builder mode(Mode mode) {
+            this.mode = mode;
+            return this;
+        }
+
+        @Override
         public Builder token(String token) {
             this.token = token;
             return this;
@@ -228,5 +333,11 @@
             this.clientKeyData = clientKeyData;
             return this;
         }
+
+        @Override
+        public K8sApiConfig.Builder infos(Set<HostNodesInfo> infos) {
+            this.infos = infos;
+            return this;
+        }
     }
 }
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sExternalNetwork.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sExternalNetwork.java
new file mode 100644
index 0000000..cc19ff7
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sExternalNetwork.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+
+import java.util.Objects;
+
+/**
+ * Representation of a external network.
+ */
+public class DefaultK8sExternalNetwork implements K8sExternalNetwork {
+
+    private final IpAddress extBridgeIp;
+    private final IpAddress extGatewayIp;
+    private final MacAddress extGatewayMac;
+
+    protected DefaultK8sExternalNetwork(IpAddress extBridgeIp, IpAddress extGatewayIp,
+                                        MacAddress extGatewayMac) {
+        this.extBridgeIp = extBridgeIp;
+        this.extGatewayIp = extGatewayIp;
+        this.extGatewayMac = extGatewayMac;
+    }
+
+    @Override
+    public IpAddress extBridgeIp() {
+        return extBridgeIp;
+    }
+
+    @Override
+    public IpAddress extGatewayIp() {
+        return extGatewayIp;
+    }
+
+    @Override
+    public MacAddress extGatewayMac() {
+        return extGatewayMac;
+    }
+
+    /**
+     * Returns new builder instance.
+     *
+     * @return kubernetes node builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultK8sExternalNetwork that = (DefaultK8sExternalNetwork) o;
+        return extBridgeIp.equals(that.extBridgeIp) &&
+                extGatewayIp.equals(that.extGatewayIp);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(extBridgeIp, extGatewayIp, extGatewayMac);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("extBridgeIp", extBridgeIp)
+                .add("extGatewayIp", extGatewayIp)
+                .add("extGatewayMac", extGatewayMac)
+                .toString();
+    }
+
+    public static final class Builder implements K8sExternalNetwork.Builder {
+
+        private IpAddress extBridgeIp;
+        private IpAddress extGatewayIp;
+        private MacAddress extGatewayMac;
+
+        // private constructor not intended to use from external
+        private Builder() {
+        }
+
+        @Override
+        public K8sExternalNetwork build() {
+            return new DefaultK8sExternalNetwork(extBridgeIp, extGatewayIp, extGatewayMac);
+        }
+
+        @Override
+        public Builder extBridgeIp(IpAddress extBridgeIp) {
+            this.extBridgeIp = extBridgeIp;
+            return this;
+        }
+
+        @Override
+        public Builder extGatewayIp(IpAddress extGatewayIp) {
+            this.extGatewayIp = extGatewayIp;
+            return this;
+        }
+
+        @Override
+        public Builder extGatewayMac(MacAddress extGatewayMac) {
+            this.extGatewayMac = extGatewayMac;
+            return this;
+        }
+    }
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sHost.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sHost.java
new file mode 100644
index 0000000..1d49757
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sHost.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Representation of a kubernetes host.
+ */
+public class DefaultK8sHost implements K8sHost {
+
+    private final IpAddress hostIp;
+    private final Set<String> nodeNames;
+    private final K8sHostState state;
+
+    private static final String NOT_NULL_MSG = "Host % cannot be null";
+
+    private static final String OVSDB = "ovsdb:";
+
+    /**
+     * A default constructor of kubernetes host.
+     *
+     * @param hostIp        host IP address
+     * @param nodeNames     node names
+     * @param state         host state
+     */
+    protected DefaultK8sHost(IpAddress hostIp, Set<String> nodeNames,
+                             K8sHostState state) {
+        this.hostIp = hostIp;
+        this.nodeNames = nodeNames;
+        this.state = state;
+    }
+
+    @Override
+    public IpAddress hostIp() {
+        return hostIp;
+    }
+
+    @Override
+    public Set<String> nodeNames() {
+        return ImmutableSet.copyOf(nodeNames);
+    }
+
+    @Override
+    public K8sHostState state() {
+        return state;
+    }
+
+    @Override
+    public DeviceId ovsdb() {
+        return DeviceId.deviceId(OVSDB + hostIp.toString());
+    }
+
+    @Override
+    public K8sHost updateState(K8sHostState newState) {
+        return new Builder()
+                .hostIp(hostIp)
+                .nodeNames(nodeNames)
+                .state(newState)
+                .build();
+    }
+
+    @Override
+    public K8sHost updateNodeNames(Set<String> nodeNames) {
+        return new Builder()
+                .hostIp(hostIp)
+                .nodeNames(nodeNames)
+                .state(state)
+                .build();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultK8sHost that = (DefaultK8sHost) o;
+        return Objects.equals(hostIp, that.hostIp) &&
+                Objects.equals(nodeNames, that.nodeNames) &&
+                state == that.state;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(hostIp, nodeNames, state);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("hostIp", hostIp)
+                .add("nodeNames", nodeNames)
+                .add("state", state)
+                .toString();
+    }
+
+    /**
+     * Returns new builder instance.
+     *
+     * @return kubernetes host builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder implements K8sHost.Builder {
+
+        private IpAddress hostIp;
+        private Set<String> nodeNames;
+        private K8sHostState state;
+
+        // private constructor not intended to use from external
+        private Builder() {
+        }
+
+        @Override
+        public K8sHost build() {
+            checkArgument(hostIp != null, NOT_NULL_MSG, "hostIp");
+            checkArgument(state != null, NOT_NULL_MSG, "state");
+
+            if (nodeNames == null) {
+                nodeNames = new HashSet<>();
+            }
+
+            return new DefaultK8sHost(hostIp, nodeNames, state);
+        }
+
+        @Override
+        public Builder hostIp(IpAddress hostIp) {
+            this.hostIp = hostIp;
+            return this;
+        }
+
+        @Override
+        public Builder nodeNames(Set<String> nodeNames) {
+            this.nodeNames = nodeNames;
+            return this;
+        }
+
+        @Override
+        public Builder state(K8sHostState state) {
+            this.state = state;
+            return this;
+        }
+    }
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
index 6a18194..56039b5 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/DefaultK8sNode.java
@@ -16,9 +16,11 @@
 package org.onosproject.k8snode.api;
 
 import com.google.common.base.MoreObjects;
+import org.apache.commons.lang.StringUtils;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onosproject.k8snode.api.K8sApiConfig.Mode;
 import org.onosproject.net.Annotations;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
@@ -26,17 +28,25 @@
 import org.onosproject.net.device.DeviceService;
 
 import java.util.Objects;
+import java.util.UUID;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_CLUSTER_NAME;
 import static org.onosproject.k8snode.api.Constants.EXTERNAL_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.GENEVE_TUNNEL;
 import static org.onosproject.k8snode.api.Constants.GRE_TUNNEL;
 import static org.onosproject.k8snode.api.Constants.INTEGRATION_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_EXTERNAL_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_LOCAL_BRIDGE;
+import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_TUN_BRIDGE;
+import static org.onosproject.k8snode.api.Constants.LOCAL_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.LOCAL_TO_INTEGRATION_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.PHYSICAL_EXTERNAL_BRIDGE;
+import static org.onosproject.k8snode.api.Constants.TUNNEL_BRIDGE;
+import static org.onosproject.k8snode.api.Constants.TUN_TO_INTEGRATION_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.VXLAN_TUNNEL;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.NORMAL;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.PASSTHROUGH;
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 
 /**
@@ -45,19 +55,24 @@
 public class DefaultK8sNode implements K8sNode {
 
     private static final String PORT_MAC = "portMac";
+    private static final String FLOW_KEY = "flow";
 
+    private static final int SHORT_NAME_LENGTH = 10;
+
+    private final String clusterName;
     private final String hostname;
     private final Type type;
+    private final int segmentId;
+    private final Mode mode;
     private final DeviceId intgBridge;
     private final DeviceId extBridge;
     private final DeviceId localBridge;
+    private final DeviceId tunBridge;
     private final IpAddress managementIp;
     private final IpAddress dataIp;
     private final K8sNodeState state;
     private final String extIntf;
-    private final IpAddress extBridgeIp;
-    private final IpAddress extGatewayIp;
-    private final MacAddress extGatewayMac;
+    private final K8sExternalNetwork extNetwork;
     private final String podCidr;
 
     private static final String NOT_NULL_MSG = "Node % cannot be null";
@@ -67,42 +82,81 @@
     /**
      * A default constructor of kubernetes Node.
      *
+     * @param clusterName       clusterName
      * @param hostname          hostname
      * @param type              node type
+     * @param segmentId         segment identifier
+     * @param mode              CNI running mode
      * @param intgBridge        integration bridge
      * @param extBridge         external bridge
      * @param localBridge       local bridge
+     * @param tunBridge         tunnel bridge
      * @param extIntf           external interface
      * @param managementIp      management IP address
      * @param dataIp            data IP address
      * @param state             node state
-     * @param extBridgeIp       external bridge IP address
-     * @param extGatewayIp      external gateway IP address
-     * @param extGatewayMac     external gateway MAC address
+     * @param extNetwork        external network
      * @param podCidr           POD CIDR
      */
-    protected DefaultK8sNode(String hostname, Type type, DeviceId intgBridge,
+    protected DefaultK8sNode(String clusterName, String hostname, Type type,
+                             int segmentId, Mode mode, DeviceId intgBridge,
                              DeviceId extBridge, DeviceId localBridge,
-                             String extIntf, IpAddress managementIp,
+                             DeviceId tunBridge, String extIntf, IpAddress managementIp,
                              IpAddress dataIp, K8sNodeState state,
-                             IpAddress extBridgeIp, IpAddress extGatewayIp,
-                             MacAddress extGatewayMac, String podCidr) {
+                             K8sExternalNetwork extNetwork, String podCidr) {
+        this.clusterName = clusterName;
         this.hostname = hostname;
         this.type = type;
+        this.mode = mode;
+        this.segmentId = segmentId;
         this.intgBridge = intgBridge;
         this.extBridge = extBridge;
         this.localBridge = localBridge;
+        this.tunBridge = tunBridge;
         this.extIntf = extIntf;
         this.managementIp = managementIp;
         this.dataIp = dataIp;
         this.state = state;
-        this.extBridgeIp = extBridgeIp;
-        this.extGatewayIp = extGatewayIp;
-        this.extGatewayMac = extGatewayMac;
+        this.extNetwork = extNetwork;
         this.podCidr = podCidr;
     }
 
     @Override
+    public String clusterName() {
+        return clusterName;
+    }
+
+    @Override
+    public String hostShortName() {
+        return StringUtils.substring(hostname, 0, SHORT_NAME_LENGTH);
+    }
+
+    @Override
+    public String uniqueString(int length) {
+        String uuid = UUID.nameUUIDFromBytes(hostname.getBytes()).toString();
+        return StringUtils.substring(uuid, 0, length);
+    }
+
+    @Override
+    public int segmentId() {
+        return segmentId;
+    }
+
+    @Override
+    public String tunnelKey() {
+        if (mode == PASSTHROUGH) {
+            return String.valueOf(segmentId);
+        } else {
+            return FLOW_KEY;
+        }
+    }
+
+    @Override
+    public Mode mode() {
+        return mode;
+    }
+
+    @Override
     public String hostname() {
         return hostname;
     }
@@ -133,6 +187,11 @@
     }
 
     @Override
+    public DeviceId tunBridge() {
+        return tunBridge;
+    }
+
+    @Override
     public String extIntf() {
         return extIntf;
     }
@@ -141,17 +200,21 @@
     public K8sNode updateIntgBridge(DeviceId deviceId) {
         return new Builder()
                 .hostname(hostname)
+                .clusterName(clusterName)
                 .type(type)
+                .segmentId(segmentId)
+                .mode(mode)
                 .intgBridge(deviceId)
                 .extBridge(extBridge)
                 .localBridge(localBridge)
+                .tunBridge(tunBridge)
                 .extIntf(extIntf)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
                 .state(state)
-                .extBridgeIp(extBridgeIp)
-                .extGatewayIp(extGatewayIp)
-                .extGatewayMac(extGatewayMac)
+                .extBridgeIp(extNetwork.extBridgeIp())
+                .extGatewayIp(extNetwork.extGatewayIp())
+                .extGatewayMac(extNetwork.extGatewayMac())
                 .podCidr(podCidr)
                 .build();
     }
@@ -160,17 +223,21 @@
     public K8sNode updateExtBridge(DeviceId deviceId) {
         return new Builder()
                 .hostname(hostname)
+                .clusterName(clusterName)
                 .type(type)
+                .segmentId(segmentId)
+                .mode(mode)
                 .intgBridge(intgBridge)
                 .extBridge(deviceId)
                 .localBridge(localBridge)
+                .tunBridge(tunBridge)
                 .extIntf(extIntf)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
                 .state(state)
-                .extBridgeIp(extBridgeIp)
-                .extGatewayIp(extGatewayIp)
-                .extGatewayMac(extGatewayMac)
+                .extBridgeIp(extNetwork.extBridgeIp())
+                .extGatewayIp(extNetwork.extGatewayIp())
+                .extGatewayMac(extNetwork.extGatewayMac())
                 .podCidr(podCidr)
                 .build();
     }
@@ -179,17 +246,44 @@
     public K8sNode updateLocalBridge(DeviceId deviceId) {
         return new Builder()
                 .hostname(hostname)
+                .clusterName(clusterName)
                 .type(type)
+                .segmentId(segmentId)
+                .mode(mode)
                 .intgBridge(intgBridge)
                 .extBridge(extBridge)
                 .localBridge(deviceId)
+                .tunBridge(tunBridge)
                 .extIntf(extIntf)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
                 .state(state)
-                .extBridgeIp(extBridgeIp)
-                .extGatewayIp(extGatewayIp)
-                .extGatewayMac(extGatewayMac)
+                .extBridgeIp(extNetwork.extBridgeIp())
+                .extGatewayIp(extNetwork.extGatewayIp())
+                .extGatewayMac(extNetwork.extGatewayMac())
+                .podCidr(podCidr)
+                .build();
+    }
+
+    @Override
+    public K8sNode updateTunBridge(DeviceId deviceId) {
+        return new Builder()
+                .hostname(hostname)
+                .clusterName(clusterName)
+                .type(type)
+                .segmentId(segmentId)
+                .mode(mode)
+                .intgBridge(intgBridge)
+                .extBridge(extBridge)
+                .localBridge(localBridge)
+                .tunBridge(deviceId)
+                .extIntf(extIntf)
+                .managementIp(managementIp)
+                .dataIp(dataIp)
+                .state(state)
+                .extBridgeIp(extNetwork.extBridgeIp())
+                .extGatewayIp(extNetwork.extGatewayIp())
+                .extGatewayMac(extNetwork.extGatewayMac())
                 .podCidr(podCidr)
                 .build();
     }
@@ -218,17 +312,21 @@
     public K8sNode updateState(K8sNodeState newState) {
         return new Builder()
                 .hostname(hostname)
+                .clusterName(clusterName)
                 .type(type)
+                .segmentId(segmentId)
+                .mode(mode)
                 .intgBridge(intgBridge)
                 .extBridge(extBridge)
                 .localBridge(localBridge)
+                .tunBridge(tunBridge)
                 .extIntf(extIntf)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
                 .state(newState)
-                .extBridgeIp(extBridgeIp)
-                .extGatewayIp(extGatewayIp)
-                .extGatewayMac(extGatewayMac)
+                .extBridgeIp(extNetwork.extBridgeIp())
+                .extGatewayIp(extNetwork.extGatewayIp())
+                .extGatewayMac(extNetwork.extGatewayMac())
                 .podCidr(podCidr)
                 .build();
     }
@@ -237,16 +335,20 @@
     public K8sNode updateExtGatewayMac(MacAddress newMac) {
         return new Builder()
                 .hostname(hostname)
+                .clusterName(clusterName)
                 .type(type)
+                .segmentId(segmentId)
+                .mode(mode)
                 .intgBridge(intgBridge)
                 .extBridge(extBridge)
                 .localBridge(localBridge)
+                .tunBridge(tunBridge)
                 .extIntf(extIntf)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
                 .state(state)
-                .extBridgeIp(extBridgeIp)
-                .extGatewayIp(extGatewayIp)
+                .extBridgeIp(extNetwork.extBridgeIp())
+                .extGatewayIp(extNetwork.extGatewayIp())
                 .extGatewayMac(newMac)
                 .podCidr(podCidr)
                 .build();
@@ -254,42 +356,43 @@
 
     @Override
     public PortNumber grePortNum() {
-        return tunnelPortNum(GRE_TUNNEL);
+        return tunnelPortNum(grePortName());
     }
 
     @Override
     public PortNumber vxlanPortNum() {
-        return tunnelPortNum(VXLAN_TUNNEL);
+        return tunnelPortNum(vxlanPortName());
     }
 
     @Override
     public PortNumber genevePortNum() {
-        return tunnelPortNum(GENEVE_TUNNEL);
+        return tunnelPortNum(genevePortName());
     }
 
     @Override
     public PortNumber intgBridgePortNum() {
-        return portNumber(intgBridge, INTEGRATION_BRIDGE);
+        return portNumber(intgBridge, intgBridgePortName());
     }
 
     @Override
     public PortNumber intgToExtPatchPortNum() {
-        return portNumber(intgBridge, INTEGRATION_TO_EXTERNAL_BRIDGE);
+        return portNumber(intgBridge, intgToExtPatchPortName());
     }
 
     @Override
     public PortNumber intgToLocalPatchPortNum() {
-        return portNumber(intgBridge, INTEGRATION_TO_LOCAL_BRIDGE);
+        return portNumber(intgBridge, intgToLocalPatchPortName());
+
     }
 
     @Override
-    public PortNumber localToIntgPatchPortNumber() {
-        return portNumber(localBridge, LOCAL_TO_INTEGRATION_BRIDGE);
+    public PortNumber localToIntgPatchPortNum() {
+        return portNumber(localBridge, localToIntgPatchPortName());
     }
 
     @Override
     public PortNumber extToIntgPatchPortNum() {
-        return portNumber(extBridge, PHYSICAL_EXTERNAL_BRIDGE);
+        return portNumber(extBridge, extToIntgPatchPortName());
     }
 
     @Override
@@ -297,33 +400,176 @@
         if (this.extIntf == null) {
             return null;
         }
-
-        return portNumber(extBridge, this.extIntf);
+        return portNumber(extBridge, extBridgePortName());
     }
 
     @Override
     public MacAddress intgBridgeMac() {
-        return macAddress(intgBridge, INTEGRATION_BRIDGE);
+        return macAddress(intgBridge, intgBridgeName());
     }
 
     @Override
     public IpAddress extBridgeIp() {
-        return extBridgeIp;
+        return extNetwork.extBridgeIp();
     }
 
     @Override
     public MacAddress extBridgeMac() {
-        return macAddress(extBridge, EXTERNAL_BRIDGE);
+        return macAddress(extBridge, extBridgeName());
     }
 
     @Override
     public IpAddress extGatewayIp() {
-        return extGatewayIp;
+        return extNetwork.extGatewayIp();
     }
 
     @Override
     public MacAddress extGatewayMac() {
-        return extGatewayMac;
+        return extNetwork.extGatewayMac();
+    }
+
+    @Override
+    public String grePortName() {
+        if (mode == PASSTHROUGH) {
+            return GRE_TUNNEL + "-" + segmentId;
+        } else {
+            return GRE_TUNNEL;
+        }
+    }
+
+    @Override
+    public String vxlanPortName() {
+        if (mode == PASSTHROUGH) {
+            return VXLAN_TUNNEL + "-" + segmentId;
+        } else {
+            return VXLAN_TUNNEL;
+        }
+    }
+
+    @Override
+    public String genevePortName() {
+        if (mode == PASSTHROUGH) {
+            return GENEVE_TUNNEL + "-" + segmentId;
+        } else {
+            return GENEVE_TUNNEL;
+        }
+    }
+
+    @Override
+    public String intgBridgeName() {
+        if (mode == PASSTHROUGH) {
+            return INTEGRATION_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return INTEGRATION_BRIDGE;
+        }
+    }
+
+    @Override
+    public String extBridgeName() {
+        if (mode == PASSTHROUGH) {
+            return EXTERNAL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return EXTERNAL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String localBridgeName() {
+        if (mode == PASSTHROUGH) {
+            return LOCAL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return LOCAL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String tunBridgeName() {
+        if (mode == PASSTHROUGH) {
+            return TUNNEL_BRIDGE + "-" + segmentId;
+        } else {
+            return TUNNEL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String intgBridgePortName() {
+        if (mode == PASSTHROUGH) {
+            return INTEGRATION_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return INTEGRATION_BRIDGE;
+        }
+    }
+
+    @Override
+    public String extBridgePortName() {
+        if (mode == PASSTHROUGH) {
+            return EXTERNAL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return EXTERNAL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String localBridgePortName() {
+        if (mode == PASSTHROUGH) {
+            return LOCAL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return LOCAL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String intgToExtPatchPortName() {
+        if (mode == PASSTHROUGH) {
+            return INTEGRATION_TO_EXTERNAL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return INTEGRATION_TO_EXTERNAL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String intgToTunPatchPortName() {
+        if (mode == PASSTHROUGH) {
+            return INTEGRATION_TO_TUN_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return INTEGRATION_TO_TUN_BRIDGE;
+        }
+    }
+
+    @Override
+    public String intgToLocalPatchPortName() {
+        if (mode == PASSTHROUGH) {
+            return INTEGRATION_TO_LOCAL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return INTEGRATION_TO_LOCAL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String localToIntgPatchPortName() {
+        if (mode == PASSTHROUGH) {
+            return LOCAL_TO_INTEGRATION_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return LOCAL_TO_INTEGRATION_BRIDGE;
+        }
+    }
+
+    @Override
+    public String extToIntgPatchPortName() {
+        if (mode == PASSTHROUGH) {
+            return PHYSICAL_EXTERNAL_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return PHYSICAL_EXTERNAL_BRIDGE;
+        }
+    }
+
+    @Override
+    public String tunToIntgPatchPortName() {
+        if (mode == PASSTHROUGH) {
+            return TUN_TO_INTEGRATION_BRIDGE + "-" + uniqueString(5);
+        } else {
+            return TUN_TO_INTEGRATION_BRIDGE;
+        }
     }
 
     @Override
@@ -335,16 +581,19 @@
         if (obj instanceof DefaultK8sNode) {
             DefaultK8sNode that = (DefaultK8sNode) obj;
 
-            return hostname.equals(that.hostname) &&
+            return clusterName.equals(that.clusterName) &&
+                    hostname.equals(that.hostname) &&
                     type == that.type &&
+                    segmentId == that.segmentId &&
+                    mode == that.mode &&
                     intgBridge.equals(that.intgBridge) &&
                     extBridge.equals(that.extBridge) &&
                     localBridge.equals(that.localBridge) &&
+                    tunBridge.equals(that.tunBridge) &&
                     extIntf.equals(that.extIntf) &&
                     managementIp.equals(that.managementIp) &&
                     dataIp.equals(that.dataIp) &&
-                    extBridgeIp.equals(that.extBridgeIp) &&
-                    extGatewayIp.equals(that.extGatewayIp) &&
+                    extNetwork.equals(that.extNetwork) &&
                     podCidr.equals(that.podCidr) &&
                     state == that.state;
         }
@@ -354,26 +603,29 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(hostname, type, intgBridge, extBridge, localBridge,
-                            extIntf, managementIp, dataIp, state, extBridgeIp,
-                            extGatewayIp, extGatewayMac, podCidr);
+        return Objects.hash(clusterName, hostname, type, segmentId, mode, intgBridge, extBridge,
+                localBridge, tunBridge, extIntf, managementIp, dataIp, state, extNetwork, podCidr);
     }
 
     @Override
     public String toString() {
         return MoreObjects.toStringHelper(this)
+                .add("clusterName", clusterName)
                 .add("hostname", hostname)
                 .add("type", type)
+                .add("segmentId", segmentId)
+                .add("mode", mode)
                 .add("intgBridge", intgBridge)
                 .add("extBridge", extBridge)
                 .add("localBridge", localBridge)
+                .add("tunBridge", tunBridge)
                 .add("extIntf", extIntf)
                 .add("managementIp", managementIp)
                 .add("dataIp", dataIp)
                 .add("state", state)
-                .add("extBridgeIp", extBridgeIp)
-                .add("extGatewayIp", extGatewayIp)
-                .add("extGatewayMac", extGatewayMac)
+                .add("extBridgeIp", extNetwork.extBridgeIp())
+                .add("extGatewayIp", extNetwork.extGatewayIp())
+                .add("extGatewayMac", extNetwork.extGatewayMac())
                 .add("podCidr", podCidr)
                 .toString();
     }
@@ -383,7 +635,7 @@
             return null;
         }
 
-        return portNumber(intgBridge, tunnelType);
+        return portNumber(tunBridge, tunnelType);
     }
 
     private MacAddress macAddress(DeviceId deviceId, String portName) {
@@ -423,10 +675,13 @@
     public static Builder from(K8sNode node) {
         return new Builder()
                 .hostname(node.hostname())
+                .clusterName(node.clusterName())
                 .type(node.type())
+                .segmentId(node.segmentId())
                 .intgBridge(node.intgBridge())
                 .extBridge(node.extBridge())
                 .localBridge(node.localBridge())
+                .tunBridge(node.tunBridge())
                 .extIntf(node.extIntf())
                 .managementIp(node.managementIp())
                 .dataIp(node.dataIp())
@@ -439,11 +694,15 @@
 
     public static final class Builder implements K8sNode.Builder {
 
+        private String clusterName;
         private String hostname;
         private Type type;
+        private int segmentId;
+        private Mode mode;
         private DeviceId intgBridge;
         private DeviceId extBridge;
         private DeviceId localBridge;
+        private DeviceId tunBridge;
         private IpAddress managementIp;
         private IpAddress dataIp;
         private K8sNodeState state;
@@ -465,22 +724,44 @@
             checkArgument(state != null, NOT_NULL_MSG, "state");
             checkArgument(managementIp != null, NOT_NULL_MSG, "management IP");
 
-            return new DefaultK8sNode(hostname,
+            if (StringUtils.isEmpty(clusterName)) {
+                clusterName = DEFAULT_CLUSTER_NAME;
+            }
+
+            if (mode == null) {
+                mode = NORMAL;
+            }
+
+            K8sExternalNetwork extNetwork = DefaultK8sExternalNetwork.builder()
+                    .extBridgeIp(extBridgeIp)
+                    .extGatewayIp(extGatewayIp)
+                    .extGatewayMac(extGatewayMac)
+                    .build();
+
+            return new DefaultK8sNode(clusterName,
+                    hostname,
                     type,
+                    segmentId,
+                    mode,
                     intgBridge,
                     extBridge,
                     localBridge,
+                    tunBridge,
                     extIntf,
                     managementIp,
                     dataIp,
                     state,
-                    extBridgeIp,
-                    extGatewayIp,
-                    extGatewayMac,
+                    extNetwork,
                     podCidr);
         }
 
         @Override
+        public Builder clusterName(String clusterName) {
+            this.clusterName = clusterName;
+            return this;
+        }
+
+        @Override
         public Builder hostname(String hostname) {
             this.hostname = hostname;
             return this;
@@ -493,6 +774,18 @@
         }
 
         @Override
+        public Builder segmentId(int segmentId) {
+            this.segmentId = segmentId;
+            return this;
+        }
+
+        @Override
+        public Builder mode(Mode mode) {
+            this.mode = mode;
+            return this;
+        }
+
+        @Override
         public Builder intgBridge(DeviceId deviceId) {
             this.intgBridge = deviceId;
             return this;
@@ -511,6 +804,12 @@
         }
 
         @Override
+        public Builder tunBridge(DeviceId deviceId) {
+            this.tunBridge = deviceId;
+            return this;
+        }
+
+        @Override
         public Builder extIntf(String intf) {
             this.extIntf = intf;
             return this;
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/ExternalNetworkService.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/ExternalNetworkService.java
new file mode 100644
index 0000000..4a66e26
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/ExternalNetworkService.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+
+import java.util.Set;
+
+/**
+ * A service manages external network objects including IP addresses.
+ */
+public interface ExternalNetworkService {
+
+    /**
+     * Registers an external network.
+     *
+     * @param cidr network CIDR
+     */
+    void registerNetwork(IpPrefix cidr);
+
+    /**
+     * Unregisters an existing external network.
+     *
+     * @param cidr network CIDR
+     */
+    void unregisterNetwork(IpPrefix cidr);
+
+    /**
+     * Obtains a gateway IP address of the given network CIDR.
+     *
+     * @param cidr network CIDR
+     * @return a gateway IP address
+     */
+    IpAddress getGatewayIp(IpPrefix cidr);
+
+    /**
+     * Obtains an allocated IP address of the given network CIDR.
+     *
+     * @param cidr network CIDR
+     * @return an IP address exists in the given network CIDR IP pool
+     */
+    IpAddress allocateIp(IpPrefix cidr);
+
+    /**
+     * Releases the given IP address to the given network CIDR.
+     *
+     * @param cidr network CIDR
+     * @param ip IP address to be released
+     */
+    void releaseIp(IpPrefix cidr, IpAddress ip);
+
+    /**
+     * Obtains all IP addresses of the given network CIDR.
+     *
+     * @param cidr network CIDR
+     * @return all IP addresses
+     */
+    Set<String> getAllIps(IpPrefix cidr);
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/HostNodesInfo.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/HostNodesInfo.java
new file mode 100644
index 0000000..c420d20
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/HostNodesInfo.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onlab.packet.IpAddress;
+
+import java.util.Set;
+
+/**
+ * Representation of host and kubernetes nodes mapping info.
+ */
+public interface HostNodesInfo {
+
+    /**
+     * Returns the host's IP address.
+     *
+     * @return host IP address
+     */
+    IpAddress hostIp();
+
+    /**
+     * Returns the list of nodes associated with the host.
+     *
+     * @return a set of node's names
+     */
+    Set<String> nodes();
+
+    /**
+     * Builder of new HostNodesInfo entity.
+     */
+    interface Builder {
+
+        /**
+         * Builds an immutable host IP to nodes mapping instance.
+         *
+         * @return HostNodesInfo instance
+         */
+        HostNodesInfo build();
+
+        /**
+         * Returns HostNodesInfo builder with host IP address.
+         *
+         * @param hostIp host IP address
+         * @return HostNodesInfo builder
+         */
+        Builder hostIp(IpAddress hostIp);
+
+        /**
+         * Returns HostNodesInfo builder with nodes.
+         *
+         * @param nodes a set of node's names
+         * @return HostNodesInfo builder
+         */
+        Builder nodes(Set<String> nodes);
+    }
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sApiConfig.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sApiConfig.java
index 62d050d..d01f3a2 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sApiConfig.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sApiConfig.java
@@ -16,6 +16,9 @@
 package org.onosproject.k8snode.api;
 
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+
+import java.util.Set;
 
 /**
  * Representation of configuration used in kubernetes API server.
@@ -52,6 +55,46 @@
         DISCONNECTED,
     }
 
+    enum Mode {
+        /**
+         * Signifies that the CNI is running in normal mode.
+         */
+        NORMAL,
+
+        /**
+         * Signifies that the CNI is running in pass-through mode.
+         */
+        PASSTHROUGH,
+    }
+
+    /**
+     * Returns the cluster name.
+     *
+     * @return cluster name
+     */
+    String clusterName();
+
+    /**
+     * Returns cluster short name.
+     *
+     * @return cluster short name
+     */
+    String clusterShortName();
+
+    /**
+     * Returns the segmentation ID.
+     *
+     * @return segmentation ID
+     */
+    int segmentId();
+
+    /**
+     * Returns the external network CIDR.
+     *
+     * @return external network CIDR
+     */
+    IpPrefix extNetworkCidr();
+
     /**
      * Returns the authentication scheme.
      *
@@ -81,6 +124,13 @@
     State state();
 
     /**
+     * Returns the CNI running mode.
+     *
+     * @return running mode
+     */
+    Mode mode();
+
+    /**
      * Returns new kubernetes API config instance with given state.
      *
      * @param newState updated state
@@ -117,6 +167,13 @@
     String clientKeyData();
 
     /**
+     * Returns the host Nodes info set.
+     *
+     * @return host nodes info set
+     */
+    Set<HostNodesInfo> infos();
+
+    /**
      * Builder of new API config entity.
      */
     interface Builder {
@@ -129,6 +186,30 @@
         K8sApiConfig build();
 
         /**
+         * Returns kubernetes API server config builder with cluster name.
+         *
+         * @param clusterName cluster name
+         * @return kubernetes API config builder
+         */
+        Builder clusterName(String clusterName);
+
+        /**
+         * Returns kubernetes API server config builder with segment ID.
+         *
+         * @param segmentId segment ID
+         * @return kubernetes API config builder
+         */
+        Builder segmentId(int segmentId);
+
+        /**
+         * Returns kubernetes API server config builder with external network CIDR.
+         *
+         * @param extNetworkCidr external network CIDR
+         * @return kubernetes API config builder
+         */
+        Builder extNetworkCidr(IpPrefix extNetworkCidr);
+
+        /**
          * Returns kubernetes API server config builder with supplied scheme.
          *
          * @param scheme scheme of authentication
@@ -161,6 +242,14 @@
         Builder state(State state);
 
         /**
+         * Returns kubernetes API server config builder with supplied mode.
+         *
+         * @param mode CNI running mode
+         * @return kubernetes API config builder
+         */
+        Builder mode(Mode mode);
+
+        /**
          * Returns kubernetes API server config builder with supplied token.
          *
          * @param token token for authentication
@@ -191,5 +280,13 @@
          * @return kubernetes API config builder
          */
         Builder clientKeyData(String clientKeyData);
+
+        /**
+         * Returns kubernetes API server config builder with supplied hostNodesInfo.
+         *
+         * @param infos hostNodesInfo
+         * @return kubernetes API config builder
+         */
+        Builder infos(Set<HostNodesInfo> infos);
     }
 }
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sExternalNetwork.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sExternalNetwork.java
new file mode 100644
index 0000000..3462ad0
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sExternalNetwork.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+
+/**
+ * Representation of an external network.
+ */
+public interface K8sExternalNetwork {
+
+    /**
+     * Returns the external bridge's IP address.
+     *
+     * @return IP address; null if the IP address does not exist
+     */
+    IpAddress extBridgeIp();
+
+    /**
+     * Returns the external gateway IP address.
+     *
+     * @return IP address; null if the IP address does not exist
+     */
+    IpAddress extGatewayIp();
+
+    /**
+     * Returns the external gateway MAC address.
+     *
+     * @return MAC address; null if the MAC address does not exist
+     */
+    MacAddress extGatewayMac();
+
+    /**
+     * Builder of new network entity.
+     */
+    interface Builder {
+
+        /**
+         * Builds an immutable kubernetes external network instance.
+         *
+         * @return kubernetes external network
+         */
+        K8sExternalNetwork build();
+
+        /**
+         * Returns kubernetes node builder with supplied external bridge IP.
+         *
+         * @param extBridgeIp external bridge IP
+         * @return kubernetes node builder
+         */
+        Builder extBridgeIp(IpAddress extBridgeIp);
+
+        /**
+         * Returns kubernetes node builder with supplied gateway IP.
+         *
+         * @param extGatewayIp external gateway IP
+         * @return kubernetes node builder
+         */
+        Builder extGatewayIp(IpAddress extGatewayIp);
+
+        /**
+         * Returns kubernetes node builder with supplied external gateway MAC.
+         *
+         * @param extGatewayMac external gateway MAC address
+         * @return kubernetes node builder
+         */
+        Builder extGatewayMac(MacAddress extGatewayMac);
+    }
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHost.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHost.java
new file mode 100644
index 0000000..c93d4d1
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHost.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+
+import java.util.Set;
+
+/**
+ * Representation of a host used in k8s-networking service.
+ */
+public interface K8sHost {
+
+    /**
+     * Returns the host IP address. Note that the host IP address is unique, and
+     * will be used as an identifier for the host.
+     *
+     * @return host IP address
+     */
+    IpAddress hostIp();
+
+    /**
+     * A set of node names included in this host.
+     *
+     * @return node names
+     */
+    Set<String> nodeNames();
+
+    /**
+     * Returns kubernetes host state.
+     *
+     * @return host state
+     */
+    K8sHostState state();
+
+    /**
+     * Returns the OVSDB device ID of the node.
+     *
+     * @return ovsdb device ID
+     */
+    DeviceId ovsdb();
+
+    /**
+     * Returns new kubernetes host instance with given state.
+     *
+     * @param newState updated state
+     * @return updated kubernetes host
+     */
+    K8sHost updateState(K8sHostState newState);
+
+    /**
+     * Returns new kuberentes host instance with given node names.
+     *
+     * @param nodeNames a set of node names
+     * @return updated kubernetes host
+     */
+    K8sHost updateNodeNames(Set<String> nodeNames);
+
+    /**
+     * Builder of new host entity.
+     */
+    interface Builder {
+
+        /**
+         * Builds an immutable kubernetes host instance.
+         *
+         * @return kubernetes host instance
+         */
+        K8sHost build();
+
+        /**
+         * Returns kubernetes host builder with supplied host IP address.
+         *
+         * @param hostIp host IP address
+         * @return kubernetes host builder
+         */
+        Builder hostIp(IpAddress hostIp);
+
+        /**
+         * Returns kubernetes host builder with supplied node names.
+         *
+         * @param nodeNames node names
+         * @return kubernetes host builder
+         */
+        Builder nodeNames(Set<String> nodeNames);
+
+        /**
+         * Returns kubernetes host builder with supplied host state.
+         *
+         * @param state host state
+         * @return kubernetes host builder
+         */
+        Builder state(K8sHostState state);
+    }
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostAdminService.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostAdminService.java
new file mode 100644
index 0000000..d3f2861
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostAdminService.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onlab.packet.IpAddress;
+
+/**
+ * Service for administering inventory of Kubernetes hosts.
+ */
+public interface K8sHostAdminService extends K8sHostService {
+
+    /**
+     * Creates a new host.
+     *
+     * @param host kubernetes host
+     */
+    void createHost(K8sHost host);
+
+    /**
+     * Updates the host.
+     *
+     * @param host kubernetes host
+     */
+    void updateHost(K8sHost host);
+
+    /**
+     * Removes the host.
+     *
+     * @param hostIp kubernetes host IP address
+     * @return removed host; null if the host does not exist
+     */
+    K8sHost removeHost(IpAddress hostIp);
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostEvent.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostEvent.java
new file mode 100644
index 0000000..a403d0e
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostEvent.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes Kubernetes host init state event.
+ */
+public class K8sHostEvent extends AbstractEvent<K8sHostEvent.Type, K8sHost> {
+
+    /**
+     * Lists of kubernetes host event types.
+     */
+    public enum Type {
+
+        /**
+         * Signifies that new host is created.
+         */
+        K8S_HOST_CREATED,
+
+        /**
+         * Signifies that the host is updated.
+         */
+        K8S_HOST_UPDATED,
+
+        /**
+         * Signifies that the host state is completed.
+         */
+        K8S_HOST_COMPLETE,
+
+        /**
+         * Signifies that the host is removed.
+         */
+        K8S_HOST_REMOVED,
+
+        /**
+         * Signifies that the host state is incomplete.
+         */
+        K8S_HOST_INCOMPLETE,
+
+        /**
+         * Signifies that a set of nodes have been added into the host.
+         */
+        K8S_NODES_ADDED,
+
+        /**
+         * Signifies that a set of nodes have been removed from the host.
+         */
+        K8S_NODES_REMOVED,
+    }
+
+    /**
+     * Creates an event with the given type and host.
+     *
+     * @param type event type
+     * @param subject kubernetes host
+     */
+    public K8sHostEvent(Type type, K8sHost subject) {
+        super(type, subject);
+    }
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostHandler.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostHandler.java
new file mode 100644
index 0000000..335c89c
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostHandler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+/**
+ * Service handling kubernetes host stats.
+ */
+public interface K8sHostHandler {
+
+    /**
+     * Processes the given host for init state.
+     * It creates required bridges on OVS by referring to host type.zw
+     *
+     * @param k8sHost kubernetes host
+     */
+    void processInitState(K8sHost k8sHost);
+
+    /**
+     * Processes the given host for complete state.
+     * It performs post-init jobs for the complete host.
+     *
+     * @param k8sHost kubernetes host
+     */
+    void processCompleteState(K8sHost k8sHost);
+
+    /**
+     * Processes the given host for incomplete state.
+     *
+     * @param k8sHost kubernetes host
+     */
+    void processIncompleteState(K8sHost k8sHost);
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostListener.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostListener.java
new file mode 100644
index 0000000..edf5763
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for Kubernetes Host event.
+ */
+public interface K8sHostListener extends EventListener<K8sHostEvent> {
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostService.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostService.java
new file mode 100644
index 0000000..abdbb74
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostService.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.event.ListenerService;
+
+import java.util.Set;
+
+/**
+ * Service for interfacing with the inventory of Kubernetes host.
+ */
+public interface K8sHostService extends ListenerService<K8sHostEvent, K8sHostListener> {
+
+    String APP_ID = "org.onosproject.k8snode";
+
+    /**
+     * Returns all registered hosts.
+     *
+     * @return set of kubernetes hosts
+     */
+    Set<K8sHost> hosts();
+
+    /**
+     * Returns all hosts with complete states.
+     *
+     * @return set of kubernetes hosts
+     */
+    Set<K8sHost> completeHosts();
+
+    /**
+     * Returns the host with the specified IP address.
+     *
+     * @param hostIp host IP address
+     * @return kubernetes host
+     */
+    K8sHost host(IpAddress hostIp);
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostState.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostState.java
new file mode 100644
index 0000000..f9a07c3
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostState.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+/**
+ * Defines the initialization state of Kubernetes host.
+ */
+public enum K8sHostState {
+
+    /**
+     * Indicates the host is newly added.
+     */
+    INIT {
+        @Override
+        public void process(K8sHostHandler handler, K8sHost host) {
+            handler.processInitState(host);
+        }
+
+        @Override
+        public K8sHostState nextState() {
+            return COMPLETE;
+        }
+    },
+    /**
+     * Indicates the host initialization is done.
+     */
+    COMPLETE {
+        @Override
+        public void process(K8sHostHandler handler, K8sHost host) {
+            handler.processCompleteState(host);
+        }
+
+        @Override
+        public K8sHostState nextState() {
+            return COMPLETE;
+        }
+    },
+    /**
+     * Indicates host is broken.
+     */
+    INCOMPLETE {
+        @Override
+        public void process(K8sHostHandler handler, K8sHost host) {
+            handler.processIncompleteState(host);
+        }
+
+        @Override
+        public K8sHostState nextState() {
+            return INIT;
+        }
+    };
+
+    /**
+     * Processes the given host which is under a certain state.
+     *
+     * @param handler kubernetes host handler
+     * @param host kubernetes host
+     */
+    public abstract void process(K8sHostHandler handler, K8sHost host);
+
+    /**
+     * Transits to the next state.
+     *
+     * @return the next kubernetes host state
+     */
+    public abstract K8sHostState nextState();
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostStore.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostStore.java
new file mode 100644
index 0000000..aec871a
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostStore.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.store.Store;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of Kubernetes Host; not intended for direct use.
+ */
+public interface K8sHostStore extends Store<K8sHostEvent, K8sHostStoreDelegate> {
+
+    /**
+     * Creates a new host.
+     *
+     * @param host kubernetes host
+     */
+    void createHost(K8sHost host);
+
+    /**
+     * Updates the host.
+     *
+     * @param host kubernetes host
+     */
+    void updateHost(K8sHost host);
+
+    /**
+     * Removes the host with the given host IP.
+     *
+     * @param hostIp kubernetes host IP
+     * @return removed kubernetes host; null if no host is associated with the host IP
+     */
+    K8sHost removeHost(IpAddress hostIp);
+
+    /**
+     * Returns all registered hosts.
+     *
+     * @return set of kubernetes hosts
+     */
+    Set<K8sHost> hosts();
+
+    /**
+     * Returns the host with the specified host IP.
+     *
+     * @param hostIp host IP address
+     * @return kubernetes host
+     */
+    K8sHost host(IpAddress hostIp);
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostStoreDelegate.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostStoreDelegate.java
new file mode 100644
index 0000000..2c863db
--- /dev/null
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sHostStoreDelegate.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.k8snode.api;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * Kubernetes Host store delegate.
+ */
+public interface K8sHostStoreDelegate extends StoreDelegate<K8sHostEvent> {
+}
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
index 055b0df..8931b45 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNode.java
@@ -17,6 +17,7 @@
 
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onosproject.k8snode.api.K8sApiConfig.Mode;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 
@@ -37,10 +38,53 @@
         /**
          * Signifies that this is a kubernetes minion node.
          */
-        MINION
+        MINION,
     }
 
     /**
+     * Returns cluster name of the node.
+     *
+     * @return cluster name
+     */
+    String clusterName();
+
+    /**
+     * Returns host short name.
+     *
+     * @return host short name
+     */
+    String hostShortName();
+
+    /**
+     * Returns a unique string with the given length and string.
+     *
+     * @param length target string length
+     * @return a unique string
+     */
+    String uniqueString(int length);
+
+    /**
+     * Returns the segmentation ID.
+     *
+     * @return segmentation ID
+     */
+    int segmentId();
+
+    /**
+     * Returns the key of VXLAN/GRE/GENEVE tunnel.
+     *
+     * @return key of various tunnel
+     */
+    String tunnelKey();
+
+    /**
+     * Returns the CNI running mode.
+     *
+     * @return CNI running mode
+     */
+    Mode mode();
+
+    /**
      * Returns hostname of the node.
      *
      * @return hostname
@@ -83,6 +127,13 @@
     DeviceId localBridge();
 
     /**
+     * Returns the device ID of the tunnel bridge at the node.
+     *
+     * @return device id
+     */
+    DeviceId tunBridge();
+
+    /**
      * Returns the external interface name.
      *
      * @return external interface name
@@ -114,6 +165,14 @@
     K8sNode updateLocalBridge(DeviceId deviceId);
 
     /**
+     * Returns new kubernetes node instance with given tun bridge.
+     *
+     * @param deviceId tunnel bridge device ID
+     * @return updated kubernetes node
+     */
+    K8sNode updateTunBridge(DeviceId deviceId);
+
+    /**
      * Returns the management network IP address of the node.
      *
      * @return ip address
@@ -158,6 +217,118 @@
     K8sNode updateExtGatewayMac(MacAddress macAddress);
 
     /**
+     * Returns GRE port name.
+     *
+     * @return GRE port name
+     */
+    String grePortName();
+
+    /**
+     * Returns VXLAN port name.
+     *
+     * @return VXLAN port name
+     */
+    String vxlanPortName();
+
+    /**
+     * Returns GENEVE port name.
+     *
+     * @return GENEVE port name
+     */
+    String genevePortName();
+
+    /**
+     * Returns integration bridge name.
+     *
+     * @return integration bridge name
+     */
+    String intgBridgeName();
+
+    /**
+     * Returns external bridge name.
+     *
+     * @return external bridge name
+     */
+    String extBridgeName();
+
+    /**
+     * Returns local bridge name.
+     *
+     * @return local bridge name
+     */
+    String localBridgeName();
+
+    /**
+     * Returns tun bridge name.
+     *
+     * @return tun bridge name
+     */
+    String tunBridgeName();
+
+    /**
+     * Returns integration bridge port name.
+     *
+     * @return integration bridge port name
+     */
+    String intgBridgePortName();
+
+    /**
+     * Returns external bridge port name.
+     *
+     * @return external bridge port name
+     */
+    String extBridgePortName();
+
+    /**
+     * Returns local bridge port name.
+     *
+     * @return local bridge port name
+     */
+    String localBridgePortName();
+
+    /**
+     * Returns integration to external patch port name.
+     *
+     * @return integration to external patch port name
+     */
+    String intgToExtPatchPortName();
+
+    /**
+     * Returns integration to tunnel patch port name.
+     *
+     * @return integration to tunnel patch port name
+     */
+    String intgToTunPatchPortName();
+
+    /**
+     * Returns integration to local patch port name.
+     *
+     * @return integration to local patch port name
+     */
+    String intgToLocalPatchPortName();
+
+    /**
+     * Returns local to integration patch port name.
+     *
+     * @return local to integration patch port name
+     */
+    String localToIntgPatchPortName();
+
+    /**
+     * Returns external to integration patch port name.
+     *
+     * @return external to integration patch port name
+     */
+    String extToIntgPatchPortName();
+
+    /**
+     * Returns tunnel to integration patch port name.
+     *
+     * @return tunnel to integration patch port name
+     */
+    String tunToIntgPatchPortName();
+
+    /**
      * Returns the GRE tunnel port number.
      *
      * @return GRE port number; null if the GRE tunnel port does not exist
@@ -204,7 +375,7 @@
      *
      * @return patch port number
      */
-    PortNumber localToIntgPatchPortNumber();
+    PortNumber localToIntgPatchPortNum();
 
     /**
      * Returns the external to integration patch port number.
@@ -268,6 +439,14 @@
         K8sNode build();
 
         /**
+         * Returns kubernetes node builder with supplied cluster name.
+         *
+         * @param clusterName cluster name
+         * @return kubernetes node builder
+         */
+        Builder clusterName(String clusterName);
+
+        /**
          * Returns kubernetes node builder with supplied hostname.
          *
          * @param hostname hostname of the node
@@ -284,6 +463,22 @@
         Builder type(Type type);
 
         /**
+         * Returns kubernetes node builder with supplied segment ID.
+         *
+         * @param segmentId kubernetes node segment ID
+         * @return kubernetes node builder
+         */
+        Builder segmentId(int segmentId);
+
+        /**
+         * Return kubernetes node builder with supplied mode.
+         *
+         * @param mode kubernetes CNI running mode
+         * @return kubernetes node builder
+         */
+        Builder mode(Mode mode);
+
+        /**
          * Returns kubernetes node builder with supplied integration bridge name.
          *
          * @param deviceId integration bridge device ID
@@ -308,6 +503,14 @@
         Builder localBridge(DeviceId deviceId);
 
         /**
+         * Returns kubernetes node builder with supplied tunnel bridge name.
+         *
+         * @param deviceId tunnel bridge device ID
+         * @return kubernetes node builder
+         */
+        Builder tunBridge(DeviceId deviceId);
+
+        /**
          * Returns kubernetes node builder with supplied external interface.
          *
          * @param intf external interface
diff --git a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNodeService.java b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNodeService.java
index 455e42c..96ce129 100644
--- a/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNodeService.java
+++ b/apps/k8s-node/api/src/main/java/org/onosproject/k8snode/api/K8sNodeService.java
@@ -36,6 +36,14 @@
     Set<K8sNode> nodes();
 
     /**
+     * Returns kubernetes nodes associated with cluster name.
+     *
+     * @param clusterName cluster name
+     * @return set of kubernetes nodes
+     */
+    Set<K8sNode> nodes(String clusterName);
+
+    /**
      * Returns all nodes with the specified type.
      *
      * @param type node type
diff --git a/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sApiConfigTest.java b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sApiConfigTest.java
index 4abac0f..22c39e4 100644
--- a/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sApiConfigTest.java
+++ b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sApiConfigTest.java
@@ -19,10 +19,14 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.k8snode.api.K8sApiConfig.Scheme;
 
 import static junit.framework.TestCase.assertEquals;
 import static org.onlab.junit.ImmutableClassChecker.assertThatClassIsImmutable;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.NORMAL;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.PASSTHROUGH;
 import static org.onosproject.k8snode.api.K8sApiConfig.Scheme.HTTP;
 import static org.onosproject.k8snode.api.K8sApiConfig.Scheme.HTTPS;
 import static org.onosproject.k8snode.api.K8sApiConfig.State.CONNECTED;
@@ -33,9 +37,19 @@
  */
 public final class DefaultK8sApiConfigTest {
 
+    private static final String CLUSTER_NAME = "kubernetes";
+
+    private static final int SEGMENT_ID_1 = 1;
+    private static final int SEGMENT_ID_2 = 2;
+
+    private static final IpPrefix EXT_NETWORK_CIDR = IpPrefix.valueOf("192.168.200.0/0");
+
     private static final Scheme SCHEME_1 = HTTP;
     private static final Scheme SCHEME_2 = HTTPS;
 
+    private static final Mode MODE_1 = NORMAL;
+    private static final Mode MODE_2 = PASSTHROUGH;
+
     private static final IpAddress IP_ADDRESS_1 = IpAddress.valueOf("192.168.0.200");
     private static final IpAddress IP_ADDRESS_2 = IpAddress.valueOf("192.168.0.201");
 
@@ -72,6 +86,10 @@
     @Before
     public void setUp() {
         config1 = DefaultK8sApiConfig.builder()
+                .clusterName(CLUSTER_NAME)
+                .segmentId(SEGMENT_ID_1)
+                .extNetworkCidr(EXT_NETWORK_CIDR)
+                .mode(NORMAL)
                 .scheme(SCHEME_1)
                 .ipAddress(IP_ADDRESS_1)
                 .port(PORT_1)
@@ -83,6 +101,10 @@
                 .build();
 
         sameAsConfig1 = DefaultK8sApiConfig.builder()
+                .clusterName(CLUSTER_NAME)
+                .segmentId(SEGMENT_ID_1)
+                .extNetworkCidr(EXT_NETWORK_CIDR)
+                .mode(NORMAL)
                 .scheme(SCHEME_1)
                 .ipAddress(IP_ADDRESS_1)
                 .port(PORT_1)
@@ -94,6 +116,10 @@
                 .build();
 
         config2 = DefaultK8sApiConfig.builder()
+                .clusterName(CLUSTER_NAME)
+                .segmentId(SEGMENT_ID_2)
+                .extNetworkCidr(EXT_NETWORK_CIDR)
+                .mode(PASSTHROUGH)
                 .scheme(SCHEME_2)
                 .ipAddress(IP_ADDRESS_2)
                 .port(PORT_2)
@@ -122,7 +148,11 @@
     public void testConstruction() {
         K8sApiConfig config = config1;
 
+        assertEquals(CLUSTER_NAME, config.clusterName());
+        assertEquals(SEGMENT_ID_1, config.segmentId());
+        assertEquals(EXT_NETWORK_CIDR, config.extNetworkCidr());
         assertEquals(SCHEME_1, config.scheme());
+        assertEquals(MODE_1, config.mode());
         assertEquals(IP_ADDRESS_1, config.ipAddress());
         assertEquals(PORT_1, config.port());
         assertEquals(CONNECTED, config.state());
diff --git a/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sHostTest.java b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sHostTest.java
new file mode 100644
index 0000000..693fce4
--- /dev/null
+++ b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sHostTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.api;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+
+import java.util.Set;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.onosproject.k8snode.api.K8sHostState.COMPLETE;
+import static org.onosproject.k8snode.api.K8sHostState.INIT;
+
+/**
+ * Unit test for DefaultK8sHost.
+ */
+public final class DefaultK8sHostTest {
+
+    private static final IpAddress HOST_IP_1 = IpAddress.valueOf("192.168.200.3");
+    private static final IpAddress HOST_IP_2 = IpAddress.valueOf("192.168.200.4");
+
+    private static final Set<String> NODE_NAMES_1 = ImmutableSet.of("1", "2");
+    private static final Set<String> NODE_NAMES_2 = ImmutableSet.of("3", "4");
+
+    private K8sHost refHost;
+
+    private static final K8sHost K8S_HOST_1 = createHost(
+            HOST_IP_1,
+            NODE_NAMES_1,
+            INIT
+    );
+
+    private static final K8sHost K8S_HOST_2 = createHost(
+            HOST_IP_1,
+            NODE_NAMES_1,
+            INIT
+    );
+
+    private static final K8sHost K8S_HOST_3 = createHost(
+            HOST_IP_2,
+            NODE_NAMES_2,
+            COMPLETE
+    );
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setUp() {
+        refHost = DefaultK8sHost.builder()
+                .hostIp(HOST_IP_1)
+                .nodeNames(NODE_NAMES_1)
+                .state(INIT)
+                .build();
+    }
+
+    /**
+     * Checks equals method works as expected.
+     */
+    @Test
+    public void testEquality() {
+        new EqualsTester().addEqualityGroup(K8S_HOST_1, K8S_HOST_2)
+                .addEqualityGroup(K8S_HOST_3)
+                .testEquals();
+    }
+
+    /**
+     * Test object construction.
+     */
+    @Test
+    public void testConstruction() {
+        assertEquals(refHost.hostIp(), HOST_IP_1);
+        assertEquals(refHost.nodeNames(), NODE_NAMES_1);
+        assertEquals(refHost.state(), INIT);
+    }
+
+    /**
+     * Checks the functionality of update state method.
+     */
+    @Test
+    public void testUpdateState() {
+        K8sHost updatedHost = refHost.updateState(COMPLETE);
+
+        assertEquals(updatedHost.hostIp(), HOST_IP_1);
+        assertEquals(updatedHost.nodeNames(), NODE_NAMES_1);
+        assertEquals(updatedHost.state(), COMPLETE);
+    }
+
+    private static K8sHost createHost(IpAddress hostIp, Set<String> nodeNames, K8sHostState state) {
+        return DefaultK8sHost.builder()
+                .hostIp(hostIp)
+                .nodeNames(nodeNames)
+                .state(state)
+                .build();
+    }
+}
diff --git a/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java
index 463d89d..26553a8 100644
--- a/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java
+++ b/apps/k8s-node/api/src/test/java/org/onosproject/k8snode/api/DefaultK8sNodeTest.java
@@ -41,10 +41,13 @@
 
     private static final IpAddress TEST_IP = IpAddress.valueOf("10.100.0.3");
 
+    private static final String CLUSTER_NAME = "cluster";
     private static final String HOSTNAME_1 = "hostname_1";
     private static final String HOSTNAME_2 = "hostname_2";
     private static final Device DEVICE_1 = createDevice(1);
     private static final Device DEVICE_2 = createDevice(2);
+    private static final int SEGMENT_ID_1 = 1;
+    private static final int SEGMENT_ID_2 = 2;
 
     private static final IpAddress MANAGEMENT_IP = IpAddress.valueOf("10.10.10.10");
     private static final IpAddress DATA_IP = IpAddress.valueOf("20.20.20.20");
@@ -64,8 +67,11 @@
     private K8sNode refNode;
 
     private static final K8sNode K8S_NODE_1 = createNode(
+            CLUSTER_NAME,
             HOSTNAME_1,
             MINION,
+            SEGMENT_ID_1,
+            DEVICE_1,
             DEVICE_1,
             DEVICE_1,
             DEVICE_1,
@@ -76,8 +82,11 @@
             EXT_GATEWAY_IP_1,
             POD_CIDR_1);
     private static final K8sNode K8S_NODE_2 = createNode(
+            CLUSTER_NAME,
             HOSTNAME_1,
             MINION,
+            SEGMENT_ID_1,
+            DEVICE_1,
             DEVICE_1,
             DEVICE_1,
             DEVICE_1,
@@ -88,8 +97,11 @@
             EXT_GATEWAY_IP_1,
             POD_CIDR_1);
     private static final K8sNode K8S_NODE_3 = createNode(
+            CLUSTER_NAME,
             HOSTNAME_2,
             MINION,
+            SEGMENT_ID_2,
+            DEVICE_2,
             DEVICE_2,
             DEVICE_2,
             DEVICE_2,
@@ -106,13 +118,16 @@
     @Before
     public void setUp() {
         refNode = DefaultK8sNode.builder()
+                .clusterName(CLUSTER_NAME)
                 .hostname(HOSTNAME_1)
                 .type(MINION)
+                .segmentId(SEGMENT_ID_1)
                 .managementIp(MANAGEMENT_IP)
                 .dataIp(DATA_IP)
                 .intgBridge(DEVICE_1.id())
                 .extBridge(DEVICE_1.id())
                 .localBridge(DEVICE_1.id())
+                .tunBridge(DEVICE_1.id())
                 .extIntf(BRIDGE_INTF_1)
                 .state(COMPLETE)
                 .extBridgeIp(EXT_BRIDGE_IP_1)
@@ -141,6 +156,7 @@
         assertEquals(refNode.intgBridge(), DEVICE_1.id());
         assertEquals(refNode.extBridge(), DEVICE_1.id());
         assertEquals(refNode.localBridge(), DEVICE_1.id());
+        assertEquals(refNode.tunBridge(), DEVICE_1.id());
     }
 
     /**
@@ -171,9 +187,12 @@
     public void testBuildWithoutHostname() {
         DefaultK8sNode.builder()
                 .type(MINION)
+                .clusterName(CLUSTER_NAME)
+                .segmentId(SEGMENT_ID_1)
                 .intgBridge(DEVICE_1.id())
                 .extBridge(DEVICE_1.id())
                 .localBridge(DEVICE_1.id())
+                .tunBridge(DEVICE_1.id())
                 .extIntf(BRIDGE_INTF_1)
                 .managementIp(TEST_IP)
                 .dataIp(TEST_IP)
@@ -191,9 +210,12 @@
     public void testBuildWithoutType() {
         DefaultK8sNode.builder()
                 .hostname(HOSTNAME_1)
+                .clusterName(CLUSTER_NAME)
+                .segmentId(SEGMENT_ID_1)
                 .intgBridge(DEVICE_1.id())
                 .extBridge(DEVICE_1.id())
                 .localBridge(DEVICE_1.id())
+                .tunBridge(DEVICE_1.id())
                 .extIntf(BRIDGE_INTF_1)
                 .managementIp(TEST_IP)
                 .dataIp(TEST_IP)
@@ -211,11 +233,14 @@
     @Test(expected = IllegalArgumentException.class)
     public void testBuildWithoutManagementIp() {
         DefaultK8sNode.builder()
+                .clusterName(CLUSTER_NAME)
                 .hostname(HOSTNAME_1)
                 .type(MINION)
+                .segmentId(SEGMENT_ID_1)
                 .intgBridge(DEVICE_1.id())
                 .extBridge(DEVICE_1.id())
                 .localBridge(DEVICE_1.id())
+                .tunBridge(DEVICE_1.id())
                 .extIntf(BRIDGE_INTF_1)
                 .dataIp(TEST_IP)
                 .state(INIT)
@@ -226,6 +251,7 @@
     }
 
     private void checkCommonProperties(K8sNode node) {
+        assertEquals(node.clusterName(), CLUSTER_NAME);
         assertEquals(node.hostname(), HOSTNAME_1);
         assertEquals(node.type(), MINION);
         assertEquals(node.managementIp(), MANAGEMENT_IP);
@@ -243,18 +269,21 @@
                 new ChassisId(1));
     }
 
-    private static K8sNode createNode(String hostname, Type type,
-                                      Device intgBridge, Device extBridge,
-                                      Device localBridge, String bridgeIntf,
+    private static K8sNode createNode(String clusterName, String hostname, Type type,
+                                      int segmentId, Device intgBridge, Device extBridge,
+                                      Device localBridge, Device tunBridge, String bridgeIntf,
                                       IpAddress ipAddr, K8sNodeState state,
                                       IpAddress extBridgeIp, IpAddress extGatewayIp,
                                       String podCidr) {
         return DefaultK8sNode.builder()
+                .clusterName(clusterName)
                 .hostname(hostname)
                 .type(type)
+                .segmentId(segmentId)
                 .intgBridge(intgBridge.id())
                 .extBridge(extBridge.id())
                 .localBridge(localBridge.id())
+                .tunBridge(tunBridge.id())
                 .extIntf(bridgeIntf)
                 .managementIp(ipAddr)
                 .dataIp(ipAddr)
diff --git a/apps/k8s-node/app/BUILD b/apps/k8s-node/app/BUILD
index ca5b850..b886139 100644
--- a/apps/k8s-node/app/BUILD
+++ b/apps/k8s-node/app/BUILD
@@ -10,6 +10,7 @@
     "@logging_interceptor//jar",
     "@jackson_dataformat_yaml//jar",
     "@snakeyaml//jar",
+    "@commons_net//jar",
 ]
 
 TEST_DEPS = TEST_ADAPTERS + TEST_REST + [
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java
index f421de0..2060712 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/cli/K8sNodeCheckCommand.java
@@ -27,16 +27,6 @@
 import org.onosproject.net.Port;
 import org.onosproject.net.device.DeviceService;
 
-import static org.onosproject.k8snode.api.Constants.EXTERNAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.GENEVE_TUNNEL;
-import static org.onosproject.k8snode.api.Constants.GRE_TUNNEL;
-import static org.onosproject.k8snode.api.Constants.INTEGRATION_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_EXTERNAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_LOCAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.LOCAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.LOCAL_TO_INTEGRATION_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.PHYSICAL_EXTERNAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.VXLAN_TUNNEL;
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 
 /**
@@ -71,22 +61,22 @@
         if (intgBridge != null) {
             print("%s %s=%s available=%s %s",
                     deviceService.isAvailable(intgBridge.id()) ? MSG_OK : MSG_ERROR,
-                    INTEGRATION_BRIDGE,
+                    node.intgBridgeName(),
                     intgBridge.id(),
                     deviceService.isAvailable(intgBridge.id()),
                     intgBridge.annotations());
-            printPortState(deviceService, node.intgBridge(), INTEGRATION_BRIDGE);
-            printPortState(deviceService, node.intgBridge(), INTEGRATION_TO_EXTERNAL_BRIDGE);
-            printPortState(deviceService, node.intgBridge(), INTEGRATION_TO_LOCAL_BRIDGE);
+            printPortState(deviceService, node.intgBridge(), node.intgBridgePortName());
+            printPortState(deviceService, node.intgBridge(), node.intgToExtPatchPortName());
+            printPortState(deviceService, node.intgBridge(), node.intgToLocalPatchPortName());
             if (node.dataIp() != null) {
-                printPortState(deviceService, node.intgBridge(), VXLAN_TUNNEL);
-                printPortState(deviceService, node.intgBridge(), GRE_TUNNEL);
-                printPortState(deviceService, node.intgBridge(), GENEVE_TUNNEL);
+                printPortState(deviceService, node.intgBridge(), node.vxlanPortName());
+                printPortState(deviceService, node.intgBridge(), node.grePortName());
+                printPortState(deviceService, node.intgBridge(), node.genevePortName());
             }
         } else {
             print("%s %s=%s is not available",
                     MSG_ERROR,
-                    INTEGRATION_BRIDGE,
+                    node.intgBridgeName(),
                     node.intgBridge());
         }
 
@@ -95,16 +85,16 @@
         if (extBridge != null) {
             print("%s %s=%s available=%s %s",
                     deviceService.isAvailable(extBridge.id()) ? MSG_OK : MSG_ERROR,
-                    EXTERNAL_BRIDGE,
+                    node.extBridgeName(),
                     extBridge.id(),
                     deviceService.isAvailable(extBridge.id()),
                     extBridge.annotations());
-            printPortState(deviceService, node.extBridge(), EXTERNAL_BRIDGE);
-            printPortState(deviceService, node.extBridge(), PHYSICAL_EXTERNAL_BRIDGE);
+            printPortState(deviceService, node.extBridge(), node.extBridgePortName());
+            printPortState(deviceService, node.extBridge(), node.extToIntgPatchPortName());
         } else {
             print("%s %s=%s is not available",
                     MSG_ERROR,
-                    EXTERNAL_BRIDGE,
+                    node.extBridgeName(),
                     node.extBridge());
         }
 
@@ -113,12 +103,12 @@
         if (localBridge != null) {
             print("%s %s=%s available=%s %s",
                     deviceService.isAvailable(localBridge.id()) ? MSG_OK : MSG_ERROR,
-                    LOCAL_BRIDGE,
+                    node.localBridgeName(),
                     localBridge.id(),
                     deviceService.isAvailable(localBridge.id()),
                     localBridge.annotations());
-            printPortState(deviceService, node.localBridge(), LOCAL_BRIDGE);
-            printPortState(deviceService, node.localBridge(), LOCAL_TO_INTEGRATION_BRIDGE);
+            printPortState(deviceService, node.localBridge(), node.localBridgePortName());
+            printPortState(deviceService, node.localBridge(), node.localToIntgPatchPortName());
         }
     }
 
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/HostNodesInfoCodec.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/HostNodesInfoCodec.java
new file mode 100644
index 0000000..4287418
--- /dev/null
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/HostNodesInfoCodec.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.k8snode.api.DefaultHostNodesInfo;
+import org.onosproject.k8snode.api.HostNodesInfo;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.onlab.util.Tools.nullIsIllegal;
+
+/**
+ * HostNodesInfo codec used for serializing and de-serializing JSON.
+ */
+public final class HostNodesInfoCodec extends JsonCodec<HostNodesInfo> {
+
+    private static final String HOST_IP = "hostIp";
+    private static final String NODES = "nodes";
+
+    private static final String MISSING_MESSAGE = " is required in HostNodesInfo";
+
+    @Override
+    public ObjectNode encode(HostNodesInfo entity, CodecContext context) {
+        ObjectNode node = context.mapper().createObjectNode()
+                .put(HOST_IP, entity.hostIp().toString());
+
+        ArrayNode nodes = context.mapper().createArrayNode();
+        entity.nodes().forEach(nodes::add);
+        node.set(NODES, nodes);
+        return node;
+    }
+
+    @Override
+    public HostNodesInfo decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        IpAddress hostIp = IpAddress.valueOf(nullIsIllegal(
+                json.get(HOST_IP).asText(), HOST_IP + MISSING_MESSAGE));
+
+        Set<String> nodes = new HashSet<>();
+        ArrayNode nodesJson = (ArrayNode) json.get(NODES);
+
+        for (JsonNode cidrJson : nodesJson) {
+            nodes.add(cidrJson.asText());
+        }
+
+        return new DefaultHostNodesInfo.Builder()
+                .hostIp(hostIp)
+                .nodes(nodes)
+                .build();
+    }
+}
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sApiConfigCodec.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sApiConfigCodec.java
index b7da2a0..5b8c05e 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sApiConfigCodec.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sApiConfigCodec.java
@@ -16,16 +16,26 @@
 package org.onosproject.k8snode.codec;
 
 import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.apache.commons.lang.StringUtils;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.k8snode.api.DefaultK8sApiConfig;
+import org.onosproject.k8snode.api.HostNodesInfo;
 import org.onosproject.k8snode.api.K8sApiConfig;
+import org.onosproject.k8snode.api.K8sApiConfig.Mode;
 import org.onosproject.k8snode.api.K8sApiConfig.Scheme;
 
+import java.util.HashSet;
+import java.util.Set;
+
 import static org.onlab.util.Tools.nullIsIllegal;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_CLUSTER_NAME;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_CONFIG_MODE;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_SEGMENT_ID;
 import static org.onosproject.k8snode.api.K8sApiConfig.Scheme.HTTPS;
 import static org.onosproject.k8snode.api.K8sApiConfig.State.DISCONNECTED;
 
@@ -34,7 +44,11 @@
  */
 public final class K8sApiConfigCodec extends JsonCodec<K8sApiConfig> {
 
+    private static final String CLUSTER_NAME = "clusterName";
+    private static final String SEGMENT_ID = "segmentId";
+    private static final String EXT_NETWORK_CIDR = "extNetworkCidr";
     private static final String SCHEME = "scheme";
+    private static final String MODE = "mode";
     private static final String IP_ADDRESS = "ipAddress";
     private static final String PORT = "port";
     private static final String STATE = "state";
@@ -42,12 +56,16 @@
     private static final String CA_CERT_DATA = "caCertData";
     private static final String CLIENT_CERT_DATA = "clientCertData";
     private static final String CLIENT_KEY_DATA = "clientKeyData";
+    private static final String HOST_NODES_INFO = "hostNodesInfo";
 
     private static final String MISSING_MESSAGE = " is required in K8sApiConfig";
 
     @Override
     public ObjectNode encode(K8sApiConfig entity, CodecContext context) {
         ObjectNode node = context.mapper().createObjectNode()
+                .put(CLUSTER_NAME, entity.clusterName())
+                .put(SEGMENT_ID, entity.segmentId())
+                .put(MODE, entity.mode().name())
                 .put(SCHEME, entity.scheme().name())
                 .put(IP_ADDRESS, entity.ipAddress().toString())
                 .put(PORT, entity.port())
@@ -80,6 +98,17 @@
             }
         }
 
+        if (entity.extNetworkCidr() != null) {
+            node.put(EXT_NETWORK_CIDR, entity.extNetworkCidr().toString());
+        }
+
+        ArrayNode infos = context.mapper().createArrayNode();
+        entity.infos().forEach(info -> {
+            ObjectNode infoJson = context.codec(HostNodesInfo.class).encode(info, context);
+            infos.add(infoJson);
+        });
+        node.set(HOST_NODES_INFO, infos);
+
         return node;
     }
 
@@ -89,6 +118,32 @@
             return null;
         }
 
+        JsonNode clusterNameJson = json.get(CLUSTER_NAME);
+        String clusterNameStr = "";
+
+        if (clusterNameJson == null) {
+            clusterNameStr = DEFAULT_CLUSTER_NAME;
+        } else {
+            clusterNameStr = clusterNameJson.asText();
+        }
+
+        JsonNode segmentIdJson = json.get(SEGMENT_ID);
+        int segmentId = DEFAULT_SEGMENT_ID;
+
+        if (segmentIdJson != null) {
+            segmentId = segmentIdJson.asInt();
+        }
+
+        JsonNode modeJson = json.get(MODE);
+        String modeStr = "";
+        if (modeJson == null) {
+            modeStr = DEFAULT_CONFIG_MODE;
+        } else {
+            modeStr = modeJson.asText();
+        }
+
+        Mode mode = Mode.valueOf(modeStr);
+
         Scheme scheme = Scheme.valueOf(nullIsIllegal(
                 json.get(SCHEME).asText(), SCHEME + MISSING_MESSAGE));
         IpAddress ipAddress = IpAddress.valueOf(nullIsIllegal(
@@ -96,6 +151,9 @@
         int port = json.get(PORT).asInt();
 
         K8sApiConfig.Builder builder = DefaultK8sApiConfig.builder()
+                .clusterName(clusterNameStr)
+                .segmentId(segmentId)
+                .mode(mode)
                 .scheme(scheme)
                 .ipAddress(ipAddress)
                 .port(port)
@@ -157,6 +215,22 @@
             builder.clientKeyData(clientKeyData);
         }
 
+        JsonNode extNetworkCidrJson = json.get(EXT_NETWORK_CIDR);
+        if (extNetworkCidrJson != null) {
+            builder.extNetworkCidr(IpPrefix.valueOf(extNetworkCidrJson.asText()));
+        }
+
+        Set<HostNodesInfo> infos = new HashSet<>();
+        ArrayNode infosJson = (ArrayNode) json.get(HOST_NODES_INFO);
+        if (infosJson != null) {
+            for (JsonNode infoJson : infosJson) {
+                HostNodesInfo info = context.codec(HostNodesInfo.class)
+                        .decode((ObjectNode) infoJson, context);
+                infos.add(info);
+            }
+            builder.infos(infos);
+        }
+
         return builder.build();
     }
 }
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sHostCodec.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sHostCodec.java
new file mode 100644
index 0000000..2edb66d
--- /dev/null
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sHostCodec.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.codec;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.k8snode.api.DefaultK8sHost;
+import org.onosproject.k8snode.api.K8sHost;
+import org.onosproject.k8snode.api.K8sHostState;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubernetes host codec used for serializing and de-serializing JSON string.
+ */
+public final class K8sHostCodec extends JsonCodec<K8sHost> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String HOST_IP = "hostIp";
+    private static final String NODE_NAMES = "nodeNames";
+    private static final String STATE = "state";
+
+    private static final String MISSING_MESSAGE = " is required in K8sHost";
+
+    @Override
+    public ObjectNode encode(K8sHost entity, CodecContext context) {
+        checkNotNull(entity, "Kubernetes host cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(HOST_IP, entity.hostIp().toString())
+                .put(STATE, entity.state().name());
+
+        ArrayNode nodes = context.mapper().createArrayNode();
+        entity.nodeNames().forEach(nodes::add);
+        result.set(NODE_NAMES, nodes);
+
+        return result;
+    }
+
+    @Override
+    public K8sHost decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        IpAddress hostIp = IpAddress.valueOf(nullIsIllegal(json.get(HOST_IP).asText(),
+                HOST_IP + MISSING_MESSAGE));
+        ArrayNode nodeNamesJson = (ArrayNode) json.get(NODE_NAMES);
+        Set<String> nodeNames = new HashSet<>();
+        nodeNamesJson.forEach(n -> nodeNames.add(n.asText()));
+
+        return DefaultK8sHost.builder()
+                .hostIp(hostIp)
+                .state(K8sHostState.INIT)
+                .nodeNames(nodeNames)
+                .build();
+    }
+}
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java
index 41bad16..8793c46 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/codec/K8sNodeCodec.java
@@ -17,6 +17,7 @@
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.apache.commons.lang.StringUtils;
 import org.onlab.packet.IpAddress;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
@@ -28,6 +29,8 @@
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onlab.util.Tools.nullIsIllegal;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_CLUSTER_NAME;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_SEGMENT_ID;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -37,13 +40,16 @@
 
     private final Logger log = getLogger(getClass());
 
+    private static final String CLUSTER_NAME = "clusterName";
     private static final String HOSTNAME = "hostname";
     private static final String TYPE = "type";
+    private static final String SEGMENT_ID = "segmentId";
     private static final String MANAGEMENT_IP = "managementIp";
     private static final String DATA_IP = "dataIp";
     private static final String INTEGRATION_BRIDGE = "integrationBridge";
     private static final String EXTERNAL_BRIDGE = "externalBridge";
     private static final String LOCAL_BRIDGE = "localBridge";
+    private static final String TUNNEL_BRIDGE = "tunnelBridge";
     private static final String STATE = "state";
     private static final String EXTERNAL_INTF = "externalInterface";
     private static final String EXTERNAL_BRIDGE_IP = "externalBridgeIp";
@@ -56,8 +62,10 @@
         checkNotNull(node, "Kubernetes node cannot be null");
 
         ObjectNode result = context.mapper().createObjectNode()
+                .put(CLUSTER_NAME, node.clusterName())
                 .put(HOSTNAME, node.hostname())
                 .put(TYPE, node.type().name())
+                .put(SEGMENT_ID, node.segmentId())
                 .put(STATE, node.state().name())
                 .put(MANAGEMENT_IP, node.managementIp().toString());
 
@@ -73,6 +81,10 @@
             result.put(LOCAL_BRIDGE, node.localBridge().toString());
         }
 
+        if (node.tunBridge() != null) {
+            result.put(TUNNEL_BRIDGE, node.tunBridge().toString());
+        }
+
         if (node.dataIp() != null) {
             result.put(DATA_IP, node.dataIp().toString());
         }
@@ -98,6 +110,12 @@
             return null;
         }
 
+        String clusterName = json.get(CLUSTER_NAME).asText();
+
+        if (StringUtils.isEmpty(clusterName)) {
+            clusterName = DEFAULT_CLUSTER_NAME;
+        }
+
         String hostname = nullIsIllegal(json.get(HOSTNAME).asText(),
                 HOSTNAME + MISSING_MESSAGE);
         String type = nullIsIllegal(json.get(TYPE).asText(),
@@ -106,6 +124,7 @@
                 MANAGEMENT_IP + MISSING_MESSAGE);
 
         DefaultK8sNode.Builder nodeBuilder = DefaultK8sNode.builder()
+                .clusterName(clusterName)
                 .hostname(hostname)
                 .type(K8sNode.Type.valueOf(type))
                 .state(K8sNodeState.INIT)
@@ -115,6 +134,13 @@
             nodeBuilder.dataIp(IpAddress.valueOf(json.get(DATA_IP).asText()));
         }
 
+        JsonNode segmentIdJson = json.get(SEGMENT_ID);
+        int segmentId = DEFAULT_SEGMENT_ID;
+        if (segmentIdJson != null) {
+            segmentId = segmentIdJson.asInt();
+        }
+        nodeBuilder.segmentId(segmentId);
+
         JsonNode intBridgeJson = json.get(INTEGRATION_BRIDGE);
         if (intBridgeJson != null) {
             nodeBuilder.intgBridge(DeviceId.deviceId(intBridgeJson.asText()));
@@ -130,6 +156,11 @@
             nodeBuilder.localBridge(DeviceId.deviceId(localBridgeJson.asText()));
         }
 
+        JsonNode tunBridgeJson = json.get(TUNNEL_BRIDGE);
+        if (tunBridgeJson != null) {
+            nodeBuilder.tunBridge(DeviceId.deviceId(tunBridgeJson.asText()));
+        }
+
         JsonNode extIntfJson = json.get(EXTERNAL_INTF);
         if (extIntfJson != null) {
             nodeBuilder.extIntf(extIntfJson.asText());
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java
index 01a5043..33415a8 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sApiConfigHandler.java
@@ -25,6 +25,8 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.k8snode.api.DefaultK8sNode;
+import org.onosproject.k8snode.api.ExternalNetworkService;
+import org.onosproject.k8snode.api.HostNodesInfo;
 import org.onosproject.k8snode.api.K8sApiConfig;
 import org.onosproject.k8snode.api.K8sApiConfigAdminService;
 import org.onosproject.k8snode.api.K8sApiConfigEvent;
@@ -44,6 +46,9 @@
 
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snode.api.Constants.DEFAULT_CLUSTER_NAME;
+import static org.onosproject.k8snode.api.Constants.EXTERNAL_TO_ROUTER;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.PASSTHROUGH;
 import static org.onosproject.k8snode.api.K8sNode.Type.MASTER;
 import static org.onosproject.k8snode.api.K8sNode.Type.MINION;
 import static org.onosproject.k8snode.api.K8sNodeService.APP_ID;
@@ -65,6 +70,9 @@
     private static final String EXT_GATEWAY_IP = "external.gateway.ip";
     private static final String EXT_INTF_NAME = "external.interface.name";
 
+    private static final String DEFAULT_GATEWAY_IP = "127.0.0.1";
+    private static final String DEFAULT_BRIDGE_IP = "127.0.0.1";
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected CoreService coreService;
 
@@ -80,6 +88,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY)
     protected K8sNodeAdminService k8sNodeAdminService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ExternalNetworkService extNetworkService;
+
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
 
@@ -127,21 +138,32 @@
         }
 
         k8sClient.nodes().list().getItems().forEach(n ->
-            k8sNodeAdminService.createNode(buildK8sNode(n))
+            k8sNodeAdminService.createNode(buildK8sNode(n, config))
         );
     }
 
-    private K8sNode buildK8sNode(Node node) {
+    private K8sNode buildK8sNode(Node node, K8sApiConfig config) {
         String hostname = node.getMetadata().getName();
         IpAddress managementIp = null;
         IpAddress dataIp = null;
 
-        for (NodeAddress nodeAddress:node.getStatus().getAddresses()) {
-            // we need to consider assigning managementIp and dataIp differently
-            // FIXME: ExternalIp is not considered currently
-            if (nodeAddress.getType().equals(INTERNAL_IP)) {
-                managementIp = IpAddress.valueOf(nodeAddress.getAddress());
-                dataIp = IpAddress.valueOf(nodeAddress.getAddress());
+        // pass-through mode: we use host IP as the management and data IP
+        // normal mode: we use K8S node's internal IP as the management and data IP
+        if (config.mode() == PASSTHROUGH) {
+            HostNodesInfo info = config.infos().stream().filter(h -> h.nodes()
+                    .contains(hostname)).findAny().orElse(null);
+            if (info == null) {
+                log.error("None of the nodes were found in the host nodes info mapping list");
+            } else {
+                managementIp = info.hostIp();
+                dataIp = info.hostIp();
+            }
+        } else {
+            for (NodeAddress nodeAddress:node.getStatus().getAddresses()) {
+                if (nodeAddress.getType().equals(INTERNAL_IP)) {
+                    managementIp = IpAddress.valueOf(nodeAddress.getAddress());
+                    dataIp = IpAddress.valueOf(nodeAddress.getAddress());
+                }
             }
         }
 
@@ -162,17 +184,37 @@
 
         Map<String, String> annots = node.getMetadata().getAnnotations();
 
-        String extIntf = annots.get(EXT_INTF_NAME);
-        String extGatewayIpStr = annots.get(EXT_GATEWAY_IP);
-        String extBridgeIpStr = annots.get(EXT_BRIDGE_IP);
+        String extIntf = "";
+        String extGatewayIpStr = DEFAULT_GATEWAY_IP;
+        String extBridgeIpStr = DEFAULT_BRIDGE_IP;
+
+        if (config.mode() == PASSTHROUGH) {
+            extNetworkService.registerNetwork(config.extNetworkCidr());
+            extIntf = EXTERNAL_TO_ROUTER + "-" + config.clusterShortName();
+            IpAddress gatewayIp = extNetworkService.getGatewayIp(config.extNetworkCidr());
+            IpAddress bridgeIp = extNetworkService.allocateIp(config.extNetworkCidr());
+            if (gatewayIp != null) {
+                extGatewayIpStr = gatewayIp.toString();
+            }
+            if (bridgeIp != null) {
+                extBridgeIpStr = bridgeIp.toString();
+            }
+        } else {
+            extIntf = annots.get(EXT_INTF_NAME);
+            extGatewayIpStr = annots.get(EXT_GATEWAY_IP);
+            extBridgeIpStr = annots.get(EXT_BRIDGE_IP);
+        }
 
         return DefaultK8sNode.builder()
+                .clusterName(DEFAULT_CLUSTER_NAME)
                 .hostname(hostname)
                 .managementIp(managementIp)
                 .dataIp(dataIp)
                 .extIntf(extIntf)
                 .type(nodeType)
+                .segmentId(config.segmentId())
                 .state(PRE_ON_BOARD)
+                .mode(config.mode())
                 .extBridgeIp(IpAddress.valueOf(extBridgeIpStr))
                 .extGatewayIp(IpAddress.valueOf(extGatewayIpStr))
                 .podCidr(node.getSpec().getPodCIDR())
@@ -209,6 +251,7 @@
             if (checkApiServerConfig(config)) {
                 K8sApiConfig newConfig = config.updateState(K8sApiConfig.State.CONNECTED);
                 k8sApiConfigAdminService.updateApiConfig(newConfig);
+
                 bootstrapK8sNodes(config);
             }
         }
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sNodeHandler.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sNodeHandler.java
index e3ca070..7e99f47 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sNodeHandler.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DefaultK8sNodeHandler.java
@@ -42,7 +42,7 @@
 import org.onosproject.net.behaviour.PatchDescription;
 import org.onosproject.net.behaviour.TunnelDescription;
 import org.onosproject.net.behaviour.TunnelEndPoints;
-import org.onosproject.net.behaviour.TunnelKeys;
+import org.onosproject.net.behaviour.TunnelKey;
 import org.onosproject.net.device.DeviceAdminService;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
@@ -68,19 +68,10 @@
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.packet.TpPort.tpPort;
 import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.k8snode.api.Constants.EXTERNAL_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.GENEVE;
-import static org.onosproject.k8snode.api.Constants.GENEVE_TUNNEL;
 import static org.onosproject.k8snode.api.Constants.GRE;
-import static org.onosproject.k8snode.api.Constants.GRE_TUNNEL;
-import static org.onosproject.k8snode.api.Constants.INTEGRATION_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_EXTERNAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.INTEGRATION_TO_LOCAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.LOCAL_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.LOCAL_TO_INTEGRATION_BRIDGE;
-import static org.onosproject.k8snode.api.Constants.PHYSICAL_EXTERNAL_BRIDGE;
 import static org.onosproject.k8snode.api.Constants.VXLAN;
-import static org.onosproject.k8snode.api.Constants.VXLAN_TUNNEL;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.NORMAL;
 import static org.onosproject.k8snode.api.K8sNodeService.APP_ID;
 import static org.onosproject.k8snode.api.K8sNodeState.COMPLETE;
 import static org.onosproject.k8snode.api.K8sNodeState.DEVICE_CREATED;
@@ -196,13 +187,19 @@
             return;
         }
         if (!deviceService.isAvailable(k8sNode.intgBridge())) {
-            createBridge(k8sNode, INTEGRATION_BRIDGE, k8sNode.intgBridge());
+            createBridge(k8sNode, k8sNode.intgBridgeName(), k8sNode.intgBridge());
         }
         if (!deviceService.isAvailable(k8sNode.extBridge())) {
-            createBridge(k8sNode, EXTERNAL_BRIDGE, k8sNode.extBridge());
+            createBridge(k8sNode, k8sNode.extBridgeName(), k8sNode.extBridge());
         }
         if (!deviceService.isAvailable(k8sNode.localBridge())) {
-            createBridge(k8sNode, LOCAL_BRIDGE, k8sNode.localBridge());
+            createBridge(k8sNode, k8sNode.localBridgeName(), k8sNode.localBridge());
+        }
+
+        if (k8sNode.mode() == NORMAL) {
+            if (!deviceService.isAvailable(k8sNode.tunBridge())) {
+                createBridge(k8sNode, k8sNode.tunBridgeName(), k8sNode.tunBridge());
+            }
         }
     }
 
@@ -217,19 +214,21 @@
             // create patch ports between integration and external bridges
             createPatchInterfaces(k8sNode);
 
-            if (k8sNode.dataIp() != null &&
-                    !isIntfEnabled(k8sNode, VXLAN_TUNNEL)) {
-                createVxlanTunnelInterface(k8sNode);
-            }
+            if (k8sNode.mode() == NORMAL) {
+                if (k8sNode.dataIp() != null &&
+                        !isIntfEnabled(k8sNode, k8sNode.vxlanPortName())) {
+                    createVxlanTunnelInterface(k8sNode);
+                }
 
-            if (k8sNode.dataIp() != null &&
-                    !isIntfEnabled(k8sNode, GRE_TUNNEL)) {
-                createGreTunnelInterface(k8sNode);
-            }
+                if (k8sNode.dataIp() != null &&
+                        !isIntfEnabled(k8sNode, k8sNode.grePortName())) {
+                    createGreTunnelInterface(k8sNode);
+                }
 
-            if (k8sNode.dataIp() != null &&
-                    !isIntfEnabled(k8sNode, GENEVE_TUNNEL)) {
-                createGeneveTunnelInterface(k8sNode);
+                if (k8sNode.dataIp() != null &&
+                        !isIntfEnabled(k8sNode, k8sNode.genevePortName())) {
+                    createGeneveTunnelInterface(k8sNode);
+                }
             }
         } catch (Exception e) {
             log.error("Exception occurred because of {}", e);
@@ -324,7 +323,7 @@
      * @param k8sNode       kubernetes node
      */
     private void createVxlanTunnelInterface(K8sNode k8sNode) {
-        createTunnelInterface(k8sNode, VXLAN, VXLAN_TUNNEL);
+        createTunnelInterface(k8sNode, VXLAN, k8sNode.vxlanPortName());
     }
 
     /**
@@ -333,7 +332,7 @@
      * @param k8sNode       kubernetes node
      */
     private void createGreTunnelInterface(K8sNode k8sNode) {
-        createTunnelInterface(k8sNode, GRE, GRE_TUNNEL);
+        createTunnelInterface(k8sNode, GRE, k8sNode.grePortName());
     }
 
     /**
@@ -342,7 +341,7 @@
      * @param k8sNode       kubernetes node
      */
     private void createGeneveTunnelInterface(K8sNode k8sNode) {
-        createTunnelInterface(k8sNode, GENEVE, GENEVE_TUNNEL);
+        createTunnelInterface(k8sNode, GENEVE, k8sNode.genevePortName());
     }
 
     private void createPatchInterfaces(K8sNode k8sNode) {
@@ -355,40 +354,61 @@
         // integration bridge -> external bridge
         PatchDescription brIntExtPatchDesc =
                 DefaultPatchDescription.builder()
-                .deviceId(INTEGRATION_BRIDGE)
-                .ifaceName(INTEGRATION_TO_EXTERNAL_BRIDGE)
-                .peer(PHYSICAL_EXTERNAL_BRIDGE)
+                .deviceId(k8sNode.intgBridgeName())
+                .ifaceName(k8sNode.intgToExtPatchPortName())
+                .peer(k8sNode.extToIntgPatchPortName())
+                .build();
+
+        // integration bridge -> tunnel bridge
+        PatchDescription brIntTunPatchDesc =
+                DefaultPatchDescription.builder()
+                .deviceId(k8sNode.intgBridgeName())
+                .ifaceName(k8sNode.intgToTunPatchPortName())
+                .peer(k8sNode.tunToIntgPatchPortName())
                 .build();
 
         // external bridge -> integration bridge
         PatchDescription brExtIntPatchDesc =
                 DefaultPatchDescription.builder()
-                .deviceId(EXTERNAL_BRIDGE)
-                .ifaceName(PHYSICAL_EXTERNAL_BRIDGE)
-                .peer(INTEGRATION_TO_EXTERNAL_BRIDGE)
+                .deviceId(k8sNode.extBridgeName())
+                .ifaceName(k8sNode.extToIntgPatchPortName())
+                .peer(k8sNode.intgToExtPatchPortName())
                 .build();
 
         // integration bridge -> local bridge
         PatchDescription brIntLocalPatchDesc =
                 DefaultPatchDescription.builder()
-                        .deviceId(INTEGRATION_BRIDGE)
-                        .ifaceName(INTEGRATION_TO_LOCAL_BRIDGE)
-                        .peer(LOCAL_TO_INTEGRATION_BRIDGE)
-                        .build();
+                .deviceId(k8sNode.intgBridgeName())
+                .ifaceName(k8sNode.intgToLocalPatchPortName())
+                .peer(k8sNode.localToIntgPatchPortName())
+                .build();
 
         // local bridge -> integration bridge
         PatchDescription brLocalIntPatchDesc =
                 DefaultPatchDescription.builder()
-                        .deviceId(LOCAL_BRIDGE)
-                        .ifaceName(LOCAL_TO_INTEGRATION_BRIDGE)
-                        .peer(INTEGRATION_TO_LOCAL_BRIDGE)
-                        .build();
+                .deviceId(k8sNode.localBridgeName())
+                .ifaceName(k8sNode.localToIntgPatchPortName())
+                .peer(k8sNode.intgToLocalPatchPortName())
+                .build();
 
         InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
-        ifaceConfig.addPatchMode(INTEGRATION_TO_EXTERNAL_BRIDGE, brIntExtPatchDesc);
-        ifaceConfig.addPatchMode(PHYSICAL_EXTERNAL_BRIDGE, brExtIntPatchDesc);
-        ifaceConfig.addPatchMode(INTEGRATION_TO_LOCAL_BRIDGE, brIntLocalPatchDesc);
-        ifaceConfig.addPatchMode(LOCAL_TO_INTEGRATION_BRIDGE, brLocalIntPatchDesc);
+        ifaceConfig.addPatchMode(k8sNode.intgToExtPatchPortName(), brIntExtPatchDesc);
+        ifaceConfig.addPatchMode(k8sNode.extToIntgPatchPortName(), brExtIntPatchDesc);
+        ifaceConfig.addPatchMode(k8sNode.intgToLocalPatchPortName(), brIntLocalPatchDesc);
+        ifaceConfig.addPatchMode(k8sNode.localToIntgPatchPortName(), brLocalIntPatchDesc);
+        ifaceConfig.addPatchMode(k8sNode.intgToTunPatchPortName(), brIntTunPatchDesc);
+
+        if (k8sNode.mode() == NORMAL) {
+            // tunnel bridge -> integration bridge
+            PatchDescription brTunIntPatchDesc =
+                    DefaultPatchDescription.builder()
+                    .deviceId(k8sNode.tunBridgeName())
+                    .ifaceName(k8sNode.tunToIntgPatchPortName())
+                    .peer(k8sNode.intgToTunPatchPortName())
+                    .build();
+
+            ifaceConfig.addPatchMode(k8sNode.tunToIntgPatchPortName(), brTunIntPatchDesc);
+        }
     }
 
     /**
@@ -408,7 +428,7 @@
             return;
         }
 
-        TunnelDescription tunnelDesc = buildTunnelDesc(type, intfName);
+        TunnelDescription tunnelDesc = buildTunnelDesc(k8sNode, type, intfName);
 
         InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
         ifaceConfig.addTunnelMode(intfName, tunnelDesc);
@@ -420,14 +440,16 @@
      * @param type      network type
      * @return tunnel description
      */
-    private TunnelDescription buildTunnelDesc(String type, String intfName) {
+    private TunnelDescription buildTunnelDesc(K8sNode k8sNode,
+                                              String type, String intfName) {
+        TunnelKey<String> key = new TunnelKey<>(k8sNode.tunnelKey());
         if (VXLAN.equals(type) || GRE.equals(type) || GENEVE.equals(type)) {
             TunnelDescription.Builder tdBuilder =
                     DefaultTunnelDescription.builder()
-                            .deviceId(INTEGRATION_BRIDGE)
+                            .deviceId(k8sNode.tunBridgeName())
                             .ifaceName(intfName)
                             .remote(TunnelEndPoints.flowTunnelEndpoint())
-                            .key(TunnelKeys.flowTunnelKey());
+                            .key(key);
 
             switch (type) {
                 case VXLAN:
@@ -457,8 +479,8 @@
      * @return true if the given interface is enabled, false otherwise
      */
     private boolean isIntfEnabled(K8sNode k8sNode, String intf) {
-        return deviceService.isAvailable(k8sNode.intgBridge()) &&
-                deviceService.getPorts(k8sNode.intgBridge()).stream()
+        return deviceService.isAvailable(k8sNode.tunBridge()) &&
+                deviceService.getPorts(k8sNode.tunBridge()).stream()
                         .anyMatch(port -> Objects.equals(
                                 port.annotations().value(PORT_NAME), intf) &&
                                 port.isEnabled());
@@ -504,10 +526,16 @@
             log.error("Exception caused during init state checking...");
         }
 
-        return k8sNode.intgBridge() != null && k8sNode.extBridge() != null &&
+        boolean result = k8sNode.intgBridge() != null && k8sNode.extBridge() != null &&
                 deviceService.isAvailable(k8sNode.intgBridge()) &&
                 deviceService.isAvailable(k8sNode.extBridge()) &&
                 deviceService.isAvailable(k8sNode.localBridge());
+
+        if (k8sNode.mode() == NORMAL) {
+            return result && deviceService.isAvailable(k8sNode.tunBridge());
+        } else {
+            return result;
+        }
     }
 
     private boolean isDeviceCreatedStateDone(K8sNode k8sNode) {
@@ -520,17 +548,19 @@
             log.error("Exception caused during init state checking...");
         }
 
-        if (k8sNode.dataIp() != null &&
-                !isIntfEnabled(k8sNode, VXLAN_TUNNEL)) {
-            return false;
-        }
-        if (k8sNode.dataIp() != null &&
-                !isIntfEnabled(k8sNode, GRE_TUNNEL)) {
-            return false;
-        }
-        if (k8sNode.dataIp() != null &&
-                !isIntfEnabled(k8sNode, GENEVE_TUNNEL)) {
-            return false;
+        if (k8sNode.mode() == NORMAL) {
+            if (k8sNode.dataIp() != null &&
+                    !isIntfEnabled(k8sNode, k8sNode.vxlanPortName())) {
+                return false;
+            }
+            if (k8sNode.dataIp() != null &&
+                    !isIntfEnabled(k8sNode, k8sNode.grePortName())) {
+                return false;
+            }
+            if (k8sNode.dataIp() != null &&
+                    !isIntfEnabled(k8sNode, k8sNode.genevePortName())) {
+                return false;
+            }
         }
 
         return true;
@@ -574,13 +604,18 @@
         }
 
         // delete integration bridge from the node
-        client.dropBridge(INTEGRATION_BRIDGE);
+        client.dropBridge(k8sNode.intgBridgeName());
 
         // delete external bridge from the node
-        client.dropBridge(EXTERNAL_BRIDGE);
+        client.dropBridge(k8sNode.extBridgeName());
 
         // delete local bridge from the node
-        client.dropBridge(LOCAL_BRIDGE);
+        client.dropBridge(k8sNode.localBridgeName());
+
+        if (k8sNode.mode() == NORMAL) {
+            // delete tunnel bridge from the node
+            client.dropBridge(k8sNode.tunBridgeName());
+        }
 
         // disconnect ovsdb
         client.disconnect();
@@ -711,9 +746,9 @@
                         Port port = event.port();
                         String portName = port.annotations().value(PORT_NAME);
                         if (k8sNode.state() == DEVICE_CREATED && (
-                                Objects.equals(portName, VXLAN_TUNNEL) ||
-                                        Objects.equals(portName, GRE_TUNNEL) ||
-                                        Objects.equals(portName, GENEVE_TUNNEL))) {
+                                Objects.equals(portName, k8sNode.vxlanPortName()) ||
+                                        Objects.equals(portName, k8sNode.grePortName()) ||
+                                        Objects.equals(portName, k8sNode.genevePortName()))) {
                             log.info("Interface {} added or updated to {}",
                                     portName, device.id());
                             bootstrapNode(k8sNode);
@@ -736,9 +771,9 @@
                         Port port = event.port();
                         String portName = port.annotations().value(PORT_NAME);
                         if (k8sNode.state() == COMPLETE && (
-                                Objects.equals(portName, VXLAN_TUNNEL) ||
-                                        Objects.equals(portName, GRE_TUNNEL) ||
-                                        Objects.equals(portName, GENEVE_TUNNEL))) {
+                                Objects.equals(portName, k8sNode.vxlanPortName()) ||
+                                        Objects.equals(portName, k8sNode.grePortName()) ||
+                                        Objects.equals(portName, k8sNode.genevePortName()))) {
                             log.warn("Interface {} removed from {}",
                                     portName, event.subject().id());
                             setState(k8sNode, INCOMPLETE);
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sApiConfigStore.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sApiConfigStore.java
index e5a65ab..044d9ae 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sApiConfigStore.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sApiConfigStore.java
@@ -20,7 +20,9 @@
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.k8snode.api.DefaultHostNodesInfo;
 import org.onosproject.k8snode.api.DefaultK8sApiConfig;
+import org.onosproject.k8snode.api.HostNodesInfo;
 import org.onosproject.k8snode.api.K8sApiConfig;
 import org.onosproject.k8snode.api.K8sApiConfigEvent;
 import org.onosproject.k8snode.api.K8sApiConfigStore;
@@ -72,8 +74,11 @@
             .register(KryoNamespaces.API)
             .register(K8sApiConfig.class)
             .register(DefaultK8sApiConfig.class)
+            .register(K8sApiConfig.Mode.class)
             .register(K8sApiConfig.Scheme.class)
             .register(K8sApiConfig.State.class)
+            .register(HostNodesInfo.class)
+            .register(DefaultHostNodesInfo.class)
             .register(Collection.class)
             .build();
 
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sHostStore.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sHostStore.java
new file mode 100644
index 0000000..17b9fde
--- /dev/null
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sHostStore.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.k8snode.api.DefaultK8sHost;
+import org.onosproject.k8snode.api.K8sHost;
+import org.onosproject.k8snode.api.K8sHostEvent;
+import org.onosproject.k8snode.api.K8sHostState;
+import org.onosproject.k8snode.api.K8sHostStore;
+import org.onosproject.k8snode.api.K8sHostStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_COMPLETE;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_CREATED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_INCOMPLETE;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_REMOVED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_UPDATED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_NODES_ADDED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_NODES_REMOVED;
+import static org.onosproject.k8snode.api.K8sHostState.COMPLETE;
+import static org.onosproject.k8snode.api.K8sHostState.INCOMPLETE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of kubernetes host store using consistent map.
+ */
+@Component(immediate = true, service = K8sHostStore.class)
+public class DistributedK8sHostStore
+        extends AbstractStore<K8sHostEvent, K8sHostStoreDelegate>
+        implements K8sHostStore {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_DUPLICATE = " already exists";
+    private static final String APP_ID = "org.onosproject.k8snode";
+
+    private static final KryoNamespace
+            SERIALIZER_K8S_HOST = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(K8sHost.class)
+            .register(DefaultK8sHost.class)
+            .register(K8sHostState.class)
+            .register(Collection.class)
+            .build();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected StorageService storageService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private final MapEventListener<String, K8sHost> hostMapListener =
+            new K8sHostMapListener();
+    private ConsistentMap<String, K8sHost> hostStore;
+
+    @Activate
+    protected void activate() {
+        ApplicationId appId = coreService.registerApplication(APP_ID);
+        hostStore = storageService.<String, K8sHost>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_K8S_HOST))
+                .withName("k8s-hoststore")
+                .withApplicationId(appId)
+                .build();
+        hostStore.addListener(hostMapListener);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        hostStore.removeListener(hostMapListener);
+        eventExecutor.shutdown();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createHost(K8sHost host) {
+        hostStore.compute(host.hostIp().toString(), (hostIp, existing) -> {
+            final String error = host.hostIp().toString() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return host;
+        });
+    }
+
+    @Override
+    public void updateHost(K8sHost host) {
+        hostStore.compute(host.hostIp().toString(), (hostIp, existing) -> {
+            final String error = host.hostIp().toString() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return host;
+        });
+    }
+
+    @Override
+    public K8sHost removeHost(IpAddress hostIp) {
+        Versioned<K8sHost> host = hostStore.remove(hostIp.toString());
+        if (host == null) {
+            final String error = hostIp.toString() + ERR_NOT_FOUND;
+            throw new IllegalArgumentException(error);
+        }
+        return host.value();
+    }
+
+    @Override
+    public Set<K8sHost> hosts() {
+        return ImmutableSet.copyOf(hostStore.asJavaMap().values());
+    }
+
+    @Override
+    public K8sHost host(IpAddress hostIp) {
+        return hostStore.asJavaMap().get(hostIp.toString());
+    }
+
+    private class K8sHostMapListener
+            implements MapEventListener<String, K8sHost> {
+
+        @Override
+        public void event(MapEvent<String, K8sHost> event) {
+            switch (event.type()) {
+                case INSERT:
+                    log.debug("Kubernetes host created {}", event.newValue());
+                    eventExecutor.execute(() ->
+                            notifyDelegate(new K8sHostEvent(
+                                    K8S_HOST_CREATED, event.newValue().value()
+                            )));
+                    break;
+                case UPDATE:
+                    log.debug("Kubernetes host updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new K8sHostEvent(
+                                K8S_HOST_UPDATED, event.newValue().value()
+                        ));
+
+                        if (event.newValue().value().state() == COMPLETE) {
+                            notifyDelegate(new K8sHostEvent(
+                                    K8S_HOST_COMPLETE,
+                                    event.newValue().value()
+                            ));
+                        } else if (event.newValue().value().state() == INCOMPLETE) {
+                            notifyDelegate(new K8sHostEvent(
+                                    K8S_HOST_INCOMPLETE,
+                                    event.newValue().value()
+                            ));
+                        }
+
+                        K8sHost origHost = event.newValue().value();
+                        Set<String> oldNodes = event.oldValue().value().nodeNames();
+                        Set<String> newNodes = event.newValue().value().nodeNames();
+
+                        Set<String> addedNodes = new HashSet<>(newNodes);
+                        Set<String> removedNodes = new HashSet<>(oldNodes);
+
+                        addedNodes.removeAll(oldNodes);
+                        removedNodes.removeAll(newNodes);
+
+                        if (addedNodes.size() > 0) {
+                            K8sHost addedHost = DefaultK8sHost.builder()
+                                    .hostIp(origHost.hostIp())
+                                    .state(origHost.state())
+                                    .nodeNames(addedNodes)
+                                    .build();
+                            notifyDelegate(new K8sHostEvent(K8S_NODES_ADDED, addedHost));
+                        }
+
+                        if (removedNodes.size() > 0) {
+                            K8sHost removedHost = DefaultK8sHost.builder()
+                                    .hostIp(origHost.hostIp())
+                                    .state(origHost.state())
+                                    .nodeNames(removedNodes)
+                                    .build();
+                            notifyDelegate(new K8sHostEvent(K8S_NODES_REMOVED, removedHost));
+                        }
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("Kubernetes host removed {}", event.oldValue());
+                    eventExecutor.execute(() ->
+                            notifyDelegate(new K8sHostEvent(
+                                    K8S_HOST_REMOVED, event.oldValue().value()
+                            )));
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java
index 15d2697..8d3e926 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/DistributedK8sNodeStore.java
@@ -19,7 +19,10 @@
 import org.onlab.util.KryoNamespace;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.k8snode.api.DefaultK8sExternalNetwork;
 import org.onosproject.k8snode.api.DefaultK8sNode;
+import org.onosproject.k8snode.api.K8sApiConfig;
+import org.onosproject.k8snode.api.K8sExternalNetwork;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeEvent;
 import org.onosproject.k8snode.api.K8sNodeState;
@@ -77,6 +80,9 @@
             .register(DefaultK8sNode.class)
             .register(K8sNode.Type.class)
             .register(K8sNodeState.class)
+            .register(K8sApiConfig.Mode.class)
+            .register(K8sExternalNetwork.class)
+            .register(DefaultK8sExternalNetwork.class)
             .register(Collection.class)
             .build();
 
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/ExternalNetworkManager.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/ExternalNetworkManager.java
new file mode 100644
index 0000000..184998a
--- /dev/null
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/ExternalNetworkManager.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.impl;
+
+import org.apache.commons.net.util.SubnetUtils;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.k8snode.api.ExternalNetworkService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Arrays;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snode.api.K8sNodeService.APP_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * External network service implementation.
+ */
+@Component(
+        immediate = true,
+        service = { ExternalNetworkService.class }
+)
+public class ExternalNetworkManager implements ExternalNetworkService {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final KryoNamespace
+            SERIALIZER_EXTERNAL_NETWORK = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .build();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected StorageService storageService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private ConsistentMap<String, Set<String>> networkIpPool;
+
+    @Activate
+    protected void activate() {
+        ApplicationId appId = coreService.registerApplication(APP_ID);
+        networkIpPool = storageService.<String, Set<String>>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_EXTERNAL_NETWORK))
+                .withName("external-network-ip-pool")
+                .withApplicationId(appId)
+                .build();
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        eventExecutor.shutdown();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void registerNetwork(IpPrefix cidr) {
+        if (!networkIpPool.containsKey(cidr.toString())) {
+            SubnetUtils utils = new SubnetUtils(cidr.toString());
+            utils.setInclusiveHostCount(false);
+            SubnetUtils.SubnetInfo info = utils.getInfo();
+
+            Set<String> all = Arrays.stream(info.getAllAddresses())
+                    .collect(Collectors.toSet());
+            all.remove(info.getNetworkAddress());
+            all.remove(info.getHighAddress());
+            all.remove(info.getLowAddress());
+            all.remove(info.getBroadcastAddress());
+
+            networkIpPool.put(cidr.toString(), all);
+        }
+    }
+
+    @Override
+    public void unregisterNetwork(IpPrefix cidr) {
+        if (!networkIpPool.containsKey(cidr.toString())) {
+            log.warn("The given network {} is not found!", cidr.toString());
+        } else {
+            networkIpPool.remove(cidr.toString());
+        }
+    }
+
+    @Override
+    public IpAddress getGatewayIp(IpPrefix cidr) {
+        SubnetUtils utils = new SubnetUtils(cidr.toString());
+        utils.setInclusiveHostCount(false);
+        SubnetUtils.SubnetInfo info = utils.getInfo();
+
+        return IpAddress.valueOf(info.getLowAddress());
+    }
+
+    @Override
+    public IpAddress allocateIp(IpPrefix cidr) {
+        if (!networkIpPool.containsKey(cidr.toString())) {
+            log.error("The given network {} is not found", cidr.toString());
+            return null;
+        } else {
+            Set<String> pool = networkIpPool.get(cidr.toString()).value();
+            String ipStr = pool.stream().findFirst().orElse(null);
+            if (ipStr == null) {
+                log.error("No IPs are found in the given network {}", cidr.toString());
+                return null;
+            }
+
+            pool.remove(ipStr);
+            networkIpPool.put(cidr.toString(), pool);
+            return IpAddress.valueOf(ipStr);
+        }
+    }
+
+    @Override
+    public void releaseIp(IpPrefix cidr, IpAddress ip) {
+        if (!networkIpPool.containsKey(cidr.toString())) {
+            log.error("The given network {} is not found", cidr.toString());
+        } else {
+            Set<String> pool = networkIpPool.get(cidr.toString()).value();
+            pool.add(ip.toString());
+            networkIpPool.put(cidr.toString(), pool);
+        }
+    }
+
+    @Override
+    public Set<String> getAllIps(IpPrefix cidr) {
+        return networkIpPool.get(cidr.toString()).value();
+    }
+}
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/K8sHostManager.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/K8sHostManager.java
new file mode 100644
index 0000000..fd26763
--- /dev/null
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/K8sHostManager.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onlab.util.Tools;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.k8snode.api.K8sHost;
+import org.onosproject.k8snode.api.K8sHostAdminService;
+import org.onosproject.k8snode.api.K8sHostEvent;
+import org.onosproject.k8snode.api.K8sHostListener;
+import org.onosproject.k8snode.api.K8sHostService;
+import org.onosproject.k8snode.api.K8sHostStore;
+import org.onosproject.k8snode.api.K8sHostStoreDelegate;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.store.service.StorageService;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Modified;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snode.api.K8sHostState.COMPLETE;
+import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT;
+import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Service administering the inventory of kubernetes hosts.
+ */
+@Component(
+        immediate = true,
+        service = {K8sHostService.class, K8sHostAdminService.class},
+        property = {
+                OVSDB_PORT + ":Integer=" + OVSDB_PORT_NUM_DEFAULT
+        }
+)
+public class K8sHostManager
+        extends ListenerRegistry<K8sHostEvent, K8sHostListener>
+        implements K8sHostService, K8sHostAdminService {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String MSG_HOST = "Kubernetes host %s %s";
+    private static final String MSG_CREATED = "created";
+    private static final String MSG_UPDATED = "updated";
+    private static final String MSG_REMOVED = "removed";
+
+    private static final String ERR_NULL_HOST = "Kubernetes host cannot be null";
+    private static final String ERR_NULL_HOST_IP = "Kubernetes host IP cannot be null";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected K8sHostStore hostStore;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected DeviceService deviceService;
+
+    /** OVSDB server listen port. */
+    private int ovsdbPortNum = OVSDB_PORT_NUM_DEFAULT;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private final K8sHostStoreDelegate delegate = new K8sHostManager.InternalHostStoreDelegate();
+
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(APP_ID);
+        hostStore.setDelegate(delegate);
+
+        leadershipService.runForLeadership(appId.name());
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        hostStore.unsetDelegate(delegate);
+
+        leadershipService.withdraw(appId.name());
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
+        if (!Objects.equals(updatedOvsdbPort, ovsdbPortNum)) {
+            ovsdbPortNum = updatedOvsdbPort;
+        }
+
+        log.info("Modified");
+    }
+
+    @Override
+    public void createHost(K8sHost host) {
+        checkNotNull(host, ERR_NULL_HOST);
+
+        hostStore.createHost(host);
+
+        log.info(String.format(MSG_HOST, host.hostIp().toString(), MSG_CREATED));
+    }
+
+    @Override
+    public void updateHost(K8sHost host) {
+        checkNotNull(host, ERR_NULL_HOST);
+
+        hostStore.updateHost(host);
+
+        log.info(String.format(MSG_HOST, host.hostIp().toString(), MSG_UPDATED));
+    }
+
+    @Override
+    public K8sHost removeHost(IpAddress hostIp) {
+        checkArgument(hostIp != null, ERR_NULL_HOST_IP);
+
+        K8sHost host = hostStore.removeHost(hostIp);
+        log.info(String.format(MSG_HOST, hostIp.toString(), MSG_REMOVED));
+
+        return host;
+    }
+
+    @Override
+    public Set<K8sHost> hosts() {
+        return hostStore.hosts();
+    }
+
+    @Override
+    public Set<K8sHost> completeHosts() {
+        Set<K8sHost> hosts = hostStore.hosts().stream()
+                .filter(h -> Objects.equals(h.state(), COMPLETE))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(hosts);
+    }
+
+    @Override
+    public K8sHost host(IpAddress hostIp) {
+        return hostStore.hosts().stream()
+                .filter(h -> Objects.equals(h.hostIp(), hostIp))
+                .findFirst().orElse(null);
+    }
+
+    private class InternalHostStoreDelegate implements K8sHostStoreDelegate {
+
+        @Override
+        public void notify(K8sHostEvent event) {
+            if (event != null) {
+                log.trace("send kubernetes host event {}", event);
+                process(event);
+            }
+        }
+    }
+}
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/K8sNodeManager.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/K8sNodeManager.java
index 5b3f353..bc789d3 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/K8sNodeManager.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/impl/K8sNodeManager.java
@@ -55,6 +55,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.k8snode.api.K8sApiConfig.Mode.NORMAL;
 import static org.onosproject.k8snode.api.K8sNodeState.COMPLETE;
 import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT;
 import static org.onosproject.k8snode.impl.OsgiPropertyConstants.OVSDB_PORT_NUM_DEFAULT;
@@ -159,6 +160,7 @@
         K8sNode intNode;
         K8sNode extNode;
         K8sNode localNode;
+        K8sNode tunNode;
 
         if (node.intgBridge() == null) {
             String deviceIdStr = genDpid(deviceIdCounter.incrementAndGet());
@@ -196,7 +198,24 @@
                     NOT_DUPLICATED_MSG, localNode.localBridge());
         }
 
-        nodeStore.createNode(localNode);
+        if (node.mode() == NORMAL) {
+            if (node.tunBridge() == null) {
+                String deviceIdStr = genDpid(deviceIdCounter.incrementAndGet());
+                checkNotNull(deviceIdStr, ERR_NULL_DEVICE_ID);
+                tunNode = localNode.updateTunBridge(DeviceId.deviceId(deviceIdStr));
+                checkArgument(!hasTunBridge(tunNode.tunBridge(), tunNode.hostname()),
+                        NOT_DUPLICATED_MSG, tunNode.tunBridge());
+            } else {
+                tunNode = localNode;
+                checkArgument(!hasTunBridge(tunNode.tunBridge(), tunNode.hostname()),
+                        NOT_DUPLICATED_MSG, tunNode.tunBridge());
+            }
+
+            nodeStore.createNode(tunNode);
+        } else {
+            nodeStore.createNode(localNode);
+        }
+
         log.info(String.format(MSG_NODE, extNode.hostname(), MSG_CREATED));
     }
 
@@ -207,6 +226,7 @@
         K8sNode intNode;
         K8sNode extNode;
         K8sNode localNode;
+        K8sNode tunNode;
 
         K8sNode existingNode = nodeStore.node(node.hostname());
         checkNotNull(existingNode, ERR_NULL_NODE);
@@ -247,7 +267,22 @@
                     NOT_DUPLICATED_MSG, localNode.localBridge());
         }
 
-        nodeStore.updateNode(localNode);
+        if (node.mode() == NORMAL) {
+            DeviceId existTunBridge = nodeStore.node(node.hostname()).tunBridge();
+
+            if (localNode.tunBridge() == null) {
+                tunNode = localNode.updateTunBridge(existTunBridge);
+                checkArgument(!hasTunBridge(tunNode.tunBridge(), tunNode.hostname()),
+                        NOT_DUPLICATED_MSG, tunNode.tunBridge());
+            } else {
+                tunNode = localNode;
+                checkArgument(!hasTunBridge(tunNode.tunBridge(), tunNode.hostname()),
+                        NOT_DUPLICATED_MSG, tunNode.tunBridge());
+            }
+            nodeStore.updateNode(tunNode);
+        } else {
+            nodeStore.updateNode(localNode);
+        }
         log.info(String.format(MSG_NODE, extNode.hostname(), MSG_UPDATED));
     }
 
@@ -265,6 +300,13 @@
     }
 
     @Override
+    public Set<K8sNode> nodes(String clusterName) {
+        return nodeStore.nodes().stream()
+                .filter(n -> n.clusterName().equals(clusterName))
+                .collect(Collectors.toSet());
+    }
+
+    @Override
     public Set<K8sNode> nodes(Type type) {
         Set<K8sNode> nodes = nodeStore.nodes().stream()
                 .filter(node -> Objects.equals(node.type(), type))
@@ -330,6 +372,15 @@
         return existNode.isPresent();
     }
 
+    private boolean hasTunBridge(DeviceId deviceId, String hostname) {
+        Optional<K8sNode> existNode = nodeStore.nodes().stream()
+                .filter(n -> !n.hostname().equals(hostname))
+                .filter(n -> deviceId.equals(n.tunBridge()))
+                .findFirst();
+
+        return existNode.isPresent();
+    }
+
     private class InternalNodeStoreDelegate implements K8sNodeStoreDelegate {
 
         @Override
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeCodecRegister.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeCodecRegister.java
index 9138539..4bca46e 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeCodecRegister.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeCodecRegister.java
@@ -16,9 +16,13 @@
 package org.onosproject.k8snode.web;
 
 import org.onosproject.codec.CodecService;
+import org.onosproject.k8snode.api.HostNodesInfo;
 import org.onosproject.k8snode.api.K8sApiConfig;
+import org.onosproject.k8snode.api.K8sHost;
 import org.onosproject.k8snode.api.K8sNode;
+import org.onosproject.k8snode.codec.HostNodesInfoCodec;
 import org.onosproject.k8snode.codec.K8sApiConfigCodec;
+import org.onosproject.k8snode.codec.K8sHostCodec;
 import org.onosproject.k8snode.codec.K8sNodeCodec;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -45,6 +49,8 @@
 
         codecService.registerCodec(K8sNode.class, new K8sNodeCodec());
         codecService.registerCodec(K8sApiConfig.class, new K8sApiConfigCodec());
+        codecService.registerCodec(HostNodesInfo.class, new HostNodesInfoCodec());
+        codecService.registerCodec(K8sHost.class, new K8sHostCodec());
 
         log.info("Started");
     }
@@ -54,6 +60,8 @@
 
         codecService.unregisterCodec(K8sNode.class);
         codecService.unregisterCodec(K8sApiConfig.class);
+        codecService.unregisterCodec(HostNodesInfo.class);
+        codecService.unregisterCodec(K8sHost.class);
 
         log.info("Stopped");
     }
diff --git a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeWebResource.java b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeWebResource.java
index df4f63f..1b20fd4 100644
--- a/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeWebResource.java
+++ b/apps/k8s-node/app/src/main/java/org/onosproject/k8snode/web/K8sNodeWebResource.java
@@ -19,8 +19,11 @@
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import com.google.common.collect.Sets;
+import org.onlab.packet.IpAddress;
 import org.onosproject.k8snode.api.K8sApiConfig;
 import org.onosproject.k8snode.api.K8sApiConfigAdminService;
+import org.onosproject.k8snode.api.K8sHost;
+import org.onosproject.k8snode.api.K8sHostAdminService;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeAdminService;
 import org.onosproject.k8snode.api.K8sNodeState;
@@ -42,6 +45,7 @@
 import javax.ws.rs.core.UriBuilder;
 import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
+import java.util.HashSet;
 import java.util.Set;
 
 import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
@@ -61,11 +65,15 @@
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String MESSAGE_NODE = "Received node %s request";
+    private static final String MESSAGE_HOST = "Received host %s request";
     private static final String NODES = "nodes";
     private static final String API_CONFIGS = "apiConfigs";
+    private static final String HOSTS = "hosts";
+    private static final String NODE_NAMES = "nodeNames";
     private static final String CREATE = "CREATE";
     private static final String UPDATE = "UPDATE";
     private static final String NODE_ID = "NODE_ID";
+    private static final String HOST_IP = "HOST_IP";
     private static final String REMOVE = "REMOVE";
     private static final String QUERY = "QUERY";
     private static final String INIT = "INIT";
@@ -78,6 +86,7 @@
     private static final String ERROR_MESSAGE = " cannot be null";
 
     private final K8sNodeAdminService nodeAdminService = get(K8sNodeAdminService.class);
+    private final K8sHostAdminService hostAdminService = get(K8sHostAdminService.class);
     private final K8sApiConfigAdminService configAdminService = get(K8sApiConfigAdminService.class);
 
     @Context
@@ -155,8 +164,8 @@
     public Response deleteNodes(@PathParam("hostname") String hostname) {
         log.trace(String.format(MESSAGE_NODE, REMOVE));
 
-        K8sNode existing =
-                nodeAdminService.node(nullIsIllegal(hostname, HOST_NAME + ERROR_MESSAGE));
+        K8sNode existing = nodeAdminService.node(
+                nullIsIllegal(hostname, HOST_NAME + ERROR_MESSAGE));
 
         if (existing == null) {
             log.warn("There is no node configuration to delete : {}", hostname);
@@ -293,30 +302,6 @@
         return ok(mapper().createObjectNode().put(RESULT, result)).build();
     }
 
-    private Set<K8sNode> readNodeConfiguration(InputStream input) {
-        Set<K8sNode> nodeSet = Sets.newHashSet();
-        try {
-            JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
-            ArrayNode nodes = (ArrayNode) jsonTree.path(NODES);
-            nodes.forEach(node -> {
-                try {
-                    ObjectNode objectNode = node.deepCopy();
-                    K8sNode k8sNode =
-                            codec(K8sNode.class).decode(objectNode, this);
-
-                    nodeSet.add(k8sNode);
-                } catch (Exception e) {
-                    log.error("Exception occurred due to {}", e);
-                    throw new IllegalArgumentException();
-                }
-            });
-        } catch (Exception e) {
-            throw new IllegalArgumentException(e);
-        }
-
-        return nodeSet;
-    }
-
     /**
      * Creates a set of kubernetes API config from the JSON input stream.
      *
@@ -387,8 +372,8 @@
     public Response deleteApiConfig(@PathParam("endpoint") String endpoint) {
         log.trace(String.format(MESSAGE_NODE, REMOVE));
 
-        K8sApiConfig existing =
-                configAdminService.apiConfig(nullIsIllegal(endpoint, ENDPOINT + ERROR_MESSAGE));
+        K8sApiConfig existing = configAdminService.apiConfig(
+                nullIsIllegal(endpoint, ENDPOINT + ERROR_MESSAGE));
 
         if (existing == null) {
             log.warn("There is no API configuration to delete : {}", endpoint);
@@ -400,6 +385,139 @@
         return Response.noContent().build();
     }
 
+    /**
+     * Creates a set of kubernetes hosts' config from the JSON input stream.
+     *
+     * @param input kubernetes hosts JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is malformed
+     * @onos.rsModel K8sHosts
+     */
+    @POST
+    @Path("host")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createHosts(InputStream input) {
+        log.trace(String.format(MESSAGE_NODE, CREATE));
+
+        readHostsConfiguration(input).forEach(host -> {
+            K8sHost existing = hostAdminService.host(host.hostIp());
+            if (existing == null) {
+                hostAdminService.createHost(host);
+            }
+        });
+
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(HOSTS)
+                .path(HOST_IP);
+
+        return created(locationBuilder.build()).build();
+    }
+
+    /**
+     * Add a set of new nodes into the existing host.
+     *
+     * @param hostIp host IP address
+     * @param input kubernetes node names JSON input stream
+     * @return 200 UPDATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is malformed
+     * @onos.rsModel K8sNodeNames
+     */
+    @PUT
+    @Path("host/add/nodes/{hostIp}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response addNodesToHost(@PathParam("hostIp") String hostIp,
+                                   InputStream input) {
+        log.trace(String.format(MESSAGE_HOST, UPDATE));
+
+        Set<String> newNodeNames = readNodeNamesConfiguration(input);
+        K8sHost host = hostAdminService.host(IpAddress.valueOf(hostIp));
+        Set<String> existNodeNames = host.nodeNames();
+        existNodeNames.addAll(newNodeNames);
+        K8sHost updated = host.updateNodeNames(existNodeNames);
+        hostAdminService.updateHost(updated);
+        return Response.ok().build();
+    }
+
+    /**
+     * Remove a set of new nodes from the existing host.
+     *
+     * @param hostIp host IP address
+     * @param input kubernetes node names JSON input stream
+     * @return 200 UPDATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is malformed
+     * @onos.rsModel K8sNodeNames
+     */
+    @PUT
+    @Path("host/delete/nodes/{hostIp}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response removeNodesFromHost(@PathParam("hostIp") String hostIp,
+                                        InputStream input) {
+        log.trace(String.format(MESSAGE_HOST, UPDATE));
+
+        Set<String> newNodeNames = readNodeNamesConfiguration(input);
+        K8sHost host = hostAdminService.host(IpAddress.valueOf(hostIp));
+        Set<String> existNodeNames = host.nodeNames();
+        existNodeNames.removeAll(newNodeNames);
+        K8sHost updated = host.updateNodeNames(existNodeNames);
+        hostAdminService.updateHost(updated);
+        return Response.ok().build();
+    }
+
+    /**
+     * Removes a kubernetes host' config.
+     *
+     * @param hostIp host IP contained in kubernetes nodes configuration
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the JSON is malformed, and
+     * 304 NOT_MODIFIED without the updated config
+     */
+    @DELETE
+    @Path("host/{hostIp}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteHost(@PathParam("hostIp") String hostIp) {
+        log.trace(String.format(MESSAGE_HOST, REMOVE));
+
+        K8sHost existing = hostAdminService.host(IpAddress.valueOf(
+                        nullIsIllegal(hostIp, HOST_IP + ERROR_MESSAGE)));
+
+        if (existing == null) {
+            log.warn("There is no host configuration to delete : {}", hostIp);
+            return Response.notModified().build();
+        } else {
+            hostAdminService.removeHost(IpAddress.valueOf(
+                    nullIsIllegal(hostIp, HOST_IP + ERROR_MESSAGE)));
+        }
+
+        return Response.noContent().build();
+    }
+
+    private Set<K8sNode> readNodeConfiguration(InputStream input) {
+        Set<K8sNode> nodeSet = Sets.newHashSet();
+        try {
+            JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+            ArrayNode nodes = (ArrayNode) jsonTree.path(NODES);
+            nodes.forEach(node -> {
+                try {
+                    ObjectNode objectNode = node.deepCopy();
+                    K8sNode k8sNode =
+                            codec(K8sNode.class).decode(objectNode, this);
+
+                    nodeSet.add(k8sNode);
+                } catch (Exception e) {
+                    log.error("Exception occurred due to {}", e);
+                    throw new IllegalArgumentException();
+                }
+            });
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+
+        return nodeSet;
+    }
+
     private Set<K8sApiConfig> readApiConfigConfiguration(InputStream input) {
         Set<K8sApiConfig> configSet = Sets.newHashSet();
         try {
@@ -423,4 +541,49 @@
 
         return configSet;
     }
+
+    private Set<K8sHost> readHostsConfiguration(InputStream input) {
+        Set<K8sHost> hostSet = new HashSet<>();
+        try {
+            JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+            ArrayNode hosts = (ArrayNode) jsonTree.path(HOSTS);
+            hosts.forEach(host -> {
+                try {
+                    ObjectNode objectNode = host.deepCopy();
+                    K8sHost k8sHost =
+                            codec(K8sHost.class).decode(objectNode, this);
+
+                    hostSet.add(k8sHost);
+                } catch (Exception e) {
+                    log.error("Exception occurred due to {}", e);
+                    throw new IllegalArgumentException();
+                }
+            });
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+
+        return hostSet;
+    }
+
+    private Set<String> readNodeNamesConfiguration(InputStream input) {
+        Set<String> nodeNames = new HashSet<>();
+        try {
+            JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+            ArrayNode names = (ArrayNode) jsonTree.path(NODE_NAMES);
+            names.forEach(name -> {
+                try {
+                    ObjectNode objectNode = name.deepCopy();
+                    nodeNames.add(objectNode.asText());
+                } catch (Exception e) {
+                    log.error("Exception occurred due to {}", e);
+                    throw new IllegalArgumentException();
+                }
+            });
+        } catch (Exception e) {
+            throw new IllegalArgumentException(e);
+        }
+
+        return nodeNames;
+    }
 }
diff --git a/apps/k8s-node/app/src/main/resources/definitions/K8sApiConfig.json b/apps/k8s-node/app/src/main/resources/definitions/K8sApiConfig.json
index b82447f..b8dd0b6 100644
--- a/apps/k8s-node/app/src/main/resources/definitions/K8sApiConfig.json
+++ b/apps/k8s-node/app/src/main/resources/definitions/K8sApiConfig.json
@@ -9,11 +9,21 @@
       "items": {
         "type": "object",
         "required": [
+          "clusterName",
+          "segmentId",
           "scheme",
           "ipAddress",
           "port"
         ],
         "properties": {
+          "clusterName": {
+            "type": "string",
+            "example": "kubernetes"
+          },
+          "segmentId": {
+            "type": "integer",
+            "example": 1
+          },
           "scheme": {
             "type": "string",
             "example": "HTTP"
diff --git a/apps/k8s-node/app/src/main/resources/definitions/K8sHosts.json b/apps/k8s-node/app/src/main/resources/definitions/K8sHosts.json
new file mode 100644
index 0000000..132a0a7
--- /dev/null
+++ b/apps/k8s-node/app/src/main/resources/definitions/K8sHosts.json
@@ -0,0 +1,31 @@
+{
+  "type": "object",
+  "required": [
+    "hosts"
+  ],
+  "properties": {
+    "hosts": {
+      "type": "array",
+      "items": {
+        "type": "object",
+        "required": [
+          "hostIp",
+          "nodeNames"
+        ],
+        "properties": {
+          "hostIp": {
+            "type": "string",
+            "example": "192.168.200.10"
+          },
+          "nodeNames": {
+            "type": "array",
+            "items": {
+              "type": "string",
+              "example": "k8s-master"
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/k8s-node/app/src/main/resources/definitions/K8sNode.json b/apps/k8s-node/app/src/main/resources/definitions/K8sNode.json
index 4351616..2e2cc37 100644
--- a/apps/k8s-node/app/src/main/resources/definitions/K8sNode.json
+++ b/apps/k8s-node/app/src/main/resources/definitions/K8sNode.json
@@ -9,6 +9,7 @@
       "items": {
         "type": "object",
         "required": [
+          "clusterName",
           "hostname",
           "type",
           "managementIp",
@@ -16,6 +17,10 @@
           "integrationBridge"
         ],
         "properties": {
+          "clusterName": {
+            "type": "string",
+            "example": "kubernetes"
+          },
           "hostname": {
             "type": "string",
             "example": "host1"
diff --git a/apps/k8s-node/app/src/main/resources/definitions/K8sNodeNames.json b/apps/k8s-node/app/src/main/resources/definitions/K8sNodeNames.json
new file mode 100644
index 0000000..a1ced8f
--- /dev/null
+++ b/apps/k8s-node/app/src/main/resources/definitions/K8sNodeNames.json
@@ -0,0 +1,7 @@
+{
+  "type": "array",
+  "items": {
+    "type": "string",
+    "example": "k8s-master"
+  }
+}
\ No newline at end of file
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/HostNodesInfoJsonMatcher.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/HostNodesInfoJsonMatcher.java
new file mode 100644
index 0000000..5de7a71
--- /dev/null
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/HostNodesInfoJsonMatcher.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.k8snode.api.HostNodesInfo;
+
+/**
+ * Hamcrest matcher for HostNodesInfo.
+ */
+public final class HostNodesInfoJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final HostNodesInfo hostNodesInfo;
+
+    private static final String HOST_IP = "hostIp";
+    private static final String NODES = "nodes";
+
+    private HostNodesInfoJsonMatcher(HostNodesInfo hostNodesInfo) {
+        this.hostNodesInfo = hostNodesInfo;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+        // check host IP
+        String jsonHostIp = jsonNode.get(HOST_IP).asText();
+        String hostIp = hostNodesInfo.hostIp().toString();
+        if (!jsonHostIp.equals(hostIp)) {
+            description.appendText("host IP was " + jsonHostIp);
+            return false;
+        }
+
+        // check nodes
+        JsonNode jsonNodes = jsonNode.get(NODES);
+        if (jsonNodes.size() != hostNodesInfo.nodes().size()) {
+            description.appendText("Nodes size was " + jsonNodes.size());
+            return false;
+        }
+
+        boolean nodeFound = true;
+        ArrayNode jsonNodeArray = (ArrayNode) jsonNodes;
+        for (JsonNode jsonNodeTmp : jsonNodeArray) {
+            if (!hostNodesInfo.nodes().contains(jsonNodeTmp.asText())) {
+                nodeFound = false;
+            }
+        }
+
+        if (!nodeFound) {
+            description.appendText("Node not found");
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(hostNodesInfo.toString());
+    }
+
+    /**
+     * Factory to allocate a hostNodesInfo matcher.
+     *
+     * @param info host IP address to nodes mapping info
+     * @return matcher
+     */
+    public static HostNodesInfoJsonMatcher matchesHostNodesInfo(HostNodesInfo info) {
+        return new HostNodesInfoJsonMatcher(info);
+    }
+}
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigCodecTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigCodecTest.java
index cca918e..b5deb38 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigCodecTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigCodecTest.java
@@ -18,20 +18,25 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
 import org.junit.Before;
 import org.junit.Test;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.core.CoreService;
+import org.onosproject.k8snode.api.DefaultHostNodesInfo;
 import org.onosproject.k8snode.api.DefaultK8sApiConfig;
+import org.onosproject.k8snode.api.HostNodesInfo;
 import org.onosproject.k8snode.api.K8sApiConfig;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Set;
 
 import static junit.framework.TestCase.assertEquals;
 import static org.easymock.EasyMock.createMock;
@@ -51,6 +56,7 @@
     MockCodecContext context;
 
     JsonCodec<K8sApiConfig> k8sApiConfigCodec;
+    JsonCodec<HostNodesInfo> hostNodesInfoCodec;
 
     final CoreService mockCoreService = createMock(CoreService.class);
     private static final String REST_APP_ID = "org.onosproject.rest";
@@ -62,8 +68,10 @@
     public void setUp() {
         context = new MockCodecContext();
         k8sApiConfigCodec = new K8sApiConfigCodec();
+        hostNodesInfoCodec = new HostNodesInfoCodec();
 
         assertThat(k8sApiConfigCodec, notNullValue());
+        assertThat(hostNodesInfoCodec, notNullValue());
 
         expect(mockCoreService.registerApplication(REST_APP_ID))
                 .andReturn(APP_ID).anyTimes();
@@ -76,7 +84,16 @@
      */
     @Test
     public void testK8sApiConfigEncode() {
+        HostNodesInfo info = new DefaultHostNodesInfo.Builder()
+                .hostIp(IpAddress.valueOf("192.168.10.10"))
+                .nodes(ImmutableSet.of("master", "worker"))
+                .build();
+
         K8sApiConfig config = DefaultK8sApiConfig.builder()
+                .clusterName("kubernetes")
+                .segmentId(1)
+                .extNetworkCidr(IpPrefix.valueOf("192.168.200.0/24"))
+                .mode(K8sApiConfig.Mode.NORMAL)
                 .scheme(K8sApiConfig.Scheme.HTTPS)
                 .ipAddress(IpAddress.valueOf("10.10.10.23"))
                 .port(6443)
@@ -85,6 +102,7 @@
                 .caCertData("caCertData")
                 .clientCertData("clientCertData")
                 .clientKeyData("clientKeyData")
+                .infos(ImmutableSet.of(info))
                 .build();
 
         ObjectNode configJson = k8sApiConfigCodec.encode(config, context);
@@ -100,6 +118,10 @@
     public void testK8sApiConfigDecode() throws IOException {
         K8sApiConfig config = getK8sApiConfig("K8sApiConfig.json");
 
+        assertEquals("kubernetes", config.clusterName());
+        assertEquals(1, config.segmentId());
+        assertEquals("192.168.200.0/24", config.extNetworkCidr().toString());
+        assertEquals("NORMAL", config.mode().name());
         assertEquals("HTTPS", config.scheme().name());
         assertEquals("10.134.34.223", config.ipAddress().toString());
         assertEquals(6443, config.port());
@@ -107,6 +129,9 @@
         assertEquals("caCertData", config.caCertData());
         assertEquals("clientCertData", config.clientCertData());
         assertEquals("clientKeyData", config.clientKeyData());
+
+        Set<HostNodesInfo> infos = config.infos();
+        assertEquals(1, infos.size());
     }
 
     private K8sApiConfig getK8sApiConfig(String resourceName) throws IOException {
@@ -141,6 +166,9 @@
             if (entityClass == K8sApiConfig.class) {
                 return (JsonCodec<T>) k8sApiConfigCodec;
             }
+            if (entityClass == HostNodesInfo.class) {
+                return (JsonCodec<T>) hostNodesInfoCodec;
+            }
             return manager.getCodec(entityClass);
         }
 
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigJsonMatcher.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigJsonMatcher.java
index 2eb42b9..7ec3866 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigJsonMatcher.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sApiConfigJsonMatcher.java
@@ -18,6 +18,7 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.k8snode.api.HostNodesInfo;
 import org.onosproject.k8snode.api.K8sApiConfig;
 
 /**
@@ -27,6 +28,10 @@
 
     private final K8sApiConfig k8sApiConfig;
 
+    private static final String CLUSTER_NAME = "clusterName";
+    private static final String SEGMENT_ID = "segmentId";
+    private static final String EXT_NETWORK_CIDR = "extNetworkCidr";
+    private static final String MODE = "mode";
     private static final String SCHEME = "scheme";
     private static final String IP_ADDRESS = "ipAddress";
     private static final String PORT = "port";
@@ -35,6 +40,7 @@
     private static final String CA_CERT_DATA = "caCertData";
     private static final String CLIENT_CERT_DATA = "clientCertData";
     private static final String CLIENT_KEY_DATA = "clientKeyData";
+    private static final String HOST_NODES_INFO = "hostNodesInfo";
 
     private K8sApiConfigJsonMatcher(K8sApiConfig k8sApiConfig) {
         this.k8sApiConfig = k8sApiConfig;
@@ -43,6 +49,40 @@
     @Override
     protected boolean matchesSafely(JsonNode jsonNode, Description description) {
 
+        // check cluster name
+        String jsonClusterName = jsonNode.get(CLUSTER_NAME).asText();
+        String clusterName = k8sApiConfig.clusterName();
+        if (!jsonClusterName.equals(clusterName)) {
+            description.appendText("cluster name was " + jsonClusterName);
+            return false;
+        }
+
+        // check segment ID
+        int jsonSegmentId = jsonNode.get(SEGMENT_ID).asInt();
+        int segmentId = k8sApiConfig.segmentId();
+        if (jsonSegmentId != segmentId) {
+            description.appendText("Segment ID was " + jsonSegmentId);
+            return false;
+        }
+
+        // check mode
+        String jsonMode = jsonNode.get(MODE).asText();
+        String mode = k8sApiConfig.mode().name();
+        if (!jsonMode.equals(mode)) {
+            description.appendText("mode was " + jsonMode);
+            return false;
+        }
+
+        // check external network CIDR
+        JsonNode jsonCidr = jsonNode.get(EXT_NETWORK_CIDR);
+        String cidr = k8sApiConfig.extNetworkCidr().toString();
+        if (jsonCidr != null) {
+            if (!jsonCidr.asText().equals(cidr)) {
+                description.appendText("External network CIDR was " + jsonCidr);
+                return false;
+            }
+        }
+
         // check scheme
         String jsonScheme = jsonNode.get(SCHEME).asText();
         String scheme = k8sApiConfig.scheme().name();
@@ -119,6 +159,29 @@
             }
         }
 
+        // check hostNodesInfo size
+        JsonNode jsonInfos = jsonNode.get(HOST_NODES_INFO);
+        if (jsonInfos.size() != k8sApiConfig.infos().size()) {
+            description.appendText("Info size was " + jsonInfos.size());
+            return false;
+        }
+
+        // check info
+        for (HostNodesInfo info : k8sApiConfig.infos()) {
+            boolean infoFound = false;
+            for (int infoIndex = 0; infoIndex < jsonInfos.size(); infoIndex++) {
+                HostNodesInfoJsonMatcher infoMatcher = HostNodesInfoJsonMatcher.matchesHostNodesInfo(info);
+                if  (infoMatcher.matches(jsonInfos.get(infoIndex))) {
+                    infoFound = true;
+                    break;
+                }
+            }
+            if (!infoFound) {
+                description.appendText("Info not found " + info.toString());
+                return false;
+            }
+        }
+
         return true;
     }
 
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sHostCodecTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sHostCodecTest.java
new file mode 100644
index 0000000..c7cb041
--- /dev/null
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sHostCodecTest.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.core.CoreService;
+import org.onosproject.k8snode.api.DefaultK8sHost;
+import org.onosproject.k8snode.api.K8sHost;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onosproject.k8snode.api.K8sHostState.INIT;
+import static org.onosproject.k8snode.codec.K8sHostJsonMatcher.matchesK8sHost;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+public class K8sHostCodecTest {
+
+    MockCodecContext context;
+
+    JsonCodec<K8sHost> k8sHostCodec;
+
+    final CoreService mockCoreService = createMock(CoreService.class);
+    private static final String REST_APP_ID = "org.onosproject.rest";
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        k8sHostCodec = new K8sHostCodec();
+
+        assertThat(k8sHostCodec, notNullValue());
+
+        expect(mockCoreService.registerApplication(REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Tests the kubernetes host encoding.
+     */
+    @Test
+    public void testK8sHostEncode() {
+        K8sHost host = DefaultK8sHost.builder()
+                .hostIp(IpAddress.valueOf("192.168.200.10"))
+                .state(INIT)
+                .nodeNames(ImmutableSet.of("1", "2"))
+                .build();
+
+        ObjectNode hostJson = k8sHostCodec.encode(host, context);
+        assertThat(hostJson, matchesK8sHost(host));
+    }
+
+    /**
+     * Tests the kubernetes host decoding.
+     */
+    @Test
+    public void testK8sHostDecode() throws IOException {
+        K8sHost host = getK8sHost("K8sHost.json");
+
+        assertEquals("192.168.200.10", host.hostIp().toString());
+        assertEquals("INIT", host.state().name());
+    }
+
+    private K8sHost getK8sHost(String resourceName) throws IOException {
+        InputStream jsonStream = K8sHostCodecTest.class.getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        assertThat(json, notNullValue());
+        K8sHost host = k8sHostCodec.decode((ObjectNode) json, context);
+        assertThat(host, notNullValue());
+        return host;
+    }
+
+    private class MockCodecContext implements CodecContext {
+        private final ObjectMapper mapper = new ObjectMapper();
+        private final CodecManager manager = new CodecManager();
+        private final Map<Class<?>, Object> services = new HashMap<>();
+
+        /**
+         * Constructs a new mock codec context.
+         */
+        public MockCodecContext() {
+            manager.activate();
+        }
+
+        @Override
+        public ObjectMapper mapper() {
+            return mapper;
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public <T> JsonCodec<T> codec(Class<T> entityClass) {
+            if (entityClass == K8sHost.class) {
+                return (JsonCodec<T>) k8sHostCodec;
+            }
+            return manager.getCodec(entityClass);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public <T> T getService(Class<T> serviceClass) {
+            return (T) services.get(serviceClass);
+        }
+
+        // for registering mock services
+        public <T> void registerService(Class<T> serviceClass, T impl) {
+            services.put(serviceClass, impl);
+        }
+    }
+}
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sHostJsonMatcher.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sHostJsonMatcher.java
new file mode 100644
index 0000000..4a0cf73
--- /dev/null
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sHostJsonMatcher.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.codec;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.k8snode.api.K8sHost;
+
+/**
+ * Hamcrest matcher for kubernetes host.
+ */
+public final class K8sHostJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final K8sHost host;
+
+    private static final String HOST_IP = "hostIp";
+    private static final String NODE_NAMES = "nodeNames";
+    private static final String STATE = "state";
+
+    private K8sHostJsonMatcher(K8sHost host) {
+        this.host = host;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+        // check host IP
+        String jsonHostIp = jsonNode.get(HOST_IP).asText();
+        String hostIp = host.hostIp().toString();
+        if (!jsonHostIp.equals(hostIp)) {
+            description.appendText("host IP was " + jsonHostIp);
+            return false;
+        }
+
+        // check state
+        String jsonState = jsonNode.get(STATE).asText();
+        String state = host.state().name();
+        if (!jsonState.equals(state)) {
+            description.appendText("state was " + jsonState);
+            return false;
+        }
+
+        // check node names size
+        JsonNode jsonNames = jsonNode.get(NODE_NAMES);
+        if (jsonNames.size() != host.nodeNames().size()) {
+            description.appendText("Node names size was " + jsonNames.size());
+            return false;
+        }
+
+        // check node names
+        for (String name : host.nodeNames()) {
+            boolean nameFound = false;
+            for (int nameIndex = 0; nameIndex < jsonNames.size(); nameIndex++) {
+                if (name.equals(jsonNames.get(nameIndex).asText())) {
+                    nameFound = true;
+                    break;
+                }
+            }
+
+            if (!nameFound) {
+                description.appendText("Name not found " + name);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(host.toString());
+    }
+
+    /**
+     * Factory to allocate an k8s host matcher.
+     *
+     * @param host k8s host object we are looking for
+     * @return matcher
+     */
+    public static K8sHostJsonMatcher matchesK8sHost(K8sHost host) {
+        return new K8sHostJsonMatcher(host);
+    }
+}
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java
index 556e462..c6861be 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeCodecTest.java
@@ -78,8 +78,10 @@
     @Test
     public void testK8sMinionNodeEncode() {
         K8sNode node = DefaultK8sNode.builder()
+                .clusterName("kubernetes")
                 .hostname("minion")
                 .type(K8sNode.Type.MINION)
+                .segmentId(100)
                 .state(K8sNodeState.INIT)
                 .managementIp(IpAddress.valueOf("10.10.10.1"))
                 .dataIp(IpAddress.valueOf("20.20.20.2"))
@@ -102,8 +104,10 @@
     public void testK8sMinionNodeDecode() throws IOException {
         K8sNode node = getK8sNode("K8sMinionNode.json");
 
+        assertEquals("kubernetes", node.clusterName());
         assertEquals("minion", node.hostname());
         assertEquals("MINION", node.type().name());
+        assertEquals(100, node.segmentId());
         assertEquals("172.16.130.4", node.managementIp().toString());
         assertEquals("172.16.130.4", node.dataIp().toString());
         assertEquals("of:00000000000000a1", node.intgBridge().toString());
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeJsonMatcher.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeJsonMatcher.java
index b953f54..816ca76 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeJsonMatcher.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/codec/K8sNodeJsonMatcher.java
@@ -28,8 +28,10 @@
 
     private final K8sNode node;
 
+    private static final String CLUSTER_NAME = "clusterName";
     private static final String HOSTNAME = "hostname";
     private static final String TYPE = "type";
+    private static final String SEGMENT_ID = "segmentId";
     private static final String MANAGEMENT_IP = "managementIp";
     private static final String DATA_IP = "dataIp";
     private static final String INTEGRATION_BRIDGE = "integrationBridge";
@@ -45,6 +47,14 @@
     @Override
     protected boolean matchesSafely(JsonNode jsonNode, Description description) {
 
+        // check cluster name
+        String jsonClusterName = jsonNode.get(CLUSTER_NAME).asText();
+        String clusterName = node.clusterName();
+        if (!jsonClusterName.equals(clusterName)) {
+            description.appendText("cluster name was " + jsonClusterName);
+            return false;
+        }
+
         // check hostname
         String jsonHostname = jsonNode.get(HOSTNAME).asText();
         String hostname = node.hostname();
@@ -61,6 +71,16 @@
             return false;
         }
 
+        // check segment ID
+        JsonNode jsonSegmentId = jsonNode.get(SEGMENT_ID);
+        if (jsonSegmentId != null) {
+            int segmentId = jsonSegmentId.asInt();
+            if (segmentId != node.segmentId()) {
+                description.appendText("segment ID was " + segmentId);
+                return false;
+            }
+        }
+
         // check management IP
         String jsonMgmtIp = jsonNode.get(MANAGEMENT_IP).asText();
         String mgmtIp = node.managementIp().toString();
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/ExternalNetworkManagerTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/ExternalNetworkManagerTest.java
new file mode 100644
index 0000000..f236491
--- /dev/null
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/ExternalNetworkManagerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.impl;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.store.service.TestStorageService;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for external network manager.
+ */
+public class ExternalNetworkManagerTest {
+
+    private static final ApplicationId TEST_APP_ID = new DefaultApplicationId(1, "test");
+
+    private ExternalNetworkManager target;
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setUp() {
+        target = new ExternalNetworkManager();
+
+        target.coreService = new TestCoreService();
+        target.storageService = new TestStorageService();
+        target.activate();
+    }
+
+    /**
+     * Clean up unit test.
+     */
+    @After
+    public void tearDown() {
+        target.deactivate();
+        target = null;
+    }
+
+    /**
+     * Checks if creating and removing a config work well with proper events.
+     */
+    @Test
+    public void testObtainGatewayIp() {
+        IpPrefix cidr = IpPrefix.valueOf("192.168.200.0/24");
+        target.registerNetwork(cidr);
+
+        assertEquals(target.getGatewayIp(cidr), IpAddress.valueOf("192.168.200.1"));
+    }
+
+    @Test
+    public void testAllocateReleaseIp() {
+        IpPrefix cidr = IpPrefix.valueOf("192.168.200.0/24");
+        target.registerNetwork(cidr);
+        IpAddress ip = target.allocateIp(cidr);
+        assertEquals(251, target.getAllIps(cidr).size());
+
+        target.releaseIp(cidr, ip);
+        assertEquals(252, target.getAllIps(cidr).size());
+    }
+
+    private static class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return TEST_APP_ID;
+        }
+    }
+}
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sApiConfigManagerTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sApiConfigManagerTest.java
index f46feb8..badfced 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sApiConfigManagerTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sApiConfigManagerTest.java
@@ -72,12 +72,18 @@
     public void setUp() {
 
         apiConfig1 = DefaultK8sApiConfig.builder()
+                .clusterName("kubernetes1")
+                .segmentId(1)
+                .mode(K8sApiConfig.Mode.NORMAL)
                 .scheme(K8sApiConfig.Scheme.HTTP)
                 .ipAddress(IpAddress.valueOf("10.10.10.2"))
                 .port(6443)
                 .state(DISCONNECTED)
                 .build();
         apiConfig2 = DefaultK8sApiConfig.builder()
+                .clusterName("kubernetes2")
+                .segmentId(2)
+                .mode(K8sApiConfig.Mode.NORMAL)
                 .scheme(K8sApiConfig.Scheme.HTTPS)
                 .ipAddress(IpAddress.valueOf("10.10.10.3"))
                 .port(6443)
@@ -88,6 +94,9 @@
                 .clientKeyData("clientKeyData")
                 .build();
         apiConfig3 = DefaultK8sApiConfig.builder()
+                .clusterName("kubernetes3")
+                .segmentId(3)
+                .mode(K8sApiConfig.Mode.PASSTHROUGH)
                 .scheme(K8sApiConfig.Scheme.HTTP)
                 .ipAddress(IpAddress.valueOf("10.10.10.4"))
                 .port(8080)
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sHostManagerTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sHostManagerTest.java
new file mode 100644
index 0000000..3374672
--- /dev/null
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sHostManagerTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2020-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.k8snode.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cluster.ClusterServiceAdapter;
+import org.onosproject.cluster.LeadershipServiceAdapter;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.event.Event;
+import org.onosproject.k8snode.api.DefaultK8sHost;
+import org.onosproject.k8snode.api.K8sHost;
+import org.onosproject.k8snode.api.K8sHostEvent;
+import org.onosproject.k8snode.api.K8sHostListener;
+import org.onosproject.k8snode.api.K8sHostState;
+import org.onosproject.store.service.TestStorageService;
+
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_COMPLETE;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_CREATED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_REMOVED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_HOST_UPDATED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_NODES_ADDED;
+import static org.onosproject.k8snode.api.K8sHostEvent.Type.K8S_NODES_REMOVED;
+import static org.onosproject.k8snode.api.K8sHostState.COMPLETE;
+import static org.onosproject.k8snode.api.K8sHostState.INIT;
+
+/**
+ * Unit tests for kubernetes host manager.
+ */
+public class K8sHostManagerTest {
+
+    private static final ApplicationId TEST_APP_ID = new DefaultApplicationId(1, "test");
+
+    private static final String ERR_SIZE = "Number of hosts did not match";
+    private static final String ERR_NOT_MATCH = "Host did not match";
+    private static final String ERR_NOT_FOUND = "Host did not exist";
+
+    private static final IpAddress HOST_IP_1 = IpAddress.valueOf("192.168.100.2");
+    private static final IpAddress HOST_IP_2 = IpAddress.valueOf("192.168.101.2");
+    private static final IpAddress HOST_IP_3 = IpAddress.valueOf("192.168.102.2");
+
+    private static final K8sHost HOST_1 = createHost(
+            HOST_IP_1,
+            INIT,
+            ImmutableSet.of("1", "2")
+    );
+
+    private static final K8sHost HOST_2 = createHost(
+            HOST_IP_2,
+            INIT,
+            ImmutableSet.of("3", "4")
+    );
+
+    private static final K8sHost HOST_3 = createHost(
+            HOST_IP_3,
+            COMPLETE,
+            ImmutableSet.of("5", "6")
+    );
+
+    private final TestK8sHostListener testListener = new TestK8sHostListener();
+
+    private K8sHostManager target;
+    private DistributedK8sHostStore hostStore;
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setUp() {
+        hostStore = new DistributedK8sHostStore();
+        TestUtils.setField(hostStore, "coreService", new TestCoreService());
+        TestUtils.setField(hostStore, "storageService", new TestStorageService());
+        TestUtils.setField(hostStore, "eventExecutor", MoreExecutors.newDirectExecutorService());
+        hostStore.activate();
+
+        hostStore.createHost(HOST_2);
+        hostStore.createHost(HOST_3);
+
+        target = new K8sHostManager();
+        target.storageService = new TestStorageService();
+        target.coreService = new TestCoreService();
+        target.clusterService = new TestClusterService();
+        target.leadershipService = new TestLeadershipService();
+        target.hostStore = hostStore;
+        target.addListener(testListener);
+        target.activate();
+        testListener.events.clear();
+    }
+
+    /**
+     * Clean up unit test.
+     */
+    @After
+    public void tearDown() {
+        target.removeListener(testListener);
+        target.deactivate();
+        hostStore.deactivate();
+        hostStore = null;
+        target = null;
+    }
+
+    /**
+     * Checks if creating and removing a host work well with proper events.
+     */
+    @Test
+    public void testCreateAndRemoveHost() {
+        target.createHost(HOST_1);
+        assertEquals(ERR_SIZE, 3, target.hosts().size());
+        assertNotNull(target.host(HOST_IP_1));
+
+        target.removeHost(HOST_IP_1);
+        assertEquals(ERR_SIZE, 2, target.hosts().size());
+        assertNull(target.host(HOST_IP_1));
+
+        validateEvents(K8S_HOST_CREATED, K8S_HOST_REMOVED);
+    }
+
+    /**
+     * Checks if creating null host fails with proper exception.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testCreateNullHost() {
+        target.createHost(null);
+    }
+
+    /**
+     * Checks if creating a duplicated host fails with proper exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testCreateDuplicateHost() {
+        target.createHost(HOST_1);
+        target.createHost(HOST_1);
+    }
+
+    /**
+     * Checks if removing null host fails with proper exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testRemoveNullHost() {
+        target.removeHost(null);
+    }
+
+    /**
+     * Checks if updating a host works well with proper event.
+     */
+    @Test
+    public void testUpdateHost() {
+        K8sHost updated = HOST_2.updateState(COMPLETE);
+        target.updateHost(updated);
+        validateEvents(K8S_HOST_UPDATED, K8S_HOST_COMPLETE);
+    }
+
+    /**
+     * Checks if updating a null host fails with proper exception.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testUpdateNullHost() {
+        target.updateHost(null);
+    }
+
+    /**
+     * Checks if updating not existing host fails with proper exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testUpdateNotExistingHost() {
+        target.updateHost(HOST_1);
+    }
+
+    /**
+     * Checks if adding nodes into host works well with proper event.
+     */
+    @Test
+    public void testAddNodesToHost() {
+        K8sHost updated = HOST_2.updateNodeNames(ImmutableSet.of("3", "4", "5"));
+        target.updateHost(updated);
+        validateEvents(K8S_HOST_UPDATED, K8S_NODES_ADDED);
+    }
+
+    /**
+     * Checks if removing nodes from host works well with proper event.
+     */
+    @Test
+    public void testRemoveNodesFromHost() {
+        K8sHost updated = HOST_2.updateNodeNames(ImmutableSet.of("3"));
+        target.updateHost(updated);
+        validateEvents(K8S_HOST_UPDATED, K8S_NODES_REMOVED);
+    }
+
+    /**
+     * Checks if getting all hosts method returns correct set of nodes.
+     */
+    @Test
+    public void testGetAllHosts() {
+        assertEquals(ERR_SIZE, 2, target.hosts().size());
+        assertTrue(ERR_NOT_FOUND, target.hosts().contains(HOST_2));
+        assertTrue(ERR_NOT_FOUND, target.hosts().contains(HOST_3));
+    }
+
+    /**
+     * Checks if getting complete hosts method returns correct set of nodes.
+     */
+    @Test
+    public void testGetCompleteHosts() {
+        assertEquals(ERR_SIZE, 1, target.completeHosts().size());
+        assertTrue(ERR_NOT_FOUND, target.completeHosts().contains(HOST_3));
+    }
+
+    private void validateEvents(Enum... types) {
+        int i = 0;
+        assertEquals("Number of events did not match", types.length, testListener.events.size());
+        for (Event event : testListener.events) {
+            assertEquals("Incorrect event received", types[i], event.type());
+            i++;
+        }
+        testListener.events.clear();
+    }
+
+    private static class TestK8sHostListener implements K8sHostListener {
+        private List<K8sHostEvent> events = Lists.newArrayList();
+
+        @Override
+        public void event(K8sHostEvent event) {
+            events.add(event);
+        }
+    }
+
+    private static class TestCoreService extends CoreServiceAdapter {
+        @Override
+        public ApplicationId registerApplication(String name) {
+            return TEST_APP_ID;
+        }
+    }
+
+    private class TestClusterService extends ClusterServiceAdapter {
+
+    }
+
+    private static class TestLeadershipService extends LeadershipServiceAdapter {
+
+    }
+
+    private static K8sHost createHost(IpAddress hostIp, K8sHostState state, Set<String> nodeNames) {
+        return DefaultK8sHost.builder()
+                .hostIp(hostIp)
+                .nodeNames(nodeNames)
+                .state(state)
+                .build();
+    }
+}
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java
index 50d769f..0bb0258 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/impl/K8sNodeManagerTest.java
@@ -68,6 +68,8 @@
     private static final String ERR_NOT_MATCH = "Node did not match";
     private static final String ERR_NOT_FOUND = "Node did not exist";
 
+    private static final String CLUSTER_NAME = "kubernetes";
+
     private static final String MINION_1_HOSTNAME = "minion_1";
     private static final String MINION_2_HOSTNAME = "minion_2";
     private static final String MINION_3_HOSTNAME = "minion_3";
@@ -84,31 +86,41 @@
     private static final Device MINION_2_LOCAL_DEVICE = createDevice(8);
     private static final Device MINION_3_LOCAL_DEVICE = createDevice(9);
 
+    private static final Device MINION_1_TUN_DEVICE = createDevice(10);
+    private static final Device MINION_2_TUN_DEVICE = createDevice(11);
+    private static final Device MINION_3_TUN_DEVICE = createDevice(12);
+
 
     private static final K8sNode MINION_1 = createNode(
+            CLUSTER_NAME,
             MINION_1_HOSTNAME,
             MINION,
             MINION_1_INTG_DEVICE,
             MINION_1_EXT_DEVICE,
             MINION_1_LOCAL_DEVICE,
+            MINION_1_TUN_DEVICE,
             IpAddress.valueOf("10.100.0.1"),
             INIT
     );
     private static final K8sNode MINION_2 = createNode(
+            CLUSTER_NAME,
             MINION_2_HOSTNAME,
             MINION,
             MINION_2_INTG_DEVICE,
             MINION_2_EXT_DEVICE,
             MINION_2_LOCAL_DEVICE,
+            MINION_2_TUN_DEVICE,
             IpAddress.valueOf("10.100.0.2"),
             INIT
     );
     private static final K8sNode MINION_3 = createNode(
+            CLUSTER_NAME,
             MINION_3_HOSTNAME,
             MINION,
             MINION_3_INTG_DEVICE,
             MINION_3_EXT_DEVICE,
             MINION_3_LOCAL_DEVICE,
+            MINION_3_TUN_DEVICE,
             IpAddress.valueOf("10.100.0.3"),
             COMPLETE
     );
@@ -334,16 +346,18 @@
 
     }
 
-    private static K8sNode createNode(String hostname, K8sNode.Type type,
+    private static K8sNode createNode(String clusterName, String hostname, K8sNode.Type type,
                                       Device intgBridge, Device extBridge,
-                                      Device localBridge, IpAddress ipAddr,
-                                      K8sNodeState state) {
+                                      Device localBridge, Device tunBridge,
+                                      IpAddress ipAddr, K8sNodeState state) {
         return DefaultK8sNode.builder()
                 .hostname(hostname)
+                .clusterName(clusterName)
                 .type(type)
                 .intgBridge(intgBridge.id())
                 .extBridge(extBridge.id())
                 .localBridge(localBridge.id())
+                .tunBridge(tunBridge.id())
                 .managementIp(ipAddr)
                 .dataIp(ipAddr)
                 .state(state)
diff --git a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java
index 207bd0e..1005ab2 100644
--- a/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java
+++ b/apps/k8s-node/app/src/test/java/org/onosproject/k8snode/web/K8sNodeWebResourceTest.java
@@ -25,11 +25,13 @@
 import org.onosproject.codec.impl.CodecManager;
 import org.onosproject.k8snode.api.DefaultK8sApiConfig;
 import org.onosproject.k8snode.api.DefaultK8sNode;
+import org.onosproject.k8snode.api.HostNodesInfo;
 import org.onosproject.k8snode.api.K8sApiConfig;
 import org.onosproject.k8snode.api.K8sApiConfigAdminService;
 import org.onosproject.k8snode.api.K8sNode;
 import org.onosproject.k8snode.api.K8sNodeAdminService;
 import org.onosproject.k8snode.api.K8sNodeState;
+import org.onosproject.k8snode.codec.HostNodesInfoCodec;
 import org.onosproject.k8snode.codec.K8sApiConfigCodec;
 import org.onosproject.k8snode.codec.K8sNodeCodec;
 import org.onosproject.net.DeviceId;
@@ -81,6 +83,7 @@
         codecService.activate();
         codecService.registerCodec(K8sNode.class, new K8sNodeCodec());
         codecService.registerCodec(K8sApiConfig.class, new K8sApiConfigCodec());
+        codecService.registerCodec(HostNodesInfo.class, new HostNodesInfoCodec());
         ServiceDirectory testDirectory =
                 new TestServiceDirectory()
                 .add(K8sNodeAdminService.class, mockK8sNodeAdminService)
@@ -89,6 +92,7 @@
         setServiceDirectory(testDirectory);
 
         k8sNode = DefaultK8sNode.builder()
+                .clusterName("kubernetes")
                 .hostname("minion-node")
                 .type(K8sNode.Type.MINION)
                 .dataIp(IpAddress.valueOf("10.134.34.222"))
@@ -99,6 +103,9 @@
                 .build();
 
         k8sApiConfig = DefaultK8sApiConfig.builder()
+                .clusterName("kubernetes")
+                .segmentId(1)
+                .mode(K8sApiConfig.Mode.NORMAL)
                 .scheme(K8sApiConfig.Scheme.HTTPS)
                 .ipAddress(IpAddress.valueOf("10.134.34.223"))
                 .port(6443)
diff --git a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sApiConfig.json b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sApiConfig.json
index f706cc3..70639b9 100644
--- a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sApiConfig.json
+++ b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sApiConfig.json
@@ -1,9 +1,21 @@
 {
+  "clusterName": "kubernetes",
+  "segmentId": 1,
+  "extNetworkCidr": "192.168.200.0/24",
+  "mode": "NORMAL",
   "scheme" : "HTTPS",
   "ipAddress" : "10.134.34.223",
   "port" : 6443,
   "token": "token",
   "caCertData": "caCertData",
   "clientCertData": "clientCertData",
-  "clientKeyData": "clientKeyData"
+  "clientKeyData": "clientKeyData",
+  "hostNodesInfo": [
+    {
+      "hostIp": "192.168.10.10",
+      "nodes": [
+        "master", "worker"
+      ]
+    }
+  ]
 }
\ No newline at end of file
diff --git a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sHost.json b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sHost.json
new file mode 100644
index 0000000..4abcf2f
--- /dev/null
+++ b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sHost.json
@@ -0,0 +1,7 @@
+{
+  "hostIp": "192.168.200.10",
+  "state": "INIT",
+  "nodeNames": [
+    "1", "2"
+  ]
+}
\ No newline at end of file
diff --git a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sMinionNode.json b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sMinionNode.json
index 2930bfd..495dfee 100644
--- a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sMinionNode.json
+++ b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/codec/K8sMinionNode.json
@@ -1,6 +1,8 @@
 {
+  "clusterName": "kubernetes",
   "hostname": "minion",
   "type": "MINION",
+  "segmentId": 100,
   "managementIp": "172.16.130.4",
   "dataIp": "172.16.130.4",
   "integrationBridge": "of:00000000000000a1",
diff --git a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-api-config.json b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-api-config.json
index 958d434..d29bcee 100644
--- a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-api-config.json
+++ b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-api-config.json
@@ -1,13 +1,24 @@
 {
   "apiConfigs" : [
     {
+      "clusterName": "kubernetes",
+      "segmentId": 1,
+      "mode": "NORMAL",
       "scheme" : "HTTPS",
       "ipAddress" : "10.134.34.223",
       "port" : 6443,
       "token": "token",
       "caCertData": "caCertData",
       "clientCertData": "clientCertData",
-      "clientKeyData": "clientKeyData"
+      "clientKeyData": "clientKeyData",
+      "hostNodesInfo": [
+        {
+          "hostIp": "192.168.10.10",
+          "nodes": [
+            "master", "worker"
+          ]
+        }
+      ]
     }
   ]
 }
\ No newline at end of file
diff --git a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-node-minion-config.json b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-node-minion-config.json
index 8c65bb5..1e7d4a4 100644
--- a/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-node-minion-config.json
+++ b/apps/k8s-node/app/src/test/resources/org/onosproject/k8snode/web/k8s-node-minion-config.json
@@ -1,6 +1,7 @@
 {
   "nodes" : [
     {
+      "clusterName": "kubernetes",
       "hostname" : "minion-node",
       "type" : "MINION",
       "managementIp" : "10.134.231.32",