Initial implementation of kubevirt node along with test cases

Change-Id: I02f6abf2ae58dd79367a8b0b7e4a36fa966bf573
diff --git a/apps/kubevirt-node/api/BUILD b/apps/kubevirt-node/api/BUILD
index 55de911..1aa3718 100644
--- a/apps/kubevirt-node/api/BUILD
+++ b/apps/kubevirt-node/api/BUILD
@@ -9,6 +9,7 @@
 ]
 
 osgi_jar_with_tests(
+    exclude_tests = ["org.onosproject.kubevirtnode.api.KubevirtNodeTest"],
     test_deps = TEST_DEPS,
     deps = COMPILE_DEPS,
 )
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/Constants.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/Constants.java
new file mode 100644
index 0000000..c04c183
--- /dev/null
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * 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.kubevirtnode.api;
+
+/**
+ * Provides constants used in KubeVirt node services.
+ */
+public final class Constants {
+
+    private Constants() {
+    }
+
+    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";
+}
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNode.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNode.java
new file mode 100644
index 0000000..cd653ce
--- /dev/null
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNode.java
@@ -0,0 +1,284 @@
+/*
+ * 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.kubevirtnode.api;
+
+import com.google.common.base.MoreObjects;
+import org.apache.commons.lang.StringUtils;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.kubevirtnode.api.Constants.DEFAULT_CLUSTER_NAME;
+
+/**
+ * Representation of a KubeVirt node.
+ */
+public class DefaultKubevirtNode implements KubevirtNode {
+
+    private static final String NOT_NULL_MSG = "Node % cannot be null";
+    private static final String OVSDB = "ovsdb:";
+
+    private final String clusterName;
+    private final String hostname;
+    private final Type type;
+    private final DeviceId intgBridge;
+    private final IpAddress managementIp;
+    private final IpAddress dataIp;
+    private final KubevirtNodeState state;
+    private final Collection<KubevirtPhyInterface> phyIntfs;
+
+    /**
+     * A default constructor of kubevirt node.
+     *
+     * @param clusterName       clusterName
+     * @param hostname          hostname
+     * @param type              node type
+     * @param intgBridge        integration bridge
+     * @param managementIp      management IP address
+     * @param dataIp            data IP address
+     * @param state             node state
+     * @param phyIntfs          physical interfaces
+     */
+    protected DefaultKubevirtNode(String clusterName, String hostname, Type type,
+                                  DeviceId intgBridge, IpAddress managementIp,
+                                  IpAddress dataIp, KubevirtNodeState state,
+                                  Collection<KubevirtPhyInterface> phyIntfs) {
+        this.clusterName = clusterName;
+        this.hostname = hostname;
+        this.type = type;
+        this.intgBridge = intgBridge;
+        this.managementIp = managementIp;
+        this.dataIp = dataIp;
+        this.state = state;
+        this.phyIntfs = phyIntfs;
+    }
+
+    @Override
+    public String clusterName() {
+        return clusterName;
+    }
+
+    @Override
+    public String hostname() {
+        return hostname;
+    }
+
+    @Override
+    public Type type() {
+        return type;
+    }
+
+    @Override
+    public DeviceId ovsdb() {
+        return DeviceId.deviceId(OVSDB + managementIp().toString());
+    }
+
+    @Override
+    public DeviceId intgBridge() {
+        return intgBridge;
+    }
+
+    @Override
+    public IpAddress managementIp() {
+        return managementIp;
+    }
+
+    @Override
+    public IpAddress dataIp() {
+        return dataIp;
+    }
+
+    @Override
+    public KubevirtNodeState state() {
+        return state;
+    }
+
+    @Override
+    public KubevirtNode updateState(KubevirtNodeState newState) {
+        return new Builder()
+                .hostname(hostname)
+                .clusterName(clusterName)
+                .type(type)
+                .intgBridge(intgBridge)
+                .managementIp(managementIp)
+                .dataIp(dataIp)
+                .state(newState)
+                .phyIntfs(phyIntfs)
+                .build();
+    }
+
+    @Override
+    public Collection<KubevirtPhyInterface> phyIntfs() {
+        if (phyIntfs == null) {
+            return new ArrayList<>();
+        }
+
+        return phyIntfs;
+    }
+
+    /**
+     * Returns new builder instance.
+     *
+     * @return kubevirt node builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns new builder instance with the given node as a default value.
+     *
+     * @param node kubevirt node
+     * @return kubevirt node builder
+     */
+    public static Builder from(KubevirtNode node) {
+        return new Builder()
+                .hostname(node.hostname())
+                .clusterName(node.clusterName())
+                .type(node.type())
+                .intgBridge(node.intgBridge())
+                .managementIp(node.managementIp())
+                .dataIp(node.dataIp())
+                .state(node.state());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultKubevirtNode that = (DefaultKubevirtNode) o;
+        return clusterName.equals(that.clusterName) &&
+                hostname.equals(that.hostname) &&
+                type == that.type &&
+                intgBridge.equals(that.intgBridge) &&
+                managementIp.equals(that.managementIp) &&
+                dataIp.equals(that.dataIp);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(clusterName, hostname, type, intgBridge,
+                managementIp, dataIp);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("clusterName", clusterName)
+                .add("hostname", hostname)
+                .add("type", type)
+                .add("intgBridge", intgBridge)
+                .add("managementIp", managementIp)
+                .add("dataIp", dataIp)
+                .add("state", state)
+                .toString();
+    }
+
+    public static final class Builder implements KubevirtNode.Builder {
+
+        private String clusterName;
+        private String hostname;
+        private Type type;
+        private DeviceId intgBridge;
+        private IpAddress managementIp;
+        private IpAddress dataIp;
+        private KubevirtNodeState state;
+        private Collection<KubevirtPhyInterface> phyIntfs;
+
+        // private constructor not intended to use from external
+        private Builder() {
+        }
+
+        @Override
+        public KubevirtNode build() {
+            checkArgument(hostname != null, NOT_NULL_MSG, "hostname");
+            checkArgument(type != null, NOT_NULL_MSG, "type");
+            checkArgument(state != null, NOT_NULL_MSG, "state");
+            checkArgument(managementIp != null, NOT_NULL_MSG, "management IP");
+
+            if (StringUtils.isEmpty(clusterName)) {
+                clusterName = DEFAULT_CLUSTER_NAME;
+            }
+
+            return new DefaultKubevirtNode(
+                    clusterName,
+                    hostname,
+                    type,
+                    intgBridge,
+                    managementIp,
+                    dataIp,
+                    state,
+                    phyIntfs
+            );
+        }
+
+        @Override
+        public Builder clusterName(String clusterName) {
+            this.clusterName = clusterName;
+            return this;
+        }
+
+        @Override
+        public Builder hostname(String hostname) {
+            this.hostname = hostname;
+            return this;
+        }
+
+        @Override
+        public Builder type(Type type) {
+            this.type = type;
+            return this;
+        }
+
+        @Override
+        public Builder intgBridge(DeviceId deviceId) {
+            this.intgBridge = deviceId;
+            return this;
+        }
+
+        @Override
+        public Builder managementIp(IpAddress managementIp) {
+            this.managementIp = managementIp;
+            return this;
+        }
+
+        @Override
+        public Builder dataIp(IpAddress dataIp) {
+            this.dataIp = dataIp;
+            return this;
+        }
+
+        @Override
+        public Builder phyIntfs(Collection<KubevirtPhyInterface> phyIntfs) {
+            this.phyIntfs = phyIntfs;
+            return this;
+        }
+
+        @Override
+        public Builder state(KubevirtNodeState state) {
+            this.state = state;
+            return this;
+        }
+    }
+}
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtPhyInterface.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtPhyInterface.java
new file mode 100644
index 0000000..1cae8f2
--- /dev/null
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/DefaultKubevirtPhyInterface.java
@@ -0,0 +1,119 @@
+/*
+ * 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.kubevirtnode.api;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Implementation class of kubevirt physical interface.
+ */
+public class DefaultKubevirtPhyInterface implements KubevirtPhyInterface {
+
+    private final String network;
+    private final String intf;
+
+    private static final String NOT_NULL_MSG = "% cannot be null";
+
+    /**
+     * A default constructor of kubevirt physical interface.
+     *
+     * @param network network that this physical interface connects with
+     * @param intf    name of physical interface
+     */
+    protected DefaultKubevirtPhyInterface(String network, String intf) {
+        this.network = network;
+        this.intf = intf;
+    }
+
+    @Override
+    public String network() {
+        return network;
+    }
+
+    @Override
+    public String intf() {
+        return intf;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        DefaultKubevirtPhyInterface that = (DefaultKubevirtPhyInterface) o;
+        return network.equals(that.network) &&
+                intf.equals(that.intf);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(network, intf);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(this)
+                .add("network", network)
+                .add("intf", intf)
+                .toString();
+    }
+
+    /**
+     * Returns new builder instance.
+     *
+     * @return kubevirt physical interface builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    public static final class Builder implements KubevirtPhyInterface.Builder {
+
+        private String network;
+        private String intf;
+
+        // private constructor not intended to use from external
+        private Builder() {
+        }
+
+        @Override
+        public KubevirtPhyInterface build() {
+            checkArgument(network != null, NOT_NULL_MSG, "network");
+            checkArgument(intf != null, NOT_NULL_MSG, "intf");
+
+            return new DefaultKubevirtPhyInterface(network, intf);
+        }
+
+        @Override
+        public KubevirtPhyInterface.Builder network(String network) {
+            this.network = network;
+            return this;
+        }
+
+        @Override
+        public KubevirtPhyInterface.Builder intf(String intf) {
+            this.intf = intf;
+            return this;
+        }
+    }
+}
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNode.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNode.java
new file mode 100644
index 0000000..544c76b
--- /dev/null
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNode.java
@@ -0,0 +1,189 @@
+/*
+ * 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.kubevirtnode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DeviceId;
+
+import java.util.Collection;
+
+/**
+ * Representation of a KubeVirt node used in kubevirt networking service.
+ */
+public interface KubevirtNode {
+
+    /**
+     * Lists of kubernetes node types.
+     */
+    enum Type {
+        /**
+         * Signifies that this is a kubevirt master node.
+         */
+        MASTER,
+
+        /**
+         * Signifies that this is a kubevirt worker node.
+         */
+        WORKER,
+    }
+
+    /**
+     * Returns cluster name of the node.
+     *
+     * @return cluster name
+     */
+    String clusterName();
+
+    /**
+     * Returns hostname of the node.
+     *
+     * @return hostname
+     */
+    String hostname();
+
+    /**
+     * Returns the type of the node.
+     *
+     * @return node type
+     */
+    KubevirtNode.Type type();
+
+    /**
+     * Returns the OVSDB device ID of the node.
+     *
+     * @return ovsdb device id
+     */
+    DeviceId ovsdb();
+
+    /**
+     * Returns the device ID of the integration bridge at the node.
+     *
+     * @return device id
+     */
+    DeviceId intgBridge();
+
+    /**
+     * Returns the management network IP address of the node.
+     *
+     * @return ip address
+     */
+    IpAddress managementIp();
+
+    /**
+     * Returns the data network IP address used for tunneling.
+     *
+     * @return ip address; null if tunnel mode is not enabled
+     */
+    IpAddress dataIp();
+
+    /**
+     * Returns the initialization state of the node.
+     *
+     * @return node state
+     */
+    KubevirtNodeState state();
+
+    /**
+     * Returns new kubevirt node instance with given state.
+     *
+     * @param newState updated state
+     * @return updated kubernetes node
+     */
+    KubevirtNode updateState(KubevirtNodeState newState);
+
+    /**
+     * Returns a collection of physical interfaces.
+     *
+     * @return physical interfaces
+     */
+    Collection<KubevirtPhyInterface> phyIntfs();
+
+    /**
+     * Builder of new node entity.
+     */
+    interface Builder {
+        /**
+         * Builds an immutable kubevirt node instance.
+         *
+         * @return kubevirt node instance
+         */
+        KubevirtNode build();
+
+        /**
+         * Returns kubevirt node builder with supplied cluster name.
+         *
+         * @param clusterName cluster name
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder clusterName(String clusterName);
+
+        /**
+         * Returns kubevirt node builder with supplied hostname.
+         *
+         * @param hostname hostname of the node
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder hostname(String hostname);
+
+        /**
+         * Returns kubevirt node builder with supplied type.
+         *
+         * @param type kubevirt node type
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder type(KubevirtNode.Type type);
+
+        /**
+         * Returns kubevirt node builder with supplied integration bridge name.
+         *
+         * @param deviceId integration bridge device ID
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder intgBridge(DeviceId deviceId);
+
+        /**
+         * Returns kubevirt node builder with supplied management IP address.
+         *
+         * @param managementIp management IP address
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder managementIp(IpAddress managementIp);
+
+        /**
+         * Returns kubevirt node builder with supplied data IP address.
+         *
+         * @param dataIp data IP address
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder dataIp(IpAddress dataIp);
+
+        /**
+         * Returns kubevirt node builder with supplied physical interfaces.
+         *
+         * @param phyIntfs a collection of physical interfaces
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder phyIntfs(Collection<KubevirtPhyInterface> phyIntfs);
+
+        /**
+         * Returns kubevirt node builder with supplied node state.
+         *
+         * @param state kubevirt node state
+         * @return kubevirt node builder
+         */
+        KubevirtNode.Builder state(KubevirtNodeState state);
+    }
+}
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNodeHandler.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNodeHandler.java
new file mode 100644
index 0000000..d3833c1
--- /dev/null
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNodeHandler.java
@@ -0,0 +1,22 @@
+/*
+ * 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.kubevirtnode.api;
+
+/**
+ * Service handling KubeVirt node state.
+ */
+public interface KubevirtNodeHandler {
+}
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNodeState.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNodeState.java
new file mode 100644
index 0000000..87d921e
--- /dev/null
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtNodeState.java
@@ -0,0 +1,122 @@
+/*
+ * 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.kubevirtnode.api;
+
+/**
+ * Defines the initialization stats of KubeVirt node.
+ */
+public enum KubevirtNodeState {
+
+    /**
+     * Indicates the node is in pre-on-board.
+     */
+    PRE_ON_BOARD {
+        @Override
+        public void process(KubevirtNodeHandler handler, KubevirtNode node) {
+
+        }
+
+        @Override
+        public KubevirtNodeState nodeState() {
+            return null;
+        }
+    },
+    /**
+     * Indicates the node is on-boarded.
+     */
+    ON_BOARDED {
+        @Override
+        public void process(KubevirtNodeHandler handler, KubevirtNode node) {
+
+        }
+
+        @Override
+        public KubevirtNodeState nodeState() {
+            return null;
+        }
+    },
+    /**
+     * Indicates the node is newly added.
+     */
+    INIT {
+        @Override
+        public void process(KubevirtNodeHandler handler, KubevirtNode node) {
+
+        }
+
+        @Override
+        public KubevirtNodeState nodeState() {
+            return null;
+        }
+    },
+    /**
+     * Indicates bridge devices are added according to the node state.
+     */
+    DEVICE_CREATED {
+        @Override
+        public void process(KubevirtNodeHandler handler, KubevirtNode node) {
+
+        }
+
+        @Override
+        public KubevirtNodeState nodeState() {
+            return null;
+        }
+    },
+    /**
+     * Indicates node initialization is done.
+     */
+    COMPLETE {
+        @Override
+        public void process(KubevirtNodeHandler handler, KubevirtNode node) {
+
+        }
+
+        @Override
+        public KubevirtNodeState nodeState() {
+            return null;
+        }
+    },
+    /**
+     * Indicates node is broken.
+     */
+    INCOMPLETE {
+        @Override
+        public void process(KubevirtNodeHandler handler, KubevirtNode node) {
+
+        }
+
+        @Override
+        public KubevirtNodeState nodeState() {
+            return null;
+        }
+    };
+
+    /**
+     * Processes the given kubevirt node which is under a certain state.
+     *
+     * @param handler kubevirt node handler
+     * @param node kubevirt node
+     */
+    public abstract void process(KubevirtNodeHandler handler, KubevirtNode node);
+
+    /**
+     * Transits to the next state.
+     *
+     * @return the next kubevirt node state
+     */
+    public abstract KubevirtNodeState nodeState();
+}
diff --git a/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtPhyInterface.java b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtPhyInterface.java
new file mode 100644
index 0000000..40e8661
--- /dev/null
+++ b/apps/kubevirt-node/api/src/main/java/org/onosproject/kubevirtnode/api/KubevirtPhyInterface.java
@@ -0,0 +1,56 @@
+/*
+ * 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.kubevirtnode.api;
+
+public interface KubevirtPhyInterface {
+    /**
+     * Returns physical network name that this interface binds to.
+     *
+     * @return physical network name
+     */
+    String network();
+
+    /**
+     * Returns name of this physical interface.
+     *
+     * @return name of this physical interface
+     */
+    String intf();
+
+    /**
+     * Builder of kubevirt physical interface.
+     */
+    interface Builder {
+
+        KubevirtPhyInterface build();
+
+        /**
+         * Returns physical network that this physical interface connects with.
+         *
+         * @param network network name
+         * @return kubevirt physical interface builder
+         */
+        Builder network(String network);
+
+        /**
+         * Returns physical interface name.
+         *
+         * @param intf physical interface name of openstack node
+         * @return kubevirt physical interface builder
+         */
+        Builder intf(String intf);
+    }
+}
diff --git a/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNodeTest.java b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNodeTest.java
new file mode 100644
index 0000000..2fa6441
--- /dev/null
+++ b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtNodeTest.java
@@ -0,0 +1,189 @@
+/*
+ * 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.kubevirtnode.api;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.Device;
+
+import java.util.List;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.onosproject.kubevirtnode.api.KubevirtNodeState.DEVICE_CREATED;
+
+/**
+ * Unit tests for DefaultKubevirtNode.
+ */
+public final class DefaultKubevirtNodeTest extends KubevirtNodeTest {
+
+    private static final IpAddress TEST_IP = IpAddress.valueOf("10.100.0.3");
+
+    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 IpAddress MANAGEMENT_IP = IpAddress.valueOf("10.10.10.10");
+    private static final IpAddress DATA_IP = IpAddress.valueOf("20.20.20.20");
+
+    private static final String PHY_INTF_NETWORK = "mgmtnetwork";
+    private static final String PHY_INTF_NAME = "eth3";
+
+    private static final List<KubevirtPhyInterface> PHY_INTFS = initPhyIntfs();
+
+    private KubevirtNode refNode;
+
+    private static final KubevirtNode KV_NODE_1 = createNode(
+            HOSTNAME_1,
+            KubevirtNode.Type.WORKER,
+            DEVICE_1,
+            TEST_IP,
+            KubevirtNodeState.INIT);
+
+    private static final KubevirtNode KV_NODE_2 = createNode(
+            HOSTNAME_1,
+            KubevirtNode.Type.WORKER,
+            DEVICE_1,
+            TEST_IP,
+            KubevirtNodeState.COMPLETE);
+
+    private static final KubevirtNode KV_NODE_3 = createNode(
+            HOSTNAME_2,
+            KubevirtNode.Type.WORKER,
+            DEVICE_2,
+            TEST_IP,
+            KubevirtNodeState.INIT);
+
+    /**
+     * Initial setup for this unit test.
+     */
+    @Before
+    public void setUp() {
+        refNode = DefaultKubevirtNode.builder()
+                .hostname(HOSTNAME_1)
+                .type(KubevirtNode.Type.WORKER)
+                .managementIp(MANAGEMENT_IP)
+                .dataIp(DATA_IP)
+                .intgBridge(DEVICE_1.id())
+                .state(KubevirtNodeState.COMPLETE)
+                .phyIntfs(PHY_INTFS)
+                .build();
+    }
+
+    /**
+     * Checks equals method works as expected.
+     */
+    @Test
+    public void testEquality() {
+        new EqualsTester().addEqualityGroup(KV_NODE_1, KV_NODE_2)
+                .addEqualityGroup(KV_NODE_3)
+                .testEquals();
+    }
+
+    /**
+     * Test object construction.
+     */
+    @Test
+    public void testConstruction() {
+        checkCommonProperties(refNode);
+        assertEquals(refNode.state(), KubevirtNodeState.COMPLETE);
+        assertEquals(refNode.intgBridge(), DEVICE_1.id());
+    }
+
+    /**
+     * Checks the functionality of update state method.
+     */
+    @Test
+    public void testUpdateState() {
+        KubevirtNode updatedNode = refNode.updateState(DEVICE_CREATED);
+
+        checkCommonProperties(updatedNode);
+        assertEquals(updatedNode.state(), DEVICE_CREATED);
+    }
+
+    /**
+     * Checks the functionality of from method.
+     */
+    @Test
+    public void testFrom() {
+        KubevirtNode updatedNode = DefaultKubevirtNode.from(refNode).build();
+
+        assertEquals(updatedNode, refNode);
+    }
+
+    /**
+     * Checks building a node without hostname fails with proper exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuildWithoutHostname() {
+        DefaultKubevirtNode.builder()
+                .type(KubevirtNode.Type.WORKER)
+                .intgBridge(DEVICE_1.id())
+                .managementIp(TEST_IP)
+                .dataIp(TEST_IP)
+                .state(KubevirtNodeState.INIT)
+                .build();
+    }
+
+    /**
+     * Checks building a node without type fails with proper exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuildWithoutType() {
+        DefaultKubevirtNode.builder()
+                .hostname(HOSTNAME_1)
+                .intgBridge(DEVICE_1.id())
+                .managementIp(TEST_IP)
+                .dataIp(TEST_IP)
+                .state(KubevirtNodeState.INIT)
+                .build();
+    }
+
+    /**
+     * Checks building a node without management IP address fails with
+     * proper exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testBuildWithoutManagementIp() {
+        DefaultKubevirtNode.builder()
+                .hostname(HOSTNAME_1)
+                .type(KubevirtNode.Type.WORKER)
+                .intgBridge(DEVICE_1.id())
+                .dataIp(TEST_IP)
+                .state(KubevirtNodeState.INIT)
+                .build();
+    }
+
+    private static List<KubevirtPhyInterface> initPhyIntfs() {
+        KubevirtPhyInterface phyIntf = DefaultKubevirtPhyInterface.builder()
+                .intf(PHY_INTF_NAME)
+                .network(PHY_INTF_NETWORK)
+                .build();
+
+        return ImmutableList.of(phyIntf);
+    }
+
+    private void checkCommonProperties(KubevirtNode node) {
+        assertEquals(node.hostname(), HOSTNAME_1);
+        assertEquals(node.type(), KubevirtNode.Type.WORKER);
+        assertEquals(node.managementIp(), MANAGEMENT_IP);
+        assertEquals(node.dataIp(), DATA_IP);
+        assertEquals(node.phyIntfs(), PHY_INTFS);
+    }
+}
diff --git a/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtPhyInterfaceTest.java b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtPhyInterfaceTest.java
new file mode 100644
index 0000000..37ecdf4
--- /dev/null
+++ b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/DefaultKubevirtPhyInterfaceTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.kubevirtnode.api;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Unit tests for DefaultOpenstackPhyInterface.
+ */
+public class DefaultKubevirtPhyInterfaceTest {
+
+    private static final String NETWORK_1 = "mgmtnetwork";
+    private static final String NETWORK_2 = "oamnetwork";
+    private static final String INTERFACE_1 = "eth3";
+    private static final String INTERFACE_2 = "eth4";
+
+    private static final KubevirtPhyInterface KV_PHY_INTF_1 =
+            new DefaultKubevirtPhyInterface(NETWORK_1, INTERFACE_1);
+    private static final KubevirtPhyInterface KV_PHY_INTF_2 =
+            new DefaultKubevirtPhyInterface(NETWORK_1, INTERFACE_1);
+    private static final KubevirtPhyInterface KV_PHY_INTF_3 =
+            new DefaultKubevirtPhyInterface(NETWORK_2, INTERFACE_2);
+
+    @Test
+    public void testEquality() {
+        new EqualsTester().addEqualityGroup(KV_PHY_INTF_1, KV_PHY_INTF_2)
+                .addEqualityGroup(KV_PHY_INTF_3)
+                .testEquals();
+    }
+
+    @Test
+    public void testConstruction() {
+        DefaultKubevirtPhyInterface phyIntf = (DefaultKubevirtPhyInterface)
+                KV_PHY_INTF_1;
+
+        assertThat(phyIntf.network(), is(NETWORK_1));
+        assertThat(phyIntf.intf(), is(INTERFACE_1));
+    }
+}
diff --git a/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/KubevirtNodeTest.java b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/KubevirtNodeTest.java
new file mode 100644
index 0000000..abb1643
--- /dev/null
+++ b/apps/kubevirt-node/api/src/test/java/org/onosproject/kubevirtnode/api/KubevirtNodeTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.kubevirtnode.api;
+
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.ProviderId;
+
+import static org.onosproject.net.Device.Type.SWITCH;
+
+/**
+ * Provides a set of test KubevirtNode parameters for use with KubevirtNode related tests.
+ */
+public abstract class KubevirtNodeTest {
+
+    public static Device createDevice(long devIdNum) {
+        return new DefaultDevice(new ProviderId("of", "foo"),
+                DeviceId.deviceId(String.format("of:%016d", devIdNum)),
+                SWITCH,
+                "manufacturer",
+                "hwVersion",
+                "swVersion",
+                "serialNumber",
+                new ChassisId(1));
+    }
+
+    public static KubevirtNode createNode(String hostname, KubevirtNode.Type type,
+                                          Device intgBridge, IpAddress ipAddr,
+                                          KubevirtNodeState state) {
+        return DefaultKubevirtNode.builder()
+                .hostname(hostname)
+                .type(type)
+                .intgBridge(intgBridge.id())
+                .managementIp(ipAddr)
+                .dataIp(ipAddr)
+                .state(state)
+                .build();
+    }
+}