Extract web and codec into a separated web pkg in kubevirt-net app

Change-Id: If15fe4391009a09a79e60965d90fac26d5bad7fe
diff --git a/apps/kubevirt-networking/web/BUILD b/apps/kubevirt-networking/web/BUILD
new file mode 100644
index 0000000..06edea6
--- /dev/null
+++ b/apps/kubevirt-networking/web/BUILD
@@ -0,0 +1,21 @@
+COMPILE_DEPS = CORE_DEPS + JACKSON + KRYO + REST + [
+    "//apps/kubevirt-node/api:onos-apps-kubevirt-node-api",
+    "//apps/kubevirt-networking/api:onos-apps-kubevirt-networking-api",
+]
+
+TEST_DEPS = TEST_ADAPTERS + TEST_REST + [
+    "//core/api:onos-api-tests",
+    "//core/common:onos-core-common-tests",
+    "//web/api:onos-rest-tests",
+    "@minimal_json//jar",
+]
+
+osgi_jar_with_tests(
+    api_description = "REST API for KubeVirt Networking",
+    api_package = "org.onosproject.kubevirtnetworking.web",
+    api_title = "KubeVirt Networking API",
+    api_version = "1.0",
+    test_deps = TEST_DEPS,
+    web_context = "/onos/kubevirtnetworking",
+    deps = COMPILE_DEPS,
+)
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpCodec.java
new file mode 100644
index 0000000..fd530a1
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpCodec.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+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.kubevirtnetworking.api.DefaultKubevirtFloatingIp;
+import org.onosproject.kubevirtnetworking.api.KubevirtFloatingIp;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubevirt floating IP codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtFloatingIpCodec extends JsonCodec<KubevirtFloatingIp> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String ID = "id";
+    private static final String ROUTER_NAME = "routerName";
+    private static final String POD_NAME = "podName";
+    private static final String VM_NAME = "vmName";
+    private static final String NETWORK_NAME = "networkName";
+    private static final String FLOATING_IP = "floatingIp";
+    private static final String FIXED_IP = "fixedIp";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtFloatingIp";
+
+    @Override
+    public ObjectNode encode(KubevirtFloatingIp fip, CodecContext context) {
+        checkNotNull(fip, "Kubevirt floating IP cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(ID, fip.id())
+                .put(ROUTER_NAME, fip.routerName())
+                .put(NETWORK_NAME, fip.networkName())
+                .put(FLOATING_IP, fip.floatingIp().toString());
+
+        if (fip.podName() != null) {
+            result.put(POD_NAME, fip.podName());
+        }
+
+        if (fip.vmName() != null) {
+            result.put(VM_NAME, fip.vmName());
+        }
+
+        if (fip.fixedIp() != null) {
+            result.put(FIXED_IP, fip.fixedIp().toString());
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtFloatingIp decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String id = nullIsIllegal(json.get(ID).asText(), ID + MISSING_MESSAGE);
+        String routerName = nullIsIllegal(json.get(ROUTER_NAME).asText(),
+                ROUTER_NAME + MISSING_MESSAGE);
+        String floatingIp = nullIsIllegal(json.get(FLOATING_IP).asText(),
+                FLOATING_IP + MISSING_MESSAGE);
+        String networkName = nullIsIllegal(json.get(NETWORK_NAME).asText(),
+                NETWORK_NAME + MISSING_MESSAGE);
+
+        KubevirtFloatingIp.Builder builder = DefaultKubevirtFloatingIp.builder()
+                .id(id)
+                .routerName(routerName)
+                .networkName(networkName)
+                .floatingIp(IpAddress.valueOf(floatingIp));
+
+        JsonNode podName = json.get(POD_NAME);
+        if (podName != null) {
+            builder.podName(podName.asText());
+        }
+
+        JsonNode vmName = json.get(VM_NAME);
+        if (vmName != null) {
+            builder.vmName(vmName.asText());
+        }
+
+        JsonNode fixedIp = json.get(FIXED_IP);
+        if (fixedIp != null) {
+            builder.fixedIp(IpAddress.valueOf(fixedIp.asText()));
+        }
+
+        return builder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtHostRouteCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtHostRouteCodec.java
new file mode 100644
index 0000000..2d73979
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtHostRouteCodec.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubevirt host route codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtHostRouteCodec extends JsonCodec<KubevirtHostRoute> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String DESTINATION = "destination";
+    private static final String NEXTHOP = "nexthop";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtHostRoute";
+
+    @Override
+    public ObjectNode encode(KubevirtHostRoute hostRoute, CodecContext context) {
+        checkNotNull(hostRoute, "Kubernetes network cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(DESTINATION, hostRoute.destination().toString());
+
+        if (hostRoute.nexthop() != null) {
+            result.put(NEXTHOP, hostRoute.nexthop().toString());
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtHostRoute decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String destination = nullIsIllegal(json.get(DESTINATION).asText(),
+                DESTINATION + MISSING_MESSAGE);
+        JsonNode nexthopJson = json.get(NEXTHOP);
+        IpAddress nexthop = null;
+
+        if (nexthopJson != null) {
+            nexthop = IpAddress.valueOf(nexthopJson.asText());
+        }
+
+        return new KubevirtHostRoute(IpPrefix.valueOf(destination), nexthop);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtIpPoolCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtIpPoolCodec.java
new file mode 100644
index 0000000..8f5522c
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtIpPoolCodec.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+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.kubevirtnetworking.api.KubevirtIpPool;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubevirt IP pool codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtIpPoolCodec extends JsonCodec<KubevirtIpPool> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String START = "start";
+    private static final String END = "end";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtIpPool";
+
+    @Override
+    public ObjectNode encode(KubevirtIpPool ipPool, CodecContext context) {
+        checkNotNull(ipPool, "Kubevirt IP pool cannot be null");
+
+        return context.mapper().createObjectNode()
+                .put(START, ipPool.start().toString())
+                .put(END, ipPool.end().toString());
+    }
+
+    @Override
+    public KubevirtIpPool decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String start = nullIsIllegal(json.get(START).asText(), START + MISSING_MESSAGE);
+        String end = nullIsIllegal(json.get(END).asText(), END + MISSING_MESSAGE);
+
+        return new KubevirtIpPool(IpAddress.valueOf(start), IpAddress.valueOf(end));
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerCodec.java
new file mode 100644
index 0000000..c11d829
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerCodec.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.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.kubevirtnetworking.api.DefaultKubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerRule;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public final class KubevirtLoadBalancerCodec extends JsonCodec<KubevirtLoadBalancer> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String NAME = "name";
+    private static final String DESCRIPTION = "description";
+    private static final String VIP = "vip";
+    private static final String NETWORK_ID = "networkId";
+    private static final String MEMBERS = "members";
+    private static final String RULES = "rules";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtLoadBalancer";
+
+    @Override
+    public ObjectNode encode(KubevirtLoadBalancer lb, CodecContext context) {
+        checkNotNull(lb, "Kubevirt load balancer cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(NAME, lb.name())
+                .put(VIP, lb.vip().toString())
+                .put(NETWORK_ID, lb.networkId());
+
+        if (lb.description() != null) {
+            result.put(DESCRIPTION, lb.description());
+        }
+
+        if (lb.members() != null && !lb.members().isEmpty()) {
+            ArrayNode members = context.mapper().createArrayNode();
+            for (IpAddress ip : lb.members()) {
+                members.add(ip.toString());
+            }
+            result.set(MEMBERS, members);
+        }
+
+        if (lb.rules() != null && !lb.rules().isEmpty()) {
+            ArrayNode rules = context.mapper().createArrayNode();
+            for (KubevirtLoadBalancerRule rule : lb.rules()) {
+                ObjectNode ruleJson = context.codec(
+                        KubevirtLoadBalancerRule.class).encode(rule, context);
+                rules.add(ruleJson);
+            }
+            result.set(RULES, rules);
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtLoadBalancer decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String name = nullIsIllegal(json.get(NAME).asText(), NAME + MISSING_MESSAGE);
+        IpAddress vip = IpAddress.valueOf(nullIsIllegal(json.get(VIP).asText(),
+                VIP + MISSING_MESSAGE));
+        String networkId = nullIsIllegal(json.get(NETWORK_ID).asText(),
+                NETWORK_ID + MISSING_MESSAGE);
+
+        KubevirtLoadBalancer.Builder builder = DefaultKubevirtLoadBalancer.builder()
+                .name(name)
+                .vip(vip)
+                .networkId(networkId);
+
+        JsonNode description = json.get(DESCRIPTION);
+        if (description != null) {
+            builder.description(description.asText());
+        }
+
+        ArrayNode membersJson = (ArrayNode) json.get(MEMBERS);
+        if (membersJson != null) {
+            Set<IpAddress> members = new HashSet<>();
+            for (JsonNode memberJson : membersJson) {
+                members.add(IpAddress.valueOf(memberJson.asText()));
+            }
+            builder.members(members);
+        }
+
+        JsonNode rulesJson = json.get(RULES);
+        if (rulesJson != null) {
+            Set<KubevirtLoadBalancerRule> rules = new HashSet<>();
+            IntStream.range(0, rulesJson.size())
+                    .forEach(i -> {
+                        ObjectNode ruleJson = get(rulesJson, i);
+                        KubevirtLoadBalancerRule rule = context.codec(
+                                KubevirtLoadBalancerRule.class).decode(ruleJson, context);
+                        rules.add(rule);
+                    });
+            builder.rules(rules);
+        }
+
+        return builder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerRuleCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerRuleCodec.java
new file mode 100644
index 0000000..582d4d3
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerRuleCodec.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtLoadBalancerRule;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerRule;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubevirt load balancer rule codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtLoadBalancerRuleCodec extends JsonCodec<KubevirtLoadBalancerRule> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String PROTOCOL = "protocol";
+    private static final String PORT_RANGE_MAX = "portRangeMax";
+    private static final String PORT_RANGE_MIN = "portRangeMin";
+    private static final String TCP = "TCP";
+    private static final String UDP = "UDP";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtLoadBalancerRule";
+
+    @Override
+    public ObjectNode encode(KubevirtLoadBalancerRule rule, CodecContext context) {
+        checkNotNull(rule, "Kubevirt load balancer rule cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode().put(PROTOCOL, rule.protocol());
+
+        if (rule.protocol().equalsIgnoreCase(TCP) || rule.protocol().equalsIgnoreCase(UDP)) {
+            result.put(PORT_RANGE_MAX, rule.portRangeMax()).put(PORT_RANGE_MIN, rule.portRangeMin());
+        }
+        return result;
+    }
+
+    @Override
+    public KubevirtLoadBalancerRule decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        KubevirtLoadBalancerRule.Builder builder = DefaultKubevirtLoadBalancerRule.builder();
+
+        JsonNode protocolJson = json.get(PROTOCOL);
+        String protocol = "";
+        if (protocolJson != null) {
+            protocol = protocolJson.asText();
+        }
+        builder.protocol(protocol);
+
+        if (protocol.equalsIgnoreCase(TCP) || protocol.equalsIgnoreCase(UDP)) {
+            Integer portRangeMax = json.get(PORT_RANGE_MAX).asInt();
+            Integer portRangeMin = json.get(PORT_RANGE_MIN).asInt();
+            builder.portRangeMax(portRangeMax).portRangeMin(portRangeMin);
+        }
+
+        return builder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkCodec.java
new file mode 100644
index 0000000..2d8a07c
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkCodec.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.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.kubevirtnetworking.api.DefaultKubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubevirt network codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtNetworkCodec extends JsonCodec<KubevirtNetwork> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String NETWORK_ID = "networkId";
+    private static final String TYPE = "type";
+    private static final String NAME = "name";
+    private static final String MTU = "mtu";
+    private static final String SEGMENT_ID = "segmentId";
+    private static final String GATEWAY_IP = "gatewayIp";
+    private static final String DEFAULT_ROUTE = "defaultRoute";
+    private static final String CIDR = "cidr";
+    private static final String HOST_ROUTES = "hostRoutes";
+    private static final String IP_POOL = "ipPool";
+    private static final String DNSES = "dnses";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtNetwork";
+
+    @Override
+    public ObjectNode encode(KubevirtNetwork network, CodecContext context) {
+        checkNotNull(network, "Kubevirt network cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(NETWORK_ID, network.networkId())
+                .put(TYPE, network.type().name())
+                .put(NAME, network.name())
+                .put(MTU, network.mtu())
+                .put(GATEWAY_IP, network.gatewayIp().toString())
+                .put(DEFAULT_ROUTE, network.defaultRoute())
+                .put(CIDR, network.cidr());
+
+        if (network.segmentId() != null) {
+            result.put(SEGMENT_ID, network.segmentId());
+        }
+
+        if (network.hostRoutes() != null && !network.hostRoutes().isEmpty()) {
+            ArrayNode hostRoutes = context.mapper().createArrayNode();
+            network.hostRoutes().forEach(hostRoute -> {
+                ObjectNode hostRouteJson =
+                        context.codec(KubevirtHostRoute.class).encode(hostRoute, context);
+                hostRoutes.add(hostRouteJson);
+            });
+            result.set(HOST_ROUTES, hostRoutes);
+        }
+
+        if (network.ipPool() != null) {
+            ObjectNode ipPoolJson = context.codec(KubevirtIpPool.class).encode(network.ipPool(), context);
+            result.set(IP_POOL, ipPoolJson);
+        }
+
+        if (network.dnses() != null && !network.dnses().isEmpty()) {
+            ArrayNode dnses = context.mapper().createArrayNode();
+            network.dnses().forEach(dns -> {
+                dnses.add(dns.toString());
+            });
+            result.set(DNSES, dnses);
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtNetwork decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String networkId = nullIsIllegal(json.get(NETWORK_ID).asText(),
+                NETWORK_ID + MISSING_MESSAGE);
+        String type = nullIsIllegal(json.get(TYPE).asText(),
+                TYPE + MISSING_MESSAGE);
+        String name = nullIsIllegal(json.get(NAME).asText(),
+                NAME + MISSING_MESSAGE);
+        Integer mtu = nullIsIllegal(json.get(MTU).asInt(),
+                MTU + MISSING_MESSAGE);
+        String gatewayIp = nullIsIllegal(json.get(GATEWAY_IP).asText(),
+                GATEWAY_IP + MISSING_MESSAGE);
+        boolean defaultRoute = nullIsIllegal(json.get(DEFAULT_ROUTE).asBoolean(),
+                DEFAULT_ROUTE + MISSING_MESSAGE);
+        String cidr = nullIsIllegal(json.get(CIDR).asText(),
+                CIDR + MISSING_MESSAGE);
+
+        KubevirtNetwork.Builder networkBuilder = DefaultKubevirtNetwork.builder()
+                .networkId(networkId)
+                .type(KubevirtNetwork.Type.valueOf(type))
+                .name(name)
+                .mtu(mtu)
+                .gatewayIp(IpAddress.valueOf(gatewayIp))
+                .defaultRoute(defaultRoute)
+                .cidr(cidr);
+
+        if (!type.equals(KubevirtNetwork.Type.FLAT.name())) {
+            JsonNode segmentIdJson = json.get(SEGMENT_ID);
+            if (segmentIdJson != null) {
+                networkBuilder.segmentId(segmentIdJson.asText());
+            }
+        }
+
+        JsonNode ipPoolJson = json.get(IP_POOL);
+        if (ipPoolJson != null) {
+            final JsonCodec<KubevirtIpPool>
+                    ipPoolCodec = context.codec(KubevirtIpPool.class);
+            networkBuilder.ipPool(ipPoolCodec.decode(
+                    (ObjectNode) ipPoolJson.deepCopy(), context));
+        }
+
+        // parse host routes
+        Set<KubevirtHostRoute> hostRoutes = new HashSet<>();
+        JsonNode hostRoutesJson = json.get(HOST_ROUTES);
+        if (hostRoutesJson != null) {
+            final JsonCodec<KubevirtHostRoute>
+                    hostRouteCodec = context.codec(KubevirtHostRoute.class);
+
+            IntStream.range(0, hostRoutesJson.size()).forEach(i -> {
+                ObjectNode routeJson = get(hostRoutesJson, i);
+                hostRoutes.add(hostRouteCodec.decode(routeJson, context));
+            });
+        }
+        networkBuilder.hostRoutes(hostRoutes);
+
+        // parse DNSes
+        Set<IpAddress> dnses = new HashSet<>();
+        JsonNode dnsesJson = json.get(DNSES);
+        if (dnsesJson != null) {
+            for (int i = 0; i < dnsesJson.size(); i++) {
+                JsonNode dnsJson = dnsesJson.get(i);
+                if (dnsJson != null) {
+                    dnses.add(IpAddress.valueOf(dnsJson.asText()));
+                }
+            }
+        }
+        networkBuilder.dnses(dnses);
+
+        log.trace("Network is {}", networkBuilder.build().toString());
+
+        return networkBuilder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortCodec.java
new file mode 100644
index 0000000..281855e
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortCodec.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.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.onlab.packet.MacAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtPort;
+import org.onosproject.kubevirtnetworking.api.KubevirtPort;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public final class KubevirtPortCodec extends JsonCodec<KubevirtPort> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String VM_NAME = "vmName";
+    private static final String NETWORK_ID = "networkId";
+    private static final String MAC_ADDRESS = "macAddress";
+    private static final String IP_ADDRESS = "ipAddress";
+    private static final String DEVICE_ID = "deviceId";
+    private static final String PORT_NUMBER = "portNumber";
+    private static final String SECURITY_GROUPS = "securityGroups";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtPort";
+
+    @Override
+    public ObjectNode encode(KubevirtPort port, CodecContext context) {
+        checkNotNull(port, "Kubevirt port cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(VM_NAME, port.vmName())
+                .put(NETWORK_ID, port.networkId())
+                .put(MAC_ADDRESS, port.macAddress().toString());
+
+        if (port.ipAddress() != null) {
+            result.put(IP_ADDRESS, port.ipAddress().toString());
+        }
+
+        if (port.deviceId() != null) {
+            result.put(DEVICE_ID, port.deviceId().toString());
+        }
+
+        if (port.portNumber() != null) {
+            result.put(PORT_NUMBER, port.portNumber().toString());
+        }
+
+        if (port.securityGroups() != null) {
+            ArrayNode sgIds = context.mapper().createArrayNode();
+            for (String sgId : port.securityGroups()) {
+                sgIds.add(sgId);
+            }
+            result.set(SECURITY_GROUPS, sgIds);
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtPort decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String vmName = nullIsIllegal(json.get(VM_NAME).asText(),
+                VM_NAME + MISSING_MESSAGE);
+
+        String networkId = nullIsIllegal(json.get(NETWORK_ID).asText(),
+                NETWORK_ID + MISSING_MESSAGE);
+
+        String macAddress = nullIsIllegal(json.get(MAC_ADDRESS).asText(),
+                MAC_ADDRESS + MISSING_MESSAGE);
+
+        KubevirtPort.Builder builder = DefaultKubevirtPort.builder()
+                .vmName(vmName)
+                .networkId(networkId)
+                .macAddress(MacAddress.valueOf(macAddress));
+
+        JsonNode ipAddressJson = json.get(IP_ADDRESS);
+        if (ipAddressJson != null) {
+            final IpAddress ipAddress = IpAddress.valueOf(ipAddressJson.asText());
+            builder.ipAddress(ipAddress);
+        }
+
+        JsonNode deviceIdJson = json.get(DEVICE_ID);
+        if (deviceIdJson != null) {
+            final DeviceId deviceId = DeviceId.deviceId(deviceIdJson.asText());
+            builder.deviceId(deviceId);
+        }
+
+        JsonNode portNumberJson = json.get(PORT_NUMBER);
+        if (portNumberJson != null) {
+            final PortNumber portNumber = PortNumber.portNumber(portNumberJson.asText());
+            builder.portNumber(portNumber);
+        }
+
+        log.trace("Port is {}", builder.build().toString());
+
+        return builder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodec.java
new file mode 100644
index 0000000..5ca4ab6
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodec.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableMap;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtPeerRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Map;
+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;
+
+/**
+ * Kubevirt router codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtRouterCodec extends JsonCodec<KubevirtRouter> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String NAME = "name";
+    private static final String DESCRIPTION = "description";
+    private static final String ENABLE_SNAT = "enableSnat";
+    private static final String INTERNAL = "internal";
+    private static final String EXTERNAL = "external";
+    private static final String PEER_ROUTER = "peerRouter";
+    private static final String IP_ADDRESS = "ip";
+    private static final String MAC_ADDRESS = "mac";
+    private static final String NETWORK = "network";
+    private static final String GATEWAY = "gateway";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtRouter";
+
+    @Override
+    public ObjectNode encode(KubevirtRouter router, CodecContext context) {
+        checkNotNull(router, "Kubevirt router cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(NAME, router.name())
+                .put(ENABLE_SNAT, router.enableSnat())
+                .put(MAC_ADDRESS, router.mac().toString());
+
+        if (router.description() != null) {
+            result.put(DESCRIPTION, router.description());
+        }
+
+        if (router.internal() != null && !router.internal().isEmpty()) {
+            ArrayNode internal = context.mapper().createArrayNode();
+            router.internal().forEach(internal::add);
+
+            result.set(INTERNAL, internal);
+        }
+
+        if (router.external() != null && !router.external().isEmpty()) {
+            ArrayNode external = context.mapper().createArrayNode();
+            router.external().forEach((k, v) -> {
+                ObjectNode item = context.mapper().createObjectNode();
+                item.put(IP_ADDRESS, k);
+                item.put(NETWORK, v);
+                external.add(item);
+            });
+            result.set(EXTERNAL, external);
+        }
+
+        if (router.peerRouter() != null) {
+            ObjectNode peerRouter = context.mapper().createObjectNode();
+            peerRouter.put(IP_ADDRESS, router.peerRouter().ipAddress().toString());
+
+            if (router.peerRouter().macAddress() != null) {
+                peerRouter.put(MAC_ADDRESS, router.peerRouter().macAddress().toString());
+            }
+
+            result.set(PEER_ROUTER, peerRouter);
+        }
+
+        if (router.electedGateway() != null) {
+            result.put(GATEWAY, router.electedGateway());
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtRouter decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String name = nullIsIllegal(json.get(NAME).asText(),
+                NAME + MISSING_MESSAGE);
+
+        String vrouterMac = nullIsIllegal(json.get(MAC_ADDRESS).asText(),
+                MAC_ADDRESS + MISSING_MESSAGE);
+
+        KubevirtRouter.Builder builder = DefaultKubevirtRouter.builder()
+                .name(name)
+                .mac(MacAddress.valueOf(vrouterMac));
+
+        JsonNode descriptionJson = json.get(DESCRIPTION);
+        if (descriptionJson != null) {
+            builder.description(descriptionJson.asText());
+        }
+
+        JsonNode enableSnatJson = json.get(ENABLE_SNAT);
+        if (enableSnatJson != null) {
+            builder.enableSnat(enableSnatJson.asBoolean());
+        }
+        JsonNode electedGwJson = json.get(GATEWAY);
+        if (electedGwJson != null) {
+            builder.electedGateway(electedGwJson.asText());
+        }
+
+        ArrayNode internalJson = (ArrayNode) json.get(INTERNAL);
+        Set<String> internal = new HashSet<>();
+        if (internalJson != null) {
+            for (int i = 0; i < internalJson.size(); i++) {
+                internal.add(internalJson.get(i).asText());
+            }
+            builder.internal(internal);
+        }
+
+        ObjectNode externalJson = (ObjectNode) json.get(EXTERNAL);
+        if (externalJson != null) {
+            Map<String, String> external = ImmutableMap.of(
+                    externalJson.get(IP_ADDRESS).asText(),
+                    externalJson.get(NETWORK).asText());
+            builder.external(external);
+        }
+
+        ObjectNode peerRouterJson = (ObjectNode) json.get(PEER_ROUTER);
+        if (peerRouterJson != null) {
+            JsonNode ipJson = peerRouterJson.get(IP_ADDRESS);
+            JsonNode macJson = peerRouterJson.get(MAC_ADDRESS);
+
+            if (ipJson != null && macJson != null) {
+                IpAddress ip = IpAddress.valueOf(ipJson.asText());
+                MacAddress mac = MacAddress.valueOf(macJson.asText());
+                KubevirtPeerRouter peer = new KubevirtPeerRouter(ip, mac);
+                builder.peerRouter(peer);
+            }
+
+            // if mac address is not specified, we will not add mac address to peer router
+            if (ipJson != null && macJson == null) {
+                IpAddress ip = IpAddress.valueOf(ipJson.asText());
+                KubevirtPeerRouter peer = new KubevirtPeerRouter(ip, null);
+                builder.peerRouter(peer);
+            }
+        }
+
+        return builder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupCodec.java
new file mode 100644
index 0000000..fe290f9
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupCodec.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtSecurityGroup;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroup;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroupRule;
+import org.slf4j.Logger;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubevirt security group codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtSecurityGroupCodec extends JsonCodec<KubevirtSecurityGroup> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String ID = "id";
+    private static final String NAME = "name";
+    private static final String DESCRIPTION = "description";
+    private static final String RULES = "rules";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtSecurityGroup";
+
+    @Override
+    public ObjectNode encode(KubevirtSecurityGroup sg, CodecContext context) {
+        checkNotNull(sg, "Kubevirt Security Group cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(ID, sg.id())
+                .put(NAME, sg.name());
+
+        if (sg.description() != null) {
+            result.put(DESCRIPTION, sg.description());
+        }
+
+        if (sg.rules() != null && !sg.rules().isEmpty()) {
+            ArrayNode rules = context.mapper().createArrayNode();
+            sg.rules().forEach(rule -> {
+                ObjectNode ruleJson = context.codec(
+                        KubevirtSecurityGroupRule.class).encode(rule, context);
+                rules.add(ruleJson);
+            });
+            result.set(RULES, rules);
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtSecurityGroup decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String id = nullIsIllegal(json.get(ID).asText(), ID + MISSING_MESSAGE);
+        String name = nullIsIllegal(json.get(NAME).asText(), NAME + MISSING_MESSAGE);
+
+        KubevirtSecurityGroup.Builder builder = DefaultKubevirtSecurityGroup.builder()
+                .id(id)
+                .name(name);
+
+        JsonNode description = json.get(DESCRIPTION);
+        if (description != null) {
+            builder.description(description.asText());
+        }
+
+        JsonNode rulesJson = json.get(RULES);
+        if (rulesJson != null) {
+            Set<KubevirtSecurityGroupRule> rules = new HashSet<>();
+            IntStream.range(0, rulesJson.size())
+                    .forEach(i -> {
+                        ObjectNode ruleJson = get(rulesJson, i);
+                        KubevirtSecurityGroupRule rule = context.codec(
+                                KubevirtSecurityGroupRule.class).decode(ruleJson, context);
+                        rules.add(rule);
+                    });
+            builder.rules(rules);
+        }
+
+        return builder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupRuleCodec.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupRuleCodec.java
new file mode 100644
index 0000000..b63b555
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupRuleCodec.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtSecurityGroupRule;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroupRule;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.nullIsIllegal;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Kubevirt security group rule codec used for serializing and de-serializing JSON string.
+ */
+public final class KubevirtSecurityGroupRuleCodec extends JsonCodec<KubevirtSecurityGroupRule> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String ID = "id";
+    private static final String SECURITY_GROUP_ID = "securityGroupId";
+    private static final String DIRECTION = "direction";
+    private static final String ETHER_TYPE = "etherType";
+    private static final String PORT_RANGE_MAX = "portRangeMax";
+    private static final String PORT_RANGE_MIN = "portRangeMin";
+    private static final String PROTOCOL = "protocol";
+    private static final String REMOTE_IP_PREFIX = "remoteIpPrefix";
+    private static final String REMOTE_GROUP_ID = "remoteGroupId";
+
+    private static final String MISSING_MESSAGE = " is required in KubevirtSecurityGroupRule";
+
+    @Override
+    public ObjectNode encode(KubevirtSecurityGroupRule sgRule, CodecContext context) {
+        checkNotNull(sgRule, "Kubevirt security group rule cannot be null");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(ID, sgRule.id())
+                .put(SECURITY_GROUP_ID, sgRule.securityGroupId())
+                .put(DIRECTION, sgRule.direction());
+
+        if (sgRule.etherType() != null) {
+            result.put(ETHER_TYPE, sgRule.etherType());
+        }
+
+        if (sgRule.portRangeMax() != null) {
+            result.put(PORT_RANGE_MAX, sgRule.portRangeMax());
+        }
+
+        if (sgRule.portRangeMin() != null) {
+            result.put(PORT_RANGE_MIN, sgRule.portRangeMin());
+        }
+
+        if (sgRule.protocol() != null) {
+            result.put(PROTOCOL, sgRule.protocol());
+        }
+
+        if (sgRule.remoteIpPrefix() != null) {
+            result.put(REMOTE_IP_PREFIX, sgRule.remoteIpPrefix().toString());
+        }
+
+        if (sgRule.remoteGroupId() != null) {
+            result.put(REMOTE_GROUP_ID, sgRule.remoteGroupId());
+        }
+
+        return result;
+    }
+
+    @Override
+    public KubevirtSecurityGroupRule decode(ObjectNode json, CodecContext context) {
+        if (json == null || !json.isObject()) {
+            return null;
+        }
+
+        String id = nullIsIllegal(json.get(ID).asText(), ID + MISSING_MESSAGE);
+        String securityGroupId = nullIsIllegal(json.get(SECURITY_GROUP_ID).asText(),
+                SECURITY_GROUP_ID + MISSING_MESSAGE);
+        String direction = nullIsIllegal(json.get(DIRECTION).asText(),
+                DIRECTION + MISSING_MESSAGE);
+
+        KubevirtSecurityGroupRule.Builder builder = DefaultKubevirtSecurityGroupRule.builder()
+                .id(id)
+                .securityGroupId(securityGroupId)
+                .direction(direction);
+
+        JsonNode etherType = json.get(ETHER_TYPE);
+        if (etherType != null) {
+            builder.etherType(etherType.asText());
+        }
+
+        JsonNode portRangeMax = json.get(PORT_RANGE_MAX);
+        if (portRangeMax != null) {
+            builder.portRangeMax(portRangeMax.asInt());
+        }
+
+        JsonNode portRangeMin = json.get(PORT_RANGE_MIN);
+        if (portRangeMin != null) {
+            builder.portRangeMin(portRangeMin.asInt());
+        }
+
+        JsonNode protocol = json.get(PROTOCOL);
+        if (protocol != null) {
+            builder.protocol(protocol.asText());
+        }
+
+        JsonNode remoteIpPrefix = json.get(REMOTE_IP_PREFIX);
+        if (remoteIpPrefix != null) {
+            builder.remoteIpPrefix(IpPrefix.valueOf(remoteIpPrefix.asText()));
+        }
+
+        JsonNode remoteGroupId = json.get(REMOTE_GROUP_ID);
+        if (remoteGroupId != null) {
+            builder.remoteGroupId(remoteGroupId.asText());
+        }
+
+        return builder.build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/package-info.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/package-info.java
new file mode 100644
index 0000000..91d8c00
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/codec/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementations of the codec broker and KubeVirt networking entity JSON codecs.
+ */
+package org.onosproject.kubevirtnetworking.codec;
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtFloatingIpsWebResource.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtFloatingIpsWebResource.java
new file mode 100644
index 0000000..ec41622
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtFloatingIpsWebResource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.web;
+
+import org.onosproject.kubevirtnetworking.api.KubevirtFloatingIp;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Handles REST API call for kubevirt floating IPs.
+ */
+@Path("floating-ip")
+public class KubevirtFloatingIpsWebResource extends AbstractWebResource {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String FLOATING_IPS = "floating-ips";
+
+    /**
+     * Returns set of all floating IPs.
+     *
+     * @return 200 OK with set of all floating IPs
+     * @onos.rsModel KubevirtFloatingIps
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getFloatingIps() {
+        KubevirtRouterService service = get(KubevirtRouterService.class);
+        final Iterable<KubevirtFloatingIp> fips = service.floatingIps();
+        return ok(encodeArray(KubevirtFloatingIp.class, FLOATING_IPS, fips)).build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtLoadBalancerWebResource.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtLoadBalancerWebResource.java
new file mode 100644
index 0000000..8fa61a2
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtLoadBalancerWebResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.web;
+
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Handles REST API call for kubevirt load balancer.
+ */
+@Path("loadbalancer")
+public class KubevirtLoadBalancerWebResource extends AbstractWebResource {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+    private static final String LOAD_BALANCERS = "loadBalancers";
+
+    /**
+     * Returns set of all load balancers.
+     *
+     * @return 200 OK with set of all load balancers
+     * @onos.rsModel KubevirtLoadBalancers
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getLoadBalancers() {
+        KubevirtLoadBalancerService service = get(KubevirtLoadBalancerService.class);
+        final Iterable<KubevirtLoadBalancer> lbs = service.loadBalancers();
+        return ok(encodeArray(KubevirtLoadBalancer.class, LOAD_BALANCERS, lbs)).build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtMm5WebResource.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtMm5WebResource.java
new file mode 100644
index 0000000..ef8f707
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtMm5WebResource.java
@@ -0,0 +1,485 @@
+/*
+ * Copyright 2022-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.kubevirtnetworking.web;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.kubevirtnetworking.api.KubevirtFloatingIp;
+import org.onosproject.kubevirtnetworking.api.KubevirtInstance;
+import org.onosproject.kubevirtnetworking.api.KubevirtInstanceService;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerService;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
+import org.onosproject.kubevirtnode.api.KubevirtNode;
+import org.onosproject.kubevirtnode.api.KubevirtNodeService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.io.InputStream;
+
+/**
+ * Handles REST API for communication with MEC Orchestrator.
+ */
+@Path("api/mm5/v1")
+public class KubevirtMm5WebResource extends AbstractWebResource {
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String RECEIVED_REQUEST = "Received %s request";
+    private static final String QUERY_STATUS_LEG = "LEG status query";
+    private static final String QUERY_STATUS_VR = "VR status query";
+
+    private static final String QUERY_GET_NETWORK = "get network query";
+    private static final String QUERY_CREATE_NETWORK = "create network query";
+    private static final String QUERY_UPDATE_NETWORK = "update network query";
+    private static final String QUERY_DELETE_NETWORK = "delete network query";
+
+    private static final String QUERY_GET_VR = "get virtual router query";
+    private static final String QUERY_CREATE_VR = "create virtual router query";
+    private static final String QUERY_UPDATE_VR = "update virtual router query";
+    private static final String QUERY_DELETE_VR = "delete virtual router query";
+
+    private static final String QUERY_GET_FIP = "get floating ip query";
+    private static final String QUERY_CREATE_FIP = "create floating ip query";
+    private static final String QUERY_UPDATE_FIP = "update floating ip query";
+    private static final String QUERY_DELETE_FIP = "delete floating ip query";
+
+    private static final String QUERY_GET_LB = "get LoadBalancer query";
+    private static final String QUERY_CREATE_LB = "create LoadBalancer query";
+    private static final String QUERY_UPDATE_LB = "update LoadBalancer query";
+    private static final String QUERY_DELETE_LB = "delete LoadBalancer query";
+
+    private static final String QUERY_GET_INSTANCE = "get instance query";
+    private static final String QUERY_CREATE_INSTANCE = "create instance query";
+    private static final String QUERY_UPDATE_INSTANCE = "update floating ip query";
+    private static final String QUERY_DELETE_INSTANCE = "delete floating ip query";
+
+    private static final String NODE_NAME = "nodeName";
+    private static final String STATE = "state";
+    private static final String ACTIVE = "ACTIVE";
+    private static final String INACTIVE = "INACTIVE";
+
+    private static final String NETWORKS = "networks";
+    private static final String INSTANCES = "instances";
+    private static final String VIRTUAL_ROUTERS = "virtualRouters";
+    private static final String LOAD_BALANCERS = "loadBalancers";
+    private static final String FLOATING_IPS = "floatingIps";
+
+    private static final String UP = "up";
+    private static final String DOWN = "down";
+    private static final String NONE = "none";
+
+    private static final String VR_NAME = "vrName";
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Obtains the status of the virtual router.
+     *
+     * @param vrName virtual router name
+     * @return the state of the virtual router in Json
+     */
+    @GET
+    @Path("status/vr/{vrName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response vrStatus(@PathParam(VR_NAME) String vrName) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_STATUS_VR));
+
+        KubevirtRouterService service = get(KubevirtRouterService.class);
+
+        KubevirtRouter router = service.routers().stream()
+                .filter(r -> r.name().equals(vrName))
+                .findAny().orElse(null);
+
+        if (router == null) {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        } else {
+            ObjectNode jsonResult = mapper().createObjectNode();
+
+            jsonResult.put(VR_NAME, router.name());
+
+            if (router.electedGateway() == null) {
+                jsonResult.put("merName", NONE);
+                jsonResult.put("status", DOWN);
+            } else {
+                jsonResult.put("merName", router.electedGateway());
+                jsonResult.put("status", UP);
+            }
+
+            jsonResult.put("timeUpdated",  System.currentTimeMillis());
+            return ok(jsonResult).build();
+        }
+    }
+
+    /**
+     * Obtains the state of the leg node.
+     *
+     * @param nodeName leg host name
+     * @return the state of the leg node in Json
+     */
+    @PUT
+    @Path("state/mer/{nodeName}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response legStatus(@PathParam("nodeName") String nodeName) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_STATUS_LEG));
+
+        KubevirtNodeService service = get(KubevirtNodeService.class);
+
+        ObjectNode jsonResult = mapper().createObjectNode();
+        jsonResult.put(NODE_NAME, nodeName);
+
+        boolean isActive = service.completeNodes().stream()
+                .anyMatch(node -> node.type().equals(KubevirtNode.Type.GATEWAY) &&
+                        node.hostname().equals(nodeName));
+        if (isActive) {
+            jsonResult.put(STATE, ACTIVE);
+        } else {
+            jsonResult.put(STATE, INACTIVE);
+        }
+        return ok(jsonResult).build();
+    }
+
+    /**
+     * Obtains the network information in Json Array.
+     *
+     * @return network information in Json
+     */
+    @GET
+    @Path("network")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getNetwork() {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_GET_NETWORK));
+
+        KubevirtNetworkService service = get(KubevirtNetworkService.class);
+        final Iterable<KubevirtNetwork> networks = service.networks();
+        return ok(encodeArray(KubevirtNetwork.class, NETWORKS, networks)).build();
+    }
+
+    /**
+     * Creates the kubevirt network with specified input stream.
+     *
+     * @param inputStream network Json input stream
+     * @return 200 OK if succeeded
+     */
+    @POST
+    @Path("network")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createNetwork(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_CREATE_NETWORK));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Updates the kubevirt network with the specified input stream.
+     *
+     * @param inputStream network Json input stream
+     * @return 200 OK if succeeded
+     */
+    @PUT
+    @Path("network/{networkName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateNetwork(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_UPDATE_NETWORK));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Deletes the kubevirt network with the specified input stream.
+     *
+     * @param inputStream network Json input stream
+     * @return 200 OK if succeeded
+     */
+    @DELETE
+    @Path("network/{networkName}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteNetwork(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_DELETE_NETWORK));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Obtains the instance information in Json Array.
+     *
+     * @return instance information in Json
+     */
+    @GET
+    @Path("instance")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getInstance() {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_GET_INSTANCE));
+
+        KubevirtInstanceService service = get(KubevirtInstanceService.class);
+        final Iterable<KubevirtInstance> instances = service.instances();
+        return ok(encodeArray(KubevirtInstance.class, INSTANCES, instances)).build();
+    }
+
+    /**
+     * Creates the instance with specified input stream.
+     *
+     * @param inputStream instance Json insput stream
+     * @return 200 OK if succeeded
+     */
+    @POST
+    @Path("instance")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createInstance(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_CREATE_INSTANCE));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Updates the instance with specified input stream.
+     *
+     * @param inputStream instance Json insput stream
+     * @return 200 OK if succeeded
+     */
+    @PUT
+    @Path("instance/{instanceName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateInstance(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_UPDATE_INSTANCE));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Deletes the instance with specified input stream.
+     *
+     * @param inputStream inputStream instance Json insput stream
+     * @return 200 OK if succeeded
+     */
+    @DELETE
+    @Path("instance/{instanceName}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteInstance(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_DELETE_INSTANCE));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Obtains the virtual router in Json array.
+     *
+     * @return virtual router information in Json
+     */
+    @GET
+    @Path("vr")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getVirtualRouter() {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_GET_VR));
+
+        KubevirtRouterService service = get(KubevirtRouterService.class);
+        final Iterable<KubevirtRouter> routers = service.routers();
+        return ok(encodeArray(KubevirtRouter.class, VIRTUAL_ROUTERS, routers)).build();
+    }
+
+    /**
+     * Creates the virtual router with specified input stream.
+     *
+     * @param inputStream virtual router Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @POST
+    @Path("vr")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createVirtualRouter(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_CREATE_VR));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Updates the virtual router with specified input stream.
+     *
+     * @param inputStream virtual router Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @PUT
+    @Path("vr/{vrName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateVirtualRouter(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_UPDATE_VR));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Deletes the virtual router with specified input stream.
+     *
+     * @param inputStream virtual router Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @DELETE
+    @Path("vr/{vrName}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteVirtualRouter(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_DELETE_VR));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Obtains the floating ip in Json array.
+     *
+     * @return floating ip information in Json
+     */
+    @GET
+    @Path("fip")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getFloatingIp() {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_GET_FIP));
+
+        KubevirtRouterService service = get(KubevirtRouterService.class);
+        final Iterable<KubevirtFloatingIp> fips = service.floatingIps();
+        return ok(encodeArray(KubevirtFloatingIp.class, FLOATING_IPS, fips)).build();
+    }
+
+    /**
+     * Creates the floating ip with specified input stream.
+     *
+     * @param inputStream floating ip Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @POST
+    @Path("fip")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createFloatingIp(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_CREATE_FIP));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Updates the floating ip with specified input stream.
+     *
+     * @param inputStream floating ip Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @PUT
+    @Path("fip/{fipName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateFloatingIp(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_UPDATE_FIP));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Deletes the floating ip with specified input stream.
+     *
+     * @param inputStream floating ip Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @DELETE
+    @Path("fip/{fipName}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteFloatingIp(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_DELETE_FIP));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Obtains the loadbalaner in Json array.
+     *
+     * @return loadbalancer information in Json
+     */
+    @GET
+    @Path("lb")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getLoadBalancer() {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_GET_LB));
+
+        KubevirtLoadBalancerService service = get(KubevirtLoadBalancerService.class);
+        final Iterable<KubevirtLoadBalancer> lbs = service.loadBalancers();
+        return ok(encodeArray(KubevirtLoadBalancer.class, LOAD_BALANCERS, lbs)).build();
+    }
+
+    /**
+     * Creates the loadbalander with specified input stream.
+     *
+     * @param inputStream loadbalancer Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @POST
+    @Path("lb")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createLoadBalancer(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_CREATE_LB));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Updates the loadbalander with specified input stream.
+     *
+     * @param inputStream loadbalancer Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @PUT
+    @Path("lb/{lbName}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateLoadBalancer(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_UPDATE_LB));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+
+    /**
+     * Deletes the loadbalander with specified input stream.
+     *
+     * @param inputStream loadbalancer Json inputstream
+     * @return 200 OK if succeeded
+     */
+    @DELETE
+    @Path("lb/{lbName}")
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteLoadBalancer(InputStream inputStream) {
+        log.trace(String.format(RECEIVED_REQUEST, QUERY_DELETE_LB));
+        //Just sends 200 OK for now.
+        return ok(mapper().createObjectNode()).build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkWebResource.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkWebResource.java
new file mode 100644
index 0000000..4e8f5df
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkWebResource.java
@@ -0,0 +1,193 @@
+/*
+ * 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.kubevirtnetworking.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetworkAdminService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import static org.onlab.util.Tools.nullIsNotFound;
+import static org.onlab.util.Tools.readTreeFromStream;
+
+/**
+ * Handles REST API call from CNI plugin.
+ */
+@Path("network")
+public class KubevirtNetworkWebResource extends AbstractWebResource {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String MESSAGE = "Received network %s request";
+    private static final String NETWORK_NOT_FOUND = "Network is not found for";
+    private static final String NETWORK_INVALID = "Invalid networkId in network update request";
+
+    private static final String RESULT = "result";
+
+    /**
+     * Creates a network from the JSON input stream.
+     *
+     * @param input network JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated network already exists
+     * @onos.rsModel KubevirtNetwork
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response createNetwork(InputStream input) {
+        log.trace(String.format(MESSAGE, "CREATE"));
+        KubevirtNetworkAdminService service = get(KubevirtNetworkAdminService.class);
+        URI location;
+
+        try {
+            ObjectNode jsonTree = readTreeFromStream(mapper(), input);
+            final KubevirtNetwork network =
+                    codec(KubevirtNetwork.class).decode(jsonTree, this);
+            service.createNetwork(network);
+            location = new URI(network.networkId());
+        } catch (IOException | URISyntaxException e) {
+            throw new IllegalArgumentException(e);
+        }
+
+        return Response.created(location).build();
+    }
+
+    /**
+     * Updates the network with the specified identifier.
+     *
+     * @param id    network identifier
+     * @param input network JSON input stream
+     * @return 200 OK with the updated network, 400 BAD_REQUEST if the requested
+     * network does not exist
+     * @onos.rsModel KubevirtNetwork
+     */
+    @PUT
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateNetwork(@PathParam("id") String id, InputStream input) {
+        log.trace(String.format(MESSAGE, "UPDATED"));
+        KubevirtNetworkAdminService service = get(KubevirtNetworkAdminService.class);
+
+        try {
+            ObjectNode jsonTree = readTreeFromStream(mapper(), input);
+            JsonNode specifiedNetworkId = jsonTree.get("networkId");
+
+            if (specifiedNetworkId != null && !specifiedNetworkId.asText().equals(id)) {
+                throw new IllegalArgumentException(NETWORK_INVALID);
+            }
+
+            final KubevirtNetwork network =
+                    codec(KubevirtNetwork.class).decode(jsonTree, this);
+            service.updateNetwork(network);
+        } catch (IOException e) {
+            throw new IllegalArgumentException(e);
+        }
+
+        return Response.ok().build();
+    }
+
+    /**
+     * Removes the network with the given id.
+     *
+     * @param id network identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the network does not exist
+     */
+    @DELETE
+    @Path("{id}")
+    public Response removeNetwork(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "DELETE " + id));
+        KubevirtNetworkAdminService service = get(KubevirtNetworkAdminService.class);
+
+        service.removeNetwork(id);
+        return Response.noContent().build();
+    }
+
+    /**
+     * Checks whether the network exists with given network id.
+     *
+     * @param id network identifier
+     * @return 200 OK with true/false result
+     */
+    @GET
+    @Path("exist/{id}")
+    public Response hasNetwork(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "QUERY " + id));
+
+        KubevirtNetworkAdminService service = get(KubevirtNetworkAdminService.class);
+
+        ObjectNode root = mapper().createObjectNode();
+        KubevirtNetwork network = service.network(id);
+
+        if (network == null) {
+            root.put(RESULT, false);
+        } else {
+            root.put(RESULT, true);
+        }
+
+        return Response.ok(root).build();
+    }
+
+    /**
+     * Returns set of all networks.
+     *
+     * @return 200 OK with set of all networks
+     * @onos.rsModel KubevirtNetworks
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getNetworks() {
+        KubevirtNetworkAdminService service = get(KubevirtNetworkAdminService.class);
+        final Iterable<KubevirtNetwork> networks = service.networks();
+        return ok(encodeArray(KubevirtNetwork.class, "networks", networks)).build();
+    }
+
+    /**
+     * Returns the network with the specified identifier.
+     *
+     * @param id network identifier
+     * @return 200 OK with a network, 404 not found
+     * @onos.rsModel KubevirtNetwork
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    @Path("{id}")
+    public Response getNetworkById(@PathParam("id") String id) {
+        KubevirtNetworkAdminService service = get(KubevirtNetworkAdminService.class);
+        final KubevirtNetwork network = nullIsNotFound(service.network(id),
+                NETWORK_NOT_FOUND + id);
+        return ok(codec(KubevirtNetwork.class).encode(network, this)).build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingCodecRegister.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingCodecRegister.java
new file mode 100644
index 0000000..0280c6a
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingCodecRegister.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.web;
+
+import org.onosproject.codec.CodecService;
+import org.onosproject.kubevirtnetworking.api.KubevirtFloatingIp;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerRule;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtPort;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroup;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroupRule;
+import org.onosproject.kubevirtnetworking.codec.KubevirtFloatingIpCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtHostRouteCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtIpPoolCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtLoadBalancerCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtLoadBalancerRuleCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtNetworkCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtPortCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtRouterCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtSecurityGroupCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtSecurityGroupRuleCodec;
+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 static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the JSON codec brokering service for KubevirtNetworking.
+ */
+@Component(immediate = true)
+public class KubevirtNetworkingCodecRegister {
+
+    private final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CodecService codecService;
+
+    @Activate
+    protected void activate() {
+
+        codecService.registerCodec(KubevirtHostRoute.class, new KubevirtHostRouteCodec());
+        codecService.registerCodec(KubevirtIpPool.class, new KubevirtIpPoolCodec());
+        codecService.registerCodec(KubevirtNetwork.class, new KubevirtNetworkCodec());
+        codecService.registerCodec(KubevirtPort.class, new KubevirtPortCodec());
+        codecService.registerCodec(KubevirtRouter.class, new KubevirtRouterCodec());
+        codecService.registerCodec(KubevirtFloatingIp.class, new KubevirtFloatingIpCodec());
+        codecService.registerCodec(KubevirtSecurityGroup.class, new KubevirtSecurityGroupCodec());
+        codecService.registerCodec(KubevirtSecurityGroupRule.class, new KubevirtSecurityGroupRuleCodec());
+        codecService.registerCodec(KubevirtLoadBalancer.class, new KubevirtLoadBalancerCodec());
+        codecService.registerCodec(KubevirtLoadBalancerRule.class, new KubevirtLoadBalancerRuleCodec());
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+
+        codecService.unregisterCodec(KubevirtHostRoute.class);
+        codecService.unregisterCodec(KubevirtIpPool.class);
+        codecService.unregisterCodec(KubevirtNetwork.class);
+        codecService.unregisterCodec(KubevirtPort.class);
+        codecService.unregisterCodec(KubevirtRouter.class);
+        codecService.unregisterCodec(KubevirtFloatingIp.class);
+        codecService.unregisterCodec(KubevirtSecurityGroup.class);
+        codecService.unregisterCodec(KubevirtSecurityGroupRule.class);
+        codecService.unregisterCodec(KubevirtLoadBalancer.class);
+        codecService.unregisterCodec(KubevirtLoadBalancerRule.class);
+
+        log.info("Stopped");
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingWebApplication.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingWebApplication.java
new file mode 100644
index 0000000..dc2afbc
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingWebApplication.java
@@ -0,0 +1,37 @@
+/*
+ * 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.kubevirtnetworking.web;
+
+import org.onlab.rest.AbstractWebApplication;
+
+import java.util.Set;
+
+/**
+ * KubeVirt networking REST APIs web application.
+ */
+public class KubevirtNetworkingWebApplication extends AbstractWebApplication {
+    @Override
+    public Set<Class<?>> getClasses() {
+        return getClasses(
+                KubevirtNetworkWebResource.class,
+                KubevirtRouterWebResource.class,
+                KubevirtFloatingIpsWebResource.class,
+                KubevirtSecurityGroupWebResource.class,
+                KubevirtLoadBalancerWebResource.class,
+                KubevirtMm5WebResource.class
+        );
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtRouterWebResource.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtRouterWebResource.java
new file mode 100644
index 0000000..8a40f51
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtRouterWebResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.web;
+
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Handles REST API call for kubevirt router.
+ */
+@Path("router")
+public class KubevirtRouterWebResource extends AbstractWebResource {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+    private static final String ROUTERS = "routers";
+
+    /**
+     * Returns set of all routers.
+     *
+     * @return 200 OK with set of all routers
+     * @onos.rsModel KubevirtRouters
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getRouters() {
+        KubevirtRouterService service = get(KubevirtRouterService.class);
+        final Iterable<KubevirtRouter> routers = service.routers();
+        return ok(encodeArray(KubevirtRouter.class, ROUTERS, routers)).build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtSecurityGroupWebResource.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtSecurityGroupWebResource.java
new file mode 100644
index 0000000..02acd87
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/KubevirtSecurityGroupWebResource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.web;
+
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroup;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroupService;
+import org.onosproject.rest.AbstractWebResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+
+/**
+ * Handles REST API call for kubevirt security group.
+ */
+@Path("security-group")
+public class KubevirtSecurityGroupWebResource extends AbstractWebResource {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+    private static final String SECURITY_GROUPS = "security-groups";
+
+    /**
+     * Returns set of all security groups.
+     *
+     * @return 200 OK with set of all security groups
+     * @onos.rsModel KubevirtSecurityGroups
+     */
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getSecurityGroups() {
+        KubevirtSecurityGroupService service = get(KubevirtSecurityGroupService.class);
+        final Iterable<KubevirtSecurityGroup> sgs = service.securityGroups();
+        return ok(encodeArray(KubevirtSecurityGroup.class, SECURITY_GROUPS, sgs)).build();
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/package-info.java b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/package-info.java
new file mode 100644
index 0000000..538bf6b
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/java/org/onosproject/kubevirtnetworking/web/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2022-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.
+ */
+
+/**
+ * KubeVirt networking web implementation.
+ */
+package org.onosproject.kubevirtnetworking.web;
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtFloatingIps.json b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtFloatingIps.json
new file mode 100644
index 0000000..07a45e2
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtFloatingIps.json
@@ -0,0 +1,60 @@
+{
+  "type": "object",
+  "title": "floatingips",
+  "required": [
+    "floatingips"
+  ],
+  "properties": {
+    "floatingips": {
+      "type": "array",
+      "xml": {
+        "name": "floatingips",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "description": "A floating IP object.",
+        "required": [
+          "id",
+          "routerName",
+          "networkName",
+          "floatingIp",
+          "podName",
+          "fixedIp"
+        ],
+        "properties": {
+          "id": {
+            "type": "string",
+            "example": "6a073105-c735-4e53-8d74-64191c6985fe",
+            "description": "The ID of floating IP."
+          },
+          "routerName": {
+            "type": "string",
+            "example": "router-1",
+            "description": "Name of router."
+          },
+          "networkName": {
+            "type": "string",
+            "example": "flat-1",
+            "description": "Name of network."
+          },
+          "floatingIp": {
+            "type": "string",
+            "example": "10.10.10.5",
+            "description": "Floating IP address."
+          },
+          "podName": {
+            "type": "string",
+            "example": "pod-1",
+            "description": "Name of POD associated with this floating IP"
+          },
+          "fixedIp": {
+            "type": "string",
+            "example": "20.20.20.5",
+            "description": "Fixed IP address."
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtLoadBalancers.json b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtLoadBalancers.json
new file mode 100644
index 0000000..f06aedf
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtLoadBalancers.json
@@ -0,0 +1,94 @@
+{
+  "type": "object",
+  "title": "loadBalancers",
+  "required": [
+    "loadBalancers"
+  ],
+  "properties": {
+    "loadBalancers": {
+      "type": "array",
+      "xml": {
+        "name": "loadBalancers",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "description": "A load balancer object.",
+        "required": [
+          "name",
+          "description",
+          "networkId",
+          "vip",
+          "members",
+          "rules"
+        ],
+        "properties": {
+          "name": {
+            "type": "string",
+            "example": "lb-1",
+            "description": "The name of load balancer."
+          },
+          "description": {
+            "type": "string",
+            "example": "Example load balancer",
+            "description": "The description of load balancer."
+          },
+          "networkId": {
+            "type": "string",
+            "example": "net-1",
+            "description": "The name of network where the load balancer is attached to."
+          },
+          "vip": {
+            "type": "string",
+            "example": "10.10.10.10",
+            "description": "The virtual IP address of the load balancer."
+          },
+          "members": {
+            "type": "array",
+            "xml": {
+              "name": "members",
+              "wrapped": true
+            },
+            "items": {
+              "type": "string",
+              "example": "10.10.10.11",
+              "description": "IP address of member instance."
+            }
+          },
+          "rules": {
+            "type": "array",
+            "description": "A list of load balancer rule objects.",
+            "items": {
+              "type": "object",
+              "description": "A load balancer rule object.",
+              "required": [
+                "portRangeMax",
+                "portRangeMin",
+                "protocol"
+              ],
+              "properties": {
+                "portRangeMax": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 80,
+                  "description": "The maximum port number in the range that is matched by the rule."
+                },
+                "portRangeMin": {
+                  "type": "integer",
+                  "format": "int32",
+                  "example": 80,
+                  "description": "The minimum port number in the range that is matched by the rule."
+                },
+                "protocol": {
+                  "type": "string",
+                  "example": "tcp",
+                  "description": "The IP protocol can be represented by a string, an integer, or null."
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtNetwork.json b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtNetwork.json
new file mode 100644
index 0000000..5653ae8
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtNetwork.json
@@ -0,0 +1,50 @@
+{
+  "type": "object",
+  "description": "A network object.",
+  "required": [
+    "networkId",
+    "type",
+    "name",
+    "segmentId",
+    "gatewayIp",
+    "cidr",
+    "mtu"
+  ],
+  "properties": {
+    "networkId": {
+      "type": "string",
+      "example": "sona-network",
+      "description": "The ID of the attached network."
+    },
+    "type": {
+      "type": "string",
+      "example": "VXLAN",
+      "description": "Type of kubernetes network."
+    },
+    "name": {
+      "type": "string",
+      "example": "sona-network",
+      "description": "The name of network."
+    },
+    "segmentId": {
+      "type": "string",
+      "example": "1",
+      "description": "Segment ID of tenant network."
+    },
+    "gatewayIp": {
+      "type": "string",
+      "example": "10.10.10.1",
+      "description": "The IP address of the gateway."
+    },
+    "cidr": {
+      "type": "string",
+      "example": "10.10.10.0/24",
+      "description": "The CIDR of this network."
+    },
+    "mtu": {
+      "type": "integer",
+      "example": 1500,
+      "description": "The Maximum Transmission Unit of this network."
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtNetworks.json b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtNetworks.json
new file mode 100644
index 0000000..5ce66f0
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtNetworks.json
@@ -0,0 +1,66 @@
+{
+  "type": "object",
+  "title": "networks",
+  "required": [
+    "networks"
+  ],
+  "properties": {
+    "networks": {
+      "type": "array",
+      "xml": {
+        "name": "networks",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "description": "A network object.",
+        "required": [
+          "networkId",
+          "type",
+          "name",
+          "segmentId",
+          "gatewayIp",
+          "cidr",
+          "mtu"
+        ],
+        "properties": {
+          "networkId": {
+            "type": "string",
+            "example": "sona-network",
+            "description": "The ID of the attached network."
+          },
+          "type": {
+            "type": "string",
+            "example": "VXLAN",
+            "description": "Type of kubernetes network."
+          },
+          "name": {
+            "type": "string",
+            "example": "sona-network",
+            "description": "The name of network."
+          },
+          "segmentId": {
+            "type": "string",
+            "example": "1",
+            "description": "Segment ID of tenant network."
+          },
+          "gatewayIp": {
+            "type": "string",
+            "example": "10.10.10.1",
+            "description": "The IP address of the gateway."
+          },
+          "cidr": {
+            "type": "string",
+            "example": "10.10.10.0/24",
+            "description": "The CIDR of this network."
+          },
+          "mtu": {
+            "type": "integer",
+            "example": 1500,
+            "description": "The Maximum Transmission Unit of this network."
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtRouters.json b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtRouters.json
new file mode 100644
index 0000000..835dfea
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtRouters.json
@@ -0,0 +1,97 @@
+{
+  "type": "object",
+  "title": "routers",
+  "required": [
+    "routers"
+  ],
+  "properties": {
+    "routers": {
+      "type": "array",
+      "xml": {
+        "name": "routers",
+        "wrapped": true
+      },
+      "items": {
+        "type": "object",
+        "description": "A router object.",
+        "required": [
+          "name",
+          "enableSnat",
+          "mac",
+          "internal",
+          "external",
+          "peerRouter"
+        ],
+        "properties": {
+          "name": {
+            "type": "string",
+            "example": "router-1",
+            "description": "The name of router."
+          },
+          "enableSnat": {
+            "type": "boolean",
+            "example": "true",
+            "description": "Enable SNAT flag."
+          },
+          "mac": {
+            "type": "string",
+            "example": "11:22:33:44:55:66",
+            "description": "The MAC address of virtual router."
+          },
+          "internal": {
+            "type": "array",
+            "xml": {
+              "name": "internal",
+              "wrapped": true
+            },
+            "items": {
+              "type": "string",
+              "example": "vxlan-1",
+              "description": "name of internal network"
+            }
+          },
+          "external": {
+            "type": "object",
+            "description": "external networks",
+            "required": [
+              "ip",
+              "name"
+            ],
+            "properties": {
+              "ipaddress": {
+                "type": "string",
+                "example": "10.10.10.5",
+                "description": "Router IP address of the external network"
+              },
+              "name": {
+                "type": "string",
+                "example": "flat",
+                "description": "Name of the external network"
+              }
+            }
+          },
+          "peerRouter": {
+            "type": "object",
+            "description": "peer router",
+            "required": [
+              "ip",
+              "mac"
+            ],
+            "properties": {
+              "ipaddress": {
+                "type": "string",
+                "example": "10.10.10.1",
+                "description": "IP address of the peer router"
+              },
+              "mac": {
+                "type": "string",
+                "example": "11:22:33:44:55:66",
+                "description": "MAC address of the peer router"
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtSecurityGroups.json b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtSecurityGroups.json
new file mode 100644
index 0000000..bd724c9
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/resources/definitions/KubevirtSecurityGroups.json
@@ -0,0 +1,103 @@
+{
+  "type": "object",
+  "description": "A security_group object.",
+  "required": [
+    "securityGroup"
+  ],
+  "properties": {
+    "securityGroup": {
+      "type": "object",
+      "required": [
+        "id",
+        "description",
+        "name",
+        "securityGroupRules"
+      ],
+      "properties": {
+        "id": {
+          "type": "string",
+          "example": "2076db17-a522-4506-91de-c6dd8e837028",
+          "description": "The ID of the security group."
+        },
+        "description": {
+          "type": "string",
+          "example": "security group for webservers",
+          "description": "A human-readable description for the resource."
+        },
+        "name": {
+          "type": "string",
+          "example": "new-webservers",
+          "description": "Human-readable name of the resource."
+        },
+        "securityGroupRules": {
+          "type": "array",
+          "description": "A list of security_group_rule objects.",
+          "items": {
+            "type": "object",
+            "description": "A security group rule object.",
+            "required": [
+              "id",
+              "securityGroupId",
+              "direction",
+              "etherType",
+              "portRangeMax",
+              "portRangeMin",
+              "protocol",
+              "remoteIpPrefix",
+              "remoteGroupId"
+            ],
+            "properties": {
+              "id": {
+                "type": "string",
+                "example": "2bc0accf-312e-429a-956e-e4407625eb62",
+                "description": "The ID of this security group rule."
+              },
+              "securityGroupId": {
+                "type": "string",
+                "example": "a7734e61-b545-452d-a3cd-0189cbd9747a",
+                "description": "The security group ID to associate with this security group rule."
+              },
+              "direction": {
+                "type": "string",
+                "example": "ingress",
+                "description": "Ingress or egress, which is the direction in which the metering rule is applied."
+              },
+              "etherType": {
+                "type": "string",
+                "example": "IPv4",
+                "description": "Must be IPv4 or IPv6, and addresses represented in CIDR must match the ingress or egress rules."
+              },
+              "portRangeMax": {
+                "type": "integer",
+                "format": "int32",
+                "example": 80,
+                "description": "The maximum port number in the range that is matched by the security group rule."
+              },
+              "portRangeMin": {
+                "type": "integer",
+                "format": "int32",
+                "example": 80,
+                "description": "The minimum port number in the range that is matched by the security group rule."
+              },
+              "protocol": {
+                "type": "string",
+                "example": "tcp",
+                "description": "The IP protocol can be represented by a string, an integer, or null."
+              },
+              "remoteIpPrefix": {
+                "type": "string",
+                "example": "",
+                "description": "The remote IP prefix to associate with this metering rule packet."
+              },
+              "remoteGroupId": {
+                "type": "string",
+                "example": "85cc3048-abc3-43cc-89b3-377341426ac5",
+                "description": "The remote group UUID to associate with this security group rule."
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/main/webapp/WEB-INF/web.xml b/apps/kubevirt-networking/web/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 0000000..0b4876a
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
+         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+         id="ONOS" version="2.5">
+    <display-name>KubeVirt Networking REST API v1.0</display-name>
+
+    <security-constraint>
+        <web-resource-collection>
+            <web-resource-name>Secured</web-resource-name>
+            <url-pattern>/*</url-pattern>
+        </web-resource-collection>
+        <auth-constraint>
+            <role-name>admin</role-name>
+            <role-name>viewer</role-name>
+        </auth-constraint>
+    </security-constraint>
+
+    <security-role>
+        <role-name>admin</role-name>
+        <role-name>viewer</role-name>
+    </security-role>
+
+    <login-config>
+        <auth-method>BASIC</auth-method>
+        <realm-name>karaf</realm-name>
+    </login-config>
+
+    <servlet>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
+        <init-param>
+            <param-name>javax.ws.rs.Application</param-name>
+            <param-value>org.onosproject.kubevirtnetworking.web.KubevirtNetworkingWebApplication</param-value>
+        </init-param>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>JAX-RS Service</servlet-name>
+        <url-pattern>/*</url-pattern>
+    </servlet-mapping>
+</web-app>
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpCodecTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpCodecTest.java
new file mode 100644
index 0000000..216f3e0
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpCodecTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.MockCodecContext;
+import org.onosproject.core.CoreService;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtFloatingIp;
+import org.onosproject.kubevirtnetworking.api.KubevirtFloatingIp;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+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.kubevirtnetworking.codec.KubevirtFloatingIpJsonMatcher.matchesKubevirtFloatingIp;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Unit tests for KubevirtFloatingIp codec.
+ */
+public final class KubevirtFloatingIpCodecTest {
+
+    MockCodecContext context;
+
+    JsonCodec<KubevirtFloatingIp> kubevirtFloatingIpCodec;
+
+    final CoreService mockCoreService = createMock(CoreService.class);
+    private static final String REST_APP_ID = "org.onosproject.rest";
+
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        kubevirtFloatingIpCodec = new KubevirtFloatingIpCodec();
+
+        assertThat(kubevirtFloatingIpCodec, notNullValue());
+        expect(mockCoreService.registerApplication(REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Tests the kubevirt floating IP encoding.
+     */
+    @Test
+    public void testKubevirtFloatingIpEncode() {
+        KubevirtFloatingIp floatingIp = DefaultKubevirtFloatingIp.builder()
+                .id("fip-id")
+                .routerName("router-1")
+                .networkName("flat-1")
+                .floatingIp(IpAddress.valueOf("10.10.10.10"))
+                .podName("pod-1")
+                .vmName("vm-1")
+                .fixedIp(IpAddress.valueOf("20.20.20.20"))
+                .build();
+
+        ObjectNode floatingIpJson = kubevirtFloatingIpCodec.encode(floatingIp, context);
+        assertThat(floatingIpJson, matchesKubevirtFloatingIp(floatingIp));
+    }
+
+    @Test
+    public void testKubevirtFloatingIpDecode() throws IOException {
+        KubevirtFloatingIp floatingIp = getKubevirtFloatingIp("KubevirtFloatingIp.json");
+
+        assertEquals("fip-1", floatingIp.id());
+        assertEquals("router-1", floatingIp.routerName());
+        assertEquals("flat-1", floatingIp.networkName());
+        assertEquals("10.10.10.10", floatingIp.floatingIp().toString());
+        assertEquals("pod-1", floatingIp.podName());
+        assertEquals("vm-1", floatingIp.vmName());
+        assertEquals("20.20.20.20", floatingIp.fixedIp().toString());
+    }
+
+    private KubevirtFloatingIp getKubevirtFloatingIp(String resourceName) throws IOException {
+        InputStream jsonStream = KubevirtFloatingIpCodecTest.class.getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        MatcherAssert.assertThat(json, notNullValue());
+        KubevirtFloatingIp fip = kubevirtFloatingIpCodec.decode((ObjectNode) json, context);
+        assertThat(fip, notNullValue());
+        return fip;
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpJsonMatcher.java
new file mode 100644
index 0000000..41d8cfe
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIpJsonMatcher.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.kubevirtnetworking.api.KubevirtFloatingIp;
+
+/**
+ * Hamcrest matcher for kubevirt router interface.
+ */
+public final class KubevirtFloatingIpJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtFloatingIp floatingIp;
+    private static final String ID = "id";
+    private static final String ROUTER_NAME = "routerName";
+    private static final String NETWORK_NAME = "networkName";
+    private static final String POD_NAME = "podName";
+    private static final String VM_NAME = "vmName";
+    private static final String FLOATING_IP = "floatingIp";
+    private static final String FIXED_IP = "fixedIp";
+
+    private KubevirtFloatingIpJsonMatcher(KubevirtFloatingIp floatingIp) {
+        this.floatingIp = floatingIp;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+        // check ID
+        String jsonId = jsonNode.get(ID).asText();
+        String id = floatingIp.id();
+        if (!jsonId.equals(id)) {
+            description.appendText("ID was " + jsonId);
+            return false;
+        }
+
+        // check router name
+        String jsonRouterName = jsonNode.get(ROUTER_NAME).asText();
+        String routerName = floatingIp.routerName();
+        if (!jsonRouterName.equals(routerName)) {
+            description.appendText("Router name was " + jsonRouterName);
+            return false;
+        }
+
+        // check network name
+        String jsonNetworkName = jsonNode.get(NETWORK_NAME).asText();
+        String networkName = floatingIp.networkName();
+        if (!jsonNetworkName.equals(networkName)) {
+            description.appendText("Network name was " + jsonNetworkName);
+            return false;
+        }
+
+        // check floating IP
+        String jsonFip = jsonNode.get(FLOATING_IP).asText();
+        String fip = floatingIp.floatingIp().toString();
+        if (!jsonFip.equals(fip)) {
+            description.appendText("Floating IP was " + jsonFip);
+            return false;
+        }
+
+        // check POD name
+        JsonNode jsonPodName = jsonNode.get(POD_NAME);
+        if (jsonPodName != null) {
+            if (!floatingIp.podName().equals(jsonPodName.asText())) {
+                description.appendText("POD name was " + jsonPodName);
+                return false;
+            }
+        }
+
+        JsonNode jsonVmName = jsonNode.get(VM_NAME);
+        if (jsonVmName != null) {
+            if (!floatingIp.vmName().equals(jsonVmName.asText())) {
+                description.appendText("VM name was " + jsonVmName);
+                return false;
+            }
+        }
+
+
+        // check fixed IP
+        JsonNode jsonFixedIp = jsonNode.get(FIXED_IP);
+        if (jsonFixedIp != null) {
+            if (!floatingIp.fixedIp().toString().equals(jsonFixedIp.asText())) {
+                description.appendText("Fixed IP was " + jsonFixedIp);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(floatingIp.toString());
+    }
+
+    /**
+     * Factory to allocate a kubevirt floating IP matcher.
+     *
+     * @param fip kubevirt floating IP object we are looking for
+     * @return matcher
+     */
+    public static KubevirtFloatingIpJsonMatcher matchesKubevirtFloatingIp(KubevirtFloatingIp fip) {
+        return new KubevirtFloatingIpJsonMatcher(fip);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtHostRouteJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtHostRouteJsonMatcher.java
new file mode 100644
index 0000000..4603980
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtHostRouteJsonMatcher.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+
+/**
+ * Hamcrest matcher for kubevirt host route interface.
+ */
+public final class KubevirtHostRouteJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtHostRoute hostRoute;
+
+    private static final String DESTINATION = "destination";
+    private static final String NEXTHOP = "nexthop";
+
+    private KubevirtHostRouteJsonMatcher(KubevirtHostRoute hostRoute) {
+        this.hostRoute = hostRoute;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check destination
+        String jsonDestination = jsonNode.get(DESTINATION).asText();
+        String destination = hostRoute.destination().toString();
+        if (!jsonDestination.equals(destination)) {
+            description.appendText("destination was " + jsonDestination);
+            return false;
+        }
+
+        // check nexthop
+        JsonNode jsonNexthop = jsonNode.get(NEXTHOP);
+        if (jsonNexthop != null) {
+            String nexthop = hostRoute.nexthop().toString();
+            if (!jsonNexthop.asText().equals(nexthop)) {
+                description.appendText("nexthop was " + jsonNexthop);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(description.toString());
+    }
+
+    /**
+     * Factory to allocate an kubevirt host route matcher.
+     *
+     * @param route kubevirt host route object we are looking for
+     * @return matcher
+     */
+    public static KubevirtHostRouteJsonMatcher matchesKubevirtHostRoute(KubevirtHostRoute route) {
+        return new KubevirtHostRouteJsonMatcher(route);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtIpPoolJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtIpPoolJsonMatcher.java
new file mode 100644
index 0000000..6217335
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtIpPoolJsonMatcher.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+
+/**
+ * Hamcrest matcher for kubevirt IP pool interface.
+ */
+public final class KubevirtIpPoolJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtIpPool ipPool;
+    private static final String START = "start";
+    private static final String END = "end";
+
+    private KubevirtIpPoolJsonMatcher(KubevirtIpPool ipPool) {
+        this.ipPool = ipPool;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check start
+        String jsonStart = jsonNode.get(START).asText();
+        String start = ipPool.start().toString();
+        if (!jsonStart.equals(start)) {
+            description.appendText("start was " + jsonStart);
+            return false;
+        }
+
+        // check end
+        String jsonEnd = jsonNode.get(END).asText();
+        String end = ipPool.end().toString();
+        if (!jsonEnd.equals(end)) {
+            description.appendText("end was " + jsonEnd);
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(ipPool.toString());
+    }
+
+    /**
+     * Factory to allocate an kubevirt IP pool matcher.
+     *
+     * @param ipPool kubevirt IP pool object we are looking for
+     * @return matcher
+     */
+    public static KubevirtIpPoolJsonMatcher matchesKubevirtIpPool(KubevirtIpPool ipPool) {
+        return new KubevirtIpPoolJsonMatcher(ipPool);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerCodecTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerCodecTest.java
new file mode 100644
index 0000000..cd86402
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerCodecTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.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.hamcrest.MatcherAssert;
+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.kubevirtnetworking.api.DefaultKubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtLoadBalancerRule;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerRule;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+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.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onosproject.kubevirtnetworking.codec.KubevirtLoadBalancerJsonMatcher.matchesKubevirtLoadBalancer;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Unit tests for KubevirtLoadBalancer codec.
+ */
+public final class KubevirtLoadBalancerCodecTest {
+
+    MockCodecContext context;
+
+    JsonCodec<KubevirtLoadBalancer> kubevirtLoadBalancerCodec;
+    JsonCodec<KubevirtLoadBalancerRule> kubevirtLoadBalancerRuleCodec;
+
+    private static final KubevirtLoadBalancerRule RULE1 = DefaultKubevirtLoadBalancerRule.builder()
+            .protocol("tcp")
+            .portRangeMax(8000)
+            .portRangeMin(7000)
+            .build();
+    private static final KubevirtLoadBalancerRule RULE2 = DefaultKubevirtLoadBalancerRule.builder()
+            .protocol("udp")
+            .portRangeMax(9000)
+            .portRangeMin(8000)
+            .build();
+
+    private static final KubevirtLoadBalancerRule RULE3 = DefaultKubevirtLoadBalancerRule.builder()
+            .protocol("icmp")
+            .build();
+
+    final CoreService mockCoreService = createMock(CoreService.class);
+    private static final String REST_APP_ID = "org.onosproject.rest";
+
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        kubevirtLoadBalancerCodec = new KubevirtLoadBalancerCodec();
+        kubevirtLoadBalancerRuleCodec = new KubevirtLoadBalancerRuleCodec();
+
+        assertThat(kubevirtLoadBalancerCodec, notNullValue());
+        assertThat(kubevirtLoadBalancerRuleCodec, notNullValue());
+
+        expect(mockCoreService.registerApplication(REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Tests the kubevirt load balancer encoding.
+     */
+    @Test
+    public void testKubevirtLoadBalancerEncode() {
+        KubevirtLoadBalancer lb = DefaultKubevirtLoadBalancer.builder()
+                .name("lb-1")
+                .networkId("net-1")
+                .vip(IpAddress.valueOf("10.10.10.10"))
+                .members(ImmutableSet.of(IpAddress.valueOf("10.10.10.11"),
+                        IpAddress.valueOf("10.10.10.12")))
+                .rules(ImmutableSet.of(RULE1, RULE2, RULE3))
+                .description("network load balancer")
+                .build();
+
+        ObjectNode lbJson = kubevirtLoadBalancerCodec.encode(lb, context);
+        assertThat(lbJson, matchesKubevirtLoadBalancer(lb));
+    }
+
+    /**
+     * Tests the kubevirt load balancer decoding.
+     */
+    @Test
+    public void testKubevirtLoadBalancerDecode() throws IOException {
+        KubevirtLoadBalancer lb = getKubevirtLoadBalancer("KubevirtLoadBalancer.json");
+
+        assertThat(lb.name(), is("lb-1"));
+        assertThat(lb.description(), is("Example Load Balancer"));
+        assertThat(lb.networkId(), is("net-1"));
+        assertThat(lb.vip(), is(IpAddress.valueOf("10.10.10.10")));
+
+        Set<IpAddress> expectedMembers = ImmutableSet.of(IpAddress.valueOf("10.10.10.11"),
+                IpAddress.valueOf("10.10.10.12"));
+        Set<IpAddress> realMembers = lb.members();
+        assertThat(true, is(expectedMembers.containsAll(realMembers)));
+        assertThat(true, is(realMembers.containsAll(expectedMembers)));
+
+        Set<KubevirtLoadBalancerRule> expectedRules = ImmutableSet.of(RULE1, RULE2, RULE3);
+        Set<KubevirtLoadBalancerRule> realRules = lb.rules();
+        assertThat(true, is(expectedRules.containsAll(realRules)));
+        assertThat(true, is(realRules.containsAll(expectedRules)));
+    }
+
+    private KubevirtLoadBalancer getKubevirtLoadBalancer(String resourceName) throws IOException {
+        InputStream jsonStream = KubevirtLoadBalancerCodecTest.class.getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        MatcherAssert.assertThat(json, notNullValue());
+        KubevirtLoadBalancer lb = kubevirtLoadBalancerCodec.decode((ObjectNode) json, context);
+        assertThat(lb, notNullValue());
+        return lb;
+    }
+
+    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
+        public <T> JsonCodec<T> codec(Class<T> entityClass) {
+            if (entityClass == KubevirtLoadBalancerRule.class) {
+                return (JsonCodec<T>) kubevirtLoadBalancerRuleCodec;
+            }
+            return manager.getCodec(entityClass);
+        }
+
+        @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/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerJsonMatcher.java
new file mode 100644
index 0000000..9122bd0
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerJsonMatcher.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onlab.packet.IpAddress;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancer;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerRule;
+
+/**
+ * Hamcrest matcher for load balancer.
+ */
+public final class KubevirtLoadBalancerJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private static final String NAME = "name";
+    private static final String DESCRIPTION = "description";
+    private static final String VIP = "vip";
+    private static final String NETWORK_ID = "networkId";
+    private static final String MEMBERS = "members";
+    private static final String RULES = "rules";
+
+    private final KubevirtLoadBalancer lb;
+
+    private KubevirtLoadBalancerJsonMatcher(KubevirtLoadBalancer lb) {
+        this.lb = lb;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check name
+        String jsonName = jsonNode.get(NAME).asText();
+        String name = lb.name();
+        if (!jsonName.equals(name)) {
+            description.appendText("Name was " + jsonName);
+            return false;
+        }
+
+        // check description
+        JsonNode jsonDescription = jsonNode.get(DESCRIPTION);
+        if (jsonDescription != null) {
+            String myDescription = lb.description();
+            if (!jsonDescription.asText().equals(myDescription)) {
+                description.appendText("Description was " + jsonDescription);
+                return false;
+            }
+        }
+
+        // check VIP
+        String jsonVip = jsonNode.get(VIP).asText();
+        String vip = lb.vip().toString();
+        if (!jsonVip.equals(vip)) {
+            description.appendText("VIP was " + jsonVip);
+            return false;
+        }
+
+        // check network ID
+        String jsonNetworkId = jsonNode.get(NETWORK_ID).asText();
+        String networkId = lb.networkId();
+        if (!jsonNetworkId.equals(networkId)) {
+            description.appendText("NetworkId was " + jsonNetworkId);
+            return false;
+        }
+
+        // check members
+        ArrayNode jsonMembers = (ArrayNode) jsonNode.get(MEMBERS);
+        if (jsonMembers != null) {
+            // check size of members array
+            if (jsonMembers.size() != lb.members().size()) {
+                description.appendText("Members was " + jsonMembers.size());
+                return false;
+            }
+
+            for (JsonNode jsonMember : jsonMembers) {
+                IpAddress member = IpAddress.valueOf(jsonMember.asText());
+                if (!lb.members().contains(member)) {
+                    description.appendText("Member not found " + jsonMember.toString());
+                    return false;
+                }
+            }
+        }
+
+        ArrayNode jsonRules = (ArrayNode) jsonNode.get(RULES);
+        if (jsonRules != null) {
+            // check size of rules array
+            if (jsonRules.size() != lb.rules().size()) {
+                description.appendText("Rules was " + jsonRules.size());
+                return false;
+            }
+
+            // check rules
+            for (KubevirtLoadBalancerRule rule : lb.rules()) {
+                boolean ruleFound = false;
+                for (int ruleIndex = 0; ruleIndex < jsonRules.size(); ruleIndex++) {
+                    KubevirtLoadBalancerRuleJsonMatcher ruleMatcher =
+                            KubevirtLoadBalancerRuleJsonMatcher
+                                    .matchesKubevirtLoadBalancerRule(rule);
+                    if (ruleMatcher.matches(jsonRules.get(ruleIndex))) {
+                        ruleFound = true;
+                        break;
+                    }
+                }
+
+                if (!ruleFound) {
+                    description.appendText("Rule not found " + rule.toString());
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(lb.toString());
+    }
+
+    /**
+     * Factory to allocate a kubevirt load balancer matcher.
+     *
+     * @param lb kubevirt load balancer object we are looking for
+     * @return matcher
+     */
+    public static KubevirtLoadBalancerJsonMatcher
+        matchesKubevirtLoadBalancer(KubevirtLoadBalancer lb) {
+        return new KubevirtLoadBalancerJsonMatcher(lb);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerRuleJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerRuleJsonMatcher.java
new file mode 100644
index 0000000..404d1ee
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancerRuleJsonMatcher.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.kubevirtnetworking.api.KubevirtLoadBalancerRule;
+
+/**
+ * Hamcrest matcher for kubevirt load balancer.
+ */
+public final class KubevirtLoadBalancerRuleJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtLoadBalancerRule rule;
+
+    private static final String PROTOCOL = "protocol";
+    private static final String PORT_RANGE_MAX = "portRangeMax";
+    private static final String PORT_RANGE_MIN = "portRangeMin";
+
+    private KubevirtLoadBalancerRuleJsonMatcher(KubevirtLoadBalancerRule rule) {
+        this.rule = rule;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check protocol
+        JsonNode jsonProtocol = jsonNode.get(PROTOCOL);
+        if (jsonProtocol != null) {
+            String protocol = rule.protocol();
+            if (!jsonProtocol.asText().equals(protocol)) {
+                description.appendText("Protocol was " + jsonProtocol);
+                return false;
+            }
+        }
+
+        // check port range max
+        JsonNode jsonPortRangeMax = jsonNode.get(PORT_RANGE_MAX);
+        if (jsonPortRangeMax != null) {
+            int portRangeMax = rule.portRangeMax();
+            if (portRangeMax != jsonPortRangeMax.asInt()) {
+                description.appendText("PortRangeMax was " + jsonPortRangeMax);
+                return false;
+            }
+        }
+
+        // check port range min
+        JsonNode jsonPortRangeMin = jsonNode.get(PORT_RANGE_MIN);
+        if (jsonPortRangeMin != null) {
+            int portRangeMin = rule.portRangeMin();
+            if (portRangeMin != jsonPortRangeMin.asInt()) {
+                description.appendText("PortRangeMin was " + jsonPortRangeMin);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(rule.toString());
+    }
+
+    /**
+     * Factory to allocate an kubevirt load balancer rule matcher.
+     *
+     * @param rule kubevirt load balancer rule object we are looking for
+     * @return matcher
+     */
+    public static KubevirtLoadBalancerRuleJsonMatcher
+        matchesKubevirtLoadBalancerRule(KubevirtLoadBalancerRule rule) {
+        return new KubevirtLoadBalancerRuleJsonMatcher(rule);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkCodecTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkCodecTest.java
new file mode 100644
index 0000000..667ca4b
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkCodecTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.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.hamcrest.MatcherAssert;
+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.kubevirtnetworking.api.DefaultKubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+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.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.onosproject.kubevirtnetworking.codec.KubevirtNetworkJsonMatcher.matchesKubevirtNetwork;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Unit tests for KubevirtNetwork codec.
+ */
+public final class KubevirtNetworkCodecTest {
+
+    MockCodecContext context;
+
+    JsonCodec<KubevirtNetwork> kubevirtNetworkCodec;
+    JsonCodec<KubevirtHostRoute> kubevirtHostRouteCodec;
+    JsonCodec<KubevirtIpPool> kubevirtIpPoolCodec;
+
+    final CoreService mockCoreService = createMock(CoreService.class);
+    private static final String REST_APP_ID = "org.onosproject.rest";
+
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        kubevirtNetworkCodec = new KubevirtNetworkCodec();
+        kubevirtHostRouteCodec = new KubevirtHostRouteCodec();
+        kubevirtIpPoolCodec = new KubevirtIpPoolCodec();
+
+        assertThat(kubevirtNetworkCodec, notNullValue());
+        assertThat(kubevirtHostRouteCodec, notNullValue());
+        assertThat(kubevirtIpPoolCodec, notNullValue());
+
+        expect(mockCoreService.registerApplication(REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Tests the kubevirt network encoding.
+     */
+    @Test
+    public void testKubevirtNetworkEncode() {
+        KubevirtHostRoute hostRoute1 = new KubevirtHostRoute(IpPrefix.valueOf("10.10.10.0/24"),
+                IpAddress.valueOf("20.20.20.1"));
+        KubevirtHostRoute hostRoute2 = new KubevirtHostRoute(IpPrefix.valueOf("20.20.20.0/24"),
+                IpAddress.valueOf("10.10.10.1"));
+
+        KubevirtIpPool ipPool = new KubevirtIpPool(IpAddress.valueOf("10.10.10.100"),
+                IpAddress.valueOf("10.10.10.200"));
+
+        KubevirtNetwork network = DefaultKubevirtNetwork.builder()
+                .networkId("net-1")
+                .name("net-1")
+                .type(KubevirtNetwork.Type.FLAT)
+                .gatewayIp(IpAddress.valueOf("10.10.10.1"))
+                .defaultRoute(true)
+                .mtu(1500)
+                .cidr("10.10.10.0/24")
+                .hostRoutes(ImmutableSet.of(hostRoute1, hostRoute2))
+                .ipPool(ipPool)
+                .dnses(ImmutableSet.of(IpAddress.valueOf("8.8.8.8")))
+                .build();
+
+        ObjectNode networkJson = kubevirtNetworkCodec.encode(network, context);
+        assertThat(networkJson, matchesKubevirtNetwork(network));
+    }
+
+    /**
+     * Tests the kubevirt network decoding.
+     *
+     * @throws IOException io exception
+     */
+    @Test
+    public void testKubevirtNetworkDecode() throws IOException {
+        KubevirtNetwork network = getKubevirtNetwork("KubevirtNetwork.json");
+
+        assertThat(network.networkId(), is("network-1"));
+        assertThat(network.name(), is("network-1"));
+        assertThat(network.type().name(), is("FLAT"));
+        assertThat(network.cidr(), is("10.10.0.0/24"));
+        assertThat(network.gatewayIp().toString(), is("10.10.0.1"));
+        assertThat(network.defaultRoute(), is(true));
+        assertThat(network.ipPool().start().toString(), is("10.10.10.100"));
+        assertThat(network.ipPool().end().toString(), is("10.10.10.200"));
+        assertThat(network.dnses().size(), is(1));
+        KubevirtHostRoute route = network.hostRoutes().stream().findFirst().orElse(null);
+        assertThat(route, is(new KubevirtHostRoute(IpPrefix.valueOf("10.10.10.0/24"),
+                IpAddress.valueOf("10.10.10.1"))));
+    }
+
+    private KubevirtNetwork getKubevirtNetwork(String resourceName) throws IOException {
+        InputStream jsonStream = KubevirtNetworkCodecTest.class.getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        MatcherAssert.assertThat(json, notNullValue());
+        KubevirtNetwork network = kubevirtNetworkCodec.decode((ObjectNode) json, context);
+        assertThat(network, notNullValue());
+        return network;
+    }
+
+    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
+        public <T> JsonCodec<T> codec(Class<T> entityClass) {
+            if (entityClass == KubevirtHostRoute.class) {
+                return (JsonCodec<T>) kubevirtHostRouteCodec;
+            }
+
+            if (entityClass == KubevirtIpPool.class) {
+                return (JsonCodec<T>) kubevirtIpPoolCodec;
+            }
+
+            return manager.getCodec(entityClass);
+        }
+
+        @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/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkJsonArrayMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkJsonArrayMatcher.java
new file mode 100644
index 0000000..6701c7b
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkJsonArrayMatcher.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+
+public final class KubevirtNetworkJsonArrayMatcher extends TypeSafeMatcher<JsonArray> {
+
+    private final KubevirtNetwork network;
+    private String reason = "";
+
+    public KubevirtNetworkJsonArrayMatcher(KubevirtNetwork network) {
+        this.network = network;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonArray json) {
+        boolean networkFound = false;
+        for (int jsonNetworkIndex = 0; jsonNetworkIndex < json.size(); jsonNetworkIndex++) {
+            final JsonObject jsonNode = json.get(jsonNetworkIndex).asObject();
+
+            final String networkId = network.networkId();
+            final String jsonNetworkId = jsonNode.get("networkId").asString();
+            if (jsonNetworkId.equals(networkId)) {
+                networkFound = true;
+            }
+        }
+
+        if (!networkFound) {
+            reason = "Network with networkId " + network.networkId() + " not found";
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(reason);
+    }
+
+    /**
+     * Factory to allocate a network array matcher.
+     *
+     * @param network network object we are looking for
+     * @return matcher
+     */
+    public static KubevirtNetworkJsonArrayMatcher hasNetwork(KubevirtNetwork network) {
+        return new KubevirtNetworkJsonArrayMatcher(network);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkJsonMatcher.java
new file mode 100644
index 0000000..7cbc587
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtNetworkJsonMatcher.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onlab.packet.IpAddress;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+
+/**
+ * Hamcrest matcher for kubevirt network.
+ */
+public final class KubevirtNetworkJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtNetwork network;
+    private static final String NETWORK_ID = "networkId";
+    private static final String TYPE = "type";
+    private static final String NAME = "name";
+    private static final String MTU = "mtu";
+    private static final String SEGMENT_ID = "segmentId";
+    private static final String GATEWAY_IP = "gatewayIp";
+    private static final String DEFAULT_ROUTE = "defaultRoute";
+    private static final String CIDR = "cidr";
+    private static final String HOST_ROUTES = "hostRoutes";
+    private static final String IP_POOL = "ipPool";
+    private static final String DNSES = "dnses";
+
+    private KubevirtNetworkJsonMatcher(KubevirtNetwork network) {
+        this.network = network;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check network ID
+        String jsonNetworkId = jsonNode.get(NETWORK_ID).asText();
+        String networkId = network.networkId();
+        if (!jsonNetworkId.equals(networkId)) {
+            description.appendText("network ID was " + jsonNetworkId);
+            return false;
+        }
+
+        // check type
+        String jsonType = jsonNode.get(TYPE).asText();
+        String type = network.type().name();
+        if (!jsonType.equals(type)) {
+            description.appendText("network type was " + jsonType);
+            return false;
+        }
+
+        // check name
+        String jsonName = jsonNode.get(NAME).asText();
+        String name = network.name();
+        if (!jsonName.equals(name)) {
+            description.appendText("network name was " + jsonName);
+            return false;
+        }
+
+        // check MTU
+        int jsonMtu = jsonNode.get(MTU).asInt();
+        int mtu = network.mtu();
+        if (jsonMtu != mtu) {
+            description.appendText("network MTU was " + jsonMtu);
+            return false;
+        }
+
+        // check gateway IP
+        String jsonGatewayIp = jsonNode.get(GATEWAY_IP).asText();
+        String gatewayIp = network.gatewayIp().toString();
+        if (!jsonGatewayIp.equals(gatewayIp)) {
+            description.appendText("gateway IP was " + jsonGatewayIp);
+            return false;
+        }
+
+        // check default route
+        boolean jsonDefaultRoute = jsonNode.get(DEFAULT_ROUTE).asBoolean();
+        boolean defaultRoute = network.defaultRoute();
+        if (jsonDefaultRoute != defaultRoute) {
+            description.appendText("Default route was " + jsonDefaultRoute);
+            return false;
+        }
+
+        // check CIDR
+        String jsonCidr = jsonNode.get(CIDR).asText();
+        String cidr = network.cidr();
+        if (!jsonCidr.equals(cidr)) {
+            description.appendText("CIDR was " + jsonCidr);
+            return false;
+        }
+
+        // check segment ID
+        JsonNode jsonSegmentId = jsonNode.get(SEGMENT_ID);
+        if (jsonSegmentId != null) {
+            String segmentId = network.segmentId();
+            if (!jsonSegmentId.asText().equals(segmentId)) {
+                description.appendText("segment ID was " + jsonSegmentId.asText());
+                return false;
+            }
+        }
+
+        // check ip pool
+        JsonNode jsonIpPool = jsonNode.get(IP_POOL);
+        if (jsonIpPool != null) {
+            KubevirtIpPool ipPool = network.ipPool();
+            KubevirtIpPoolJsonMatcher ipPoolMatcher =
+                    KubevirtIpPoolJsonMatcher.matchesKubevirtIpPool(ipPool);
+            if (ipPoolMatcher.matches(jsonIpPool)) {
+                return true;
+            } else {
+                description.appendText("IP pool was " + jsonIpPool.toString());
+                return false;
+            }
+        }
+
+        // check host routes
+        JsonNode jsonHostRoutes = jsonNode.get(HOST_ROUTES);
+        if (jsonHostRoutes != null) {
+            if (jsonHostRoutes.size() != network.hostRoutes().size()) {
+                description.appendText("host routes size was " + jsonHostRoutes.size());
+                return false;
+            }
+
+            for (KubevirtHostRoute hostRoute : network.hostRoutes()) {
+                boolean routeFound = false;
+                for (int routeIndex = 0; routeIndex < jsonHostRoutes.size(); routeIndex++) {
+                    KubevirtHostRouteJsonMatcher routeMatcher =
+                            KubevirtHostRouteJsonMatcher.matchesKubevirtHostRoute(hostRoute);
+                    if (routeMatcher.matches(jsonHostRoutes.get(routeIndex))) {
+                        routeFound = true;
+                        break;
+                    }
+                }
+
+                if (!routeFound) {
+                    description.appendText("Host route not found " + hostRoute.toString());
+                    return false;
+                }
+            }
+        }
+
+        // check dnses
+        JsonNode jsonDnses = jsonNode.get(DNSES);
+        if (jsonDnses != null) {
+            if (jsonDnses.size() != network.dnses().size()) {
+                description.appendText("DNSes size was " + jsonDnses.size());
+                return false;
+            }
+
+
+            for (IpAddress dns : network.dnses()) {
+                boolean dnsFound = false;
+                for (int dnsIndex = 0; dnsIndex < jsonDnses.size(); dnsIndex++) {
+                    String jsonDns = jsonDnses.get(dnsIndex).asText();
+                    if (jsonDns.equals(dns.toString())) {
+                        dnsFound = true;
+                        break;
+                    }
+                }
+
+                if (!dnsFound) {
+                    description.appendText("DNS not found " + dns.toString());
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(network.toString());
+    }
+
+    /**
+     * Factory to allocate an kubevirt network matcher.
+     *
+     * @param network kubevirt network object we are looking for
+     * @return matcher
+     */
+    public static KubevirtNetworkJsonMatcher
+                    matchesKubevirtNetwork(KubevirtNetwork network) {
+        return new KubevirtNetworkJsonMatcher(network);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortCodecTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortCodecTest.java
new file mode 100644
index 0000000..5c21a28
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortCodecTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.codec.CodecContext;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.core.CoreService;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtPort;
+import org.onosproject.kubevirtnetworking.api.KubevirtPort;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import java.util.HashMap;
+import java.util.Map;
+
+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.kubevirtnetworking.codec.KubevirtPortJsonMatcher.matchesKubevirtPort;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Unit tests for KubevirtPort codec.
+ */
+public final class KubevirtPortCodecTest {
+
+    MockCodecContext context;
+
+    JsonCodec<KubevirtPort> kubevirtPortCodec;
+
+    final CoreService mockCoreService = createMock(CoreService.class);
+    private static final String REST_APP_ID = "org.onosproject.rest";
+
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        kubevirtPortCodec = new KubevirtPortCodec();
+
+        assertThat(kubevirtPortCodec, notNullValue());
+
+        expect(mockCoreService.registerApplication(REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Tests the kubevirt port encoding.
+     */
+    @Test
+    public void testKubevirtPortEncode() {
+        KubevirtPort port = DefaultKubevirtPort.builder()
+                .vmName("test-vm-1")
+                .networkId("net-1")
+                .macAddress(MacAddress.valueOf("11:22:33:44:55:66"))
+                .ipAddress(IpAddress.valueOf("10.10.10.100"))
+                .deviceId(DeviceId.deviceId("of:foo"))
+                .portNumber(PortNumber.portNumber("1"))
+                .build();
+        ObjectNode portJson = kubevirtPortCodec.encode(port, context);
+        assertThat(portJson, matchesKubevirtPort(port));
+    }
+
+    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
+        public <T> JsonCodec<T> codec(Class<T> entityClass) {
+            return manager.getCodec(entityClass);
+        }
+
+        @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/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortJsonMatcher.java
new file mode 100644
index 0000000..1fc6207
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtPortJsonMatcher.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onlab.packet.IpAddress;
+import org.onosproject.kubevirtnetworking.api.KubevirtPort;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Hamcrest matcher for kubevirt port.
+ */
+public final class KubevirtPortJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtPort port;
+
+    private static final String VM_NAME = "vmName";
+    private static final String NETWORK_ID = "networkId";
+    private static final String MAC_ADDRESS = "macAddress";
+    private static final String IP_ADDRESS = "ipAddress";
+    private static final String DEVICE_ID = "deviceId";
+    private static final String PORT_NUMBER = "portNumber";
+
+    private KubevirtPortJsonMatcher(KubevirtPort port) {
+        this.port = port;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check VM name
+        String jsonVmName = jsonNode.get(VM_NAME).asText();
+        String vmName = port.vmName();
+        if (!jsonVmName.equals(vmName)) {
+            description.appendText("VM name was " + jsonVmName);
+            return false;
+        }
+
+        // check network ID
+        String jsonNetworkId = jsonNode.get(NETWORK_ID).asText();
+        String networkId = port.networkId();
+        if (!jsonNetworkId.equals(networkId)) {
+            description.appendText("network ID was " + jsonNetworkId);
+            return false;
+        }
+
+        // check MAC address
+        String jsonMacAddress = jsonNode.get(MAC_ADDRESS).asText();
+        String macAddress = port.macAddress().toString();
+        if (!jsonMacAddress.equals(macAddress)) {
+            description.appendText("MAC address was " + jsonMacAddress);
+            return false;
+        }
+
+        // check IP address
+        JsonNode jsonIpAddress = jsonNode.get(IP_ADDRESS);
+        if (jsonIpAddress != null) {
+            IpAddress ipAddress = port.ipAddress();
+            if (!jsonIpAddress.asText().equals(ipAddress.toString())) {
+                description.appendText("IP address was " + jsonIpAddress.asText());
+                return false;
+            }
+        }
+
+        // check device ID
+        JsonNode jsonDeviceId = jsonNode.get(DEVICE_ID);
+        if (jsonDeviceId != null) {
+            DeviceId deviceId = port.deviceId();
+            if (!jsonDeviceId.asText().equals(deviceId.toString())) {
+                description.appendText("Device ID was " + jsonDeviceId.asText());
+                return false;
+            }
+        }
+
+        // check port number
+        JsonNode jsonPortNumber = jsonNode.get(PORT_NUMBER);
+        if (jsonPortNumber != null) {
+            PortNumber portNUmber = port.portNumber();
+            if (!jsonPortNumber.asText().equals(portNUmber.toString())) {
+                description.appendText("Port number was " + jsonPortNumber.asText());
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(port.toString());
+    }
+    /**
+     * Factory to allocate an kubevirt port matcher.
+     *
+     * @param port kubevirt port object we are looking for
+     * @return matcher
+     */
+    public static KubevirtPortJsonMatcher matchesKubevirtPort(KubevirtPort port) {
+        return new KubevirtPortJsonMatcher(port);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodecTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodecTest.java
new file mode 100644
index 0000000..1ff8951
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterCodecTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.codec.impl.MockCodecContext;
+import org.onosproject.core.CoreService;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtPeerRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+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.kubevirtnetworking.codec.KubevirtRouterJsonMatcher.matchesKubevirtRouter;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Unit tests for KubevirtRouter codec.
+ */
+public final class KubevirtRouterCodecTest {
+
+    MockCodecContext context;
+
+    JsonCodec<KubevirtRouter> kubevirtRouterCodec;
+
+    final CoreService mockCoreService = createMock(CoreService.class);
+    private static final String REST_APP_ID = "org.onosproject.rest";
+
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        kubevirtRouterCodec = new KubevirtRouterCodec();
+
+        assertThat(kubevirtRouterCodec, notNullValue());
+        expect(mockCoreService.registerApplication(REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Tests the kubevirt router encoding.
+     */
+    @Test
+    public void testKubevirtRouterEncode() {
+        KubevirtPeerRouter peerRouter = new KubevirtPeerRouter(IpAddress.valueOf("10.10.10.10"),
+                MacAddress.valueOf("11:22:33:44:55:66"));
+
+        KubevirtRouter router = DefaultKubevirtRouter.builder()
+                .name("router-1")
+                .enableSnat(true)
+                .mac(MacAddress.valueOf("11:22:33:44:55:66"))
+                .description("router-1")
+                .internal(ImmutableSet.of("vlan-1"))
+                .external(ImmutableMap.of("10.10.10.20", "flat-1"))
+                .peerRouter(peerRouter)
+                .electedGateway("gatewayNode")
+                .build();
+
+        ObjectNode routerJson = kubevirtRouterCodec.encode(router, context);
+        assertThat(routerJson, matchesKubevirtRouter(router));
+    }
+
+    @Test
+    public void testKubevirtRouterDecode() throws IOException {
+        KubevirtRouter router = getKubevirtRouter("KubevirtRouter.json");
+
+        assertEquals("router-1", router.name());
+        assertEquals("Example Virtual Router", router.description());
+        assertTrue(router.enableSnat());
+        assertEquals("11:22:33:44:55:66", router.mac().toString());
+        assertEquals("192.168.10.5",
+                router.external().keySet().stream().findAny().orElse(null));
+        assertEquals("external-network", router.external().get("192.168.10.5"));
+        assertTrue(router.internal().contains("vxlan-network-1"));
+        assertTrue(router.internal().contains("vxlan-network-2"));
+        assertEquals("192.168.10.1", router.peerRouter().ipAddress().toString());
+        assertEquals("gatewayNode", router.electedGateway());
+    }
+
+    private KubevirtRouter getKubevirtRouter(String resourceName) throws IOException {
+        InputStream jsonStream = KubevirtRouterCodecTest.class.getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        MatcherAssert.assertThat(json, notNullValue());
+        KubevirtRouter router = kubevirtRouterCodec.decode((ObjectNode) json, context);
+        assertThat(router, notNullValue());
+        return router;
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterJsonMatcher.java
new file mode 100644
index 0000000..a855f42
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtRouterJsonMatcher.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+
+/**
+ * Hamcrest matcher for kubevirt router interface.
+ */
+public final class KubevirtRouterJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtRouter router;
+    private static final String NAME = "name";
+    private static final String DESCRIPTION = "description";
+    private static final String ENABLE_SNAT = "enableSnat";
+    private static final String INTERNAL = "internal";
+    private static final String EXTERNAL = "external";
+    private static final String PEER_ROUTER = "peerRouter";
+    private static final String IP_ADDRESS = "ip";
+    private static final String MAC_ADDRESS = "mac";
+    private static final String NETWORK = "network";
+
+    private KubevirtRouterJsonMatcher(KubevirtRouter router) {
+        this.router = router;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+
+        // check name
+        String jsonName = jsonNode.get(NAME).asText();
+        String name = router.name();
+        if (!jsonName.equals(name)) {
+            description.appendText("Name was " + jsonName);
+            return false;
+        }
+
+        // check description
+        JsonNode jsonDescription = jsonNode.get(DESCRIPTION);
+        if (jsonDescription != null) {
+            String myDescription = router.description();
+            if (!jsonDescription.asText().equals(myDescription)) {
+                description.appendText("Description was " + jsonDescription);
+                return false;
+            }
+        }
+
+        // check enable snat
+        JsonNode jsonEnableSnat = jsonNode.get(ENABLE_SNAT);
+        if (jsonEnableSnat != null) {
+            boolean enableSnat = router.enableSnat();
+            if (jsonEnableSnat.asBoolean() != enableSnat) {
+                description.appendText("EnableSNAT was " + jsonEnableSnat);
+                return false;
+            }
+        }
+
+        // check vrouter MAC
+        String jsonMac = jsonNode.get(MAC_ADDRESS).asText();
+        String mac = router.mac().toString();
+        if (!jsonMac.equals(mac)) {
+            description.appendText("MAC was " + jsonMac);
+            return false;
+        }
+
+        // check internal
+        JsonNode jsonInternal = jsonNode.get(INTERNAL);
+        if (jsonInternal != null) {
+            if (jsonInternal.size() != router.internal().size()) {
+                description.appendText("Internal networks size was " + jsonInternal.size());
+                return false;
+            }
+
+            for (int networkIndex = 0; networkIndex < jsonInternal.size(); networkIndex++) {
+                boolean networkFound = false;
+                String jsonNetwork = jsonInternal.get(networkIndex).asText();
+                if (router.internal().contains(jsonNetwork)) {
+                    networkFound = true;
+                }
+
+                if (!networkFound) {
+                    description.appendText("network not found " + jsonNetwork);
+                    return false;
+                }
+            }
+        }
+
+        // check external
+        ArrayNode jsonExternal = (ArrayNode) jsonNode.get(EXTERNAL);
+        if (jsonExternal != null) {
+            if (jsonExternal.size() != router.external().size()) {
+                description.appendText("External networks size was " + jsonExternal.size());
+                return false;
+            }
+
+            for (int itemIndex = 0; itemIndex < jsonExternal.size(); itemIndex++) {
+                boolean itemFound = false;
+                ObjectNode jsonItem = (ObjectNode) jsonExternal.get(itemIndex);
+                String jsonIp = jsonItem.get(IP_ADDRESS).asText();
+                String jsonNetwork = jsonItem.get(NETWORK).asText();
+
+                if (router.external().containsKey(jsonIp)) {
+                    if (router.external().get(jsonIp).equals(jsonNetwork)) {
+                        itemFound = true;
+                    }
+                }
+
+                if (!itemFound) {
+                    description.appendText("External not found " + jsonItem.toString());
+                    return false;
+                }
+            }
+        }
+
+        // check peer router
+        ObjectNode jsonPeerRouter = (ObjectNode) jsonNode.get(PEER_ROUTER);
+        if (jsonPeerRouter != null) {
+            JsonNode jsonIp = jsonPeerRouter.get(IP_ADDRESS);
+
+            if (jsonIp != null) {
+                if (!jsonIp.asText().equals(router.peerRouter().ipAddress().toString())) {
+                    description.appendText("Peer router IP was " + jsonIp);
+                    return false;
+                }
+            }
+
+            JsonNode jsonProuterMac = jsonPeerRouter.get(MAC_ADDRESS);
+
+            if (jsonProuterMac != null) {
+                if (!jsonProuterMac.asText().equals(router.peerRouter().macAddress().toString())) {
+                    description.appendText("Peer router MAC was " + jsonMac);
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(router.toString());
+    }
+
+    /**
+     * Factory to allocate a kubevirt router matcher.
+     *
+     * @param router kubevirt router object we are looking for
+     * @return matcher
+     */
+    public static KubevirtRouterJsonMatcher matchesKubevirtRouter(KubevirtRouter router) {
+        return new KubevirtRouterJsonMatcher(router);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupCodecTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupCodecTest.java
new file mode 100644
index 0000000..51bdeb5
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupCodecTest.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.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.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Test;
+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.kubevirtnetworking.api.DefaultKubevirtSecurityGroup;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtSecurityGroupRule;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroup;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroupRule;
+
+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.kubevirtnetworking.codec.KubevirtSecurityGroupJsonMatcher.matchesKubevirtSecurityGroup;
+import static org.onosproject.net.NetTestTools.APP_ID;
+
+/**
+ * Unit tests for KubevirtSecurityGroup codec.
+ */
+public final class KubevirtSecurityGroupCodecTest {
+
+    MockCodecContext context;
+
+    JsonCodec<KubevirtSecurityGroup> kubevirtSecurityGroupCodec;
+    JsonCodec<KubevirtSecurityGroupRule> kubevirtSecurityGroupRuleCodec;
+
+    final CoreService mockCoreService = createMock(CoreService.class);
+    private static final String REST_APP_ID = "org.onosproject.rest";
+
+    @Before
+    public void setUp() {
+        context = new MockCodecContext();
+        kubevirtSecurityGroupCodec = new KubevirtSecurityGroupCodec();
+        kubevirtSecurityGroupRuleCodec = new KubevirtSecurityGroupRuleCodec();
+
+        assertThat(kubevirtSecurityGroupCodec, notNullValue());
+        assertThat(kubevirtSecurityGroupRuleCodec, notNullValue());
+        expect(mockCoreService.registerApplication(REST_APP_ID))
+                .andReturn(APP_ID).anyTimes();
+        replay(mockCoreService);
+        context.registerService(CoreService.class, mockCoreService);
+    }
+
+    /**
+     * Tests the kubevirt security group encoding.
+     */
+    @Test
+    public void testKubevirtSecurityGroupEncode() {
+        KubevirtSecurityGroupRule rule = DefaultKubevirtSecurityGroupRule.builder()
+                .id("sgr-1")
+                .securityGroupId("sg-1")
+                .direction("ingress")
+                .etherType("IPv4")
+                .portRangeMin(0)
+                .portRangeMax(80)
+                .protocol("tcp")
+                .remoteIpPrefix(IpPrefix.valueOf("0.0.0.0/0"))
+                .remoteGroupId("g-1")
+                .build();
+
+        KubevirtSecurityGroup sg = DefaultKubevirtSecurityGroup.builder()
+                .id("sg-1")
+                .name("sg")
+                .description("example-sg")
+                .rules(ImmutableSet.of(rule))
+                .build();
+
+        ObjectNode sgJson = kubevirtSecurityGroupCodec.encode(sg, context);
+        assertThat(sgJson, matchesKubevirtSecurityGroup(sg));
+    }
+
+    /**
+     * Tests the kubevirt security group decoding.
+     */
+    @Test
+    public void testKubevirtSecurityGroupDecode() throws IOException {
+        KubevirtSecurityGroup sg = getKubevirtSecurityGroup("KubevirtSecurityGroup.json");
+        KubevirtSecurityGroupRule rule = sg.rules().stream().findAny().orElse(null);
+
+        assertEquals("sg-1", sg.id());
+        assertEquals("sg", sg.name());
+        assertEquals("example-sg", sg.description());
+
+        assertEquals("sgr-1", rule.id());
+        assertEquals("sg-1", rule.securityGroupId());
+        assertEquals("ingress", rule.direction());
+        assertEquals("IPv4", rule.etherType());
+        assertEquals((Integer) 80, rule.portRangeMax());
+        assertEquals((Integer) 0, rule.portRangeMin());
+        assertEquals("tcp", rule.protocol());
+        assertEquals("0.0.0.0/0", rule.remoteIpPrefix().toString());
+        assertEquals("g-1", rule.remoteGroupId());
+    }
+
+    private KubevirtSecurityGroup getKubevirtSecurityGroup(String resourceName) throws IOException {
+        InputStream jsonStream = KubevirtSecurityGroupCodecTest.class.getResourceAsStream(resourceName);
+        JsonNode json = context.mapper().readTree(jsonStream);
+        MatcherAssert.assertThat(json, notNullValue());
+        KubevirtSecurityGroup sg = kubevirtSecurityGroupCodec.decode((ObjectNode) json, context);
+        assertThat(sg, notNullValue());
+        return sg;
+    }
+
+    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
+        public <T> JsonCodec<T> codec(Class<T> entityClass) {
+            if (entityClass == KubevirtSecurityGroupRule.class) {
+                return (JsonCodec<T>) kubevirtSecurityGroupRuleCodec;
+            }
+
+            return manager.getCodec(entityClass);
+        }
+
+        @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/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupJsonMatcher.java
new file mode 100644
index 0000000..ea04c60
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupJsonMatcher.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroup;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroupRule;
+
+/**
+ * Hamcrest matcher for security group.
+ */
+public final class KubevirtSecurityGroupJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private static final String ID = "id";
+    private static final String NAME = "name";
+    private static final String DESCRIPTION = "description";
+    private static final String RULES = "rules";
+
+    private final KubevirtSecurityGroup sg;
+
+    private KubevirtSecurityGroupJsonMatcher(KubevirtSecurityGroup sg) {
+        this.sg = sg;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check sg ID
+        String jsonId = jsonNode.get(ID).asText();
+        String id = sg.id();
+        if (!jsonId.equals(id)) {
+            description.appendText("ID was " + jsonId);
+            return false;
+        }
+
+        // check sg name
+        String jsonName = jsonNode.get(NAME).asText();
+        String name = sg.name();
+        if (!jsonName.equals(name)) {
+            description.appendText("Name was " + jsonName);
+            return false;
+        }
+
+        // check description
+        JsonNode jsonDescription = jsonNode.get(DESCRIPTION);
+        if (jsonDescription != null) {
+            String myDescription = sg.description();
+            if (!jsonDescription.asText().equals(myDescription)) {
+                description.appendText("Description was " + jsonDescription);
+                return false;
+            }
+        }
+
+        JsonNode jsonSgr = jsonNode.get(RULES);
+        if (jsonSgr != null) {
+            // check size of rule array
+            if (jsonSgr.size() != sg.rules().size()) {
+                description.appendText("Rules was " + jsonSgr.size());
+                return false;
+            }
+
+            // check rules
+            for (KubevirtSecurityGroupRule sgr : sg.rules()) {
+                boolean ruleFound = false;
+                for (int ruleIndex = 0; ruleIndex < jsonSgr.size(); ruleIndex++) {
+                    KubevirtSecurityGroupRuleJsonMatcher ruleMatcher =
+                            KubevirtSecurityGroupRuleJsonMatcher
+                                    .matchesKubevirtSecurityGroupRule(sgr);
+                    if (ruleMatcher.matches(jsonSgr.get(ruleIndex))) {
+                        ruleFound = true;
+                        break;
+                    }
+                }
+
+                if (!ruleFound) {
+                    description.appendText("Rule not found " + sgr.toString());
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(sg.toString());
+    }
+
+    /**
+     * Factory to allocate a kubevirt security group matcher.
+     *
+     * @param sg kubevirt security group object we are looking for
+     * @return matcher
+     */
+    public static KubevirtSecurityGroupJsonMatcher
+    matchesKubevirtSecurityGroup(KubevirtSecurityGroup sg) {
+        return new KubevirtSecurityGroupJsonMatcher(sg);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupRuleJsonMatcher.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupRuleJsonMatcher.java
new file mode 100644
index 0000000..81ba374
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroupRuleJsonMatcher.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.codec;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.kubevirtnetworking.api.KubevirtSecurityGroupRule;
+
+/**
+ * Hamcrest matcher for kubevirt port.
+ */
+public final class KubevirtSecurityGroupRuleJsonMatcher extends TypeSafeDiagnosingMatcher<JsonNode> {
+
+    private final KubevirtSecurityGroupRule rule;
+
+    private static final String ID = "id";
+    private static final String SECURITY_GROUP_ID = "securityGroupId";
+    private static final String DIRECTION = "direction";
+    private static final String ETHER_TYPE = "etherType";
+    private static final String PORT_RANGE_MAX = "portRangeMax";
+    private static final String PORT_RANGE_MIN = "portRangeMin";
+    private static final String PROTOCOL = "protocol";
+    private static final String REMOTE_IP_PREFIX = "remoteIpPrefix";
+    private static final String REMOTE_GROUP_ID = "remoteGroupId";
+
+    private KubevirtSecurityGroupRuleJsonMatcher(KubevirtSecurityGroupRule rule) {
+        this.rule = rule;
+    }
+
+    @Override
+    protected boolean matchesSafely(JsonNode jsonNode, Description description) {
+        // check rule ID
+        String jsonId = jsonNode.get(ID).asText();
+        String id = rule.id();
+        if (!jsonId.equals(id)) {
+            description.appendText("Rule ID was " + jsonId);
+            return false;
+        }
+
+        // check security group ID
+        String jsonSecurityGroupId = jsonNode.get(SECURITY_GROUP_ID).asText();
+        String securityGroupId = rule.securityGroupId();
+        if (!jsonSecurityGroupId.equals(securityGroupId)) {
+            description.appendText("Security group ID was " + jsonSecurityGroupId);
+            return false;
+        }
+
+        // check direction
+        String jsonDirection = jsonNode.get(DIRECTION).asText();
+        String direction = rule.direction();
+        if (!jsonDirection.equals(direction)) {
+            description.appendText("Direction was " + jsonDirection);
+            return false;
+        }
+
+        // check ether type
+        JsonNode jsonEtherType = jsonNode.get(ETHER_TYPE);
+        if (jsonEtherType != null) {
+            String etherType = rule.etherType();
+            if (!jsonEtherType.asText().equals(etherType)) {
+                description.appendText("EtherType was " + jsonEtherType);
+                return false;
+            }
+        }
+
+        // check port range max
+        JsonNode jsonPortRangeMax = jsonNode.get(PORT_RANGE_MAX);
+        if (jsonPortRangeMax != null) {
+            int portRangeMax = rule.portRangeMax();
+            if (portRangeMax != jsonPortRangeMax.asInt()) {
+                description.appendText("PortRangeMax was " + jsonPortRangeMax);
+                return false;
+            }
+        }
+
+        // check port range min
+        JsonNode jsonPortRangeMin = jsonNode.get(PORT_RANGE_MIN);
+        if (jsonPortRangeMin != null) {
+            int portRangeMin = rule.portRangeMin();
+            if (portRangeMin != jsonPortRangeMin.asInt()) {
+                description.appendText("PortRangeMin was " + jsonPortRangeMin);
+                return false;
+            }
+        }
+
+        // check protocol
+        JsonNode jsonProtocol = jsonNode.get(PROTOCOL);
+        if (jsonProtocol != null) {
+            String protocol = rule.protocol();
+            if (!jsonProtocol.asText().equals(protocol)) {
+                description.appendText("Protocol was " + jsonProtocol);
+                return false;
+            }
+        }
+
+        // check remote IP prefix
+        JsonNode jsonRemoteIpPrefix = jsonNode.get(REMOTE_IP_PREFIX);
+        if (jsonRemoteIpPrefix != null) {
+            IpPrefix remoteIpPrefix = rule.remoteIpPrefix();
+            if (!jsonRemoteIpPrefix.asText().equals(remoteIpPrefix.toString())) {
+                description.appendText("Remote IP prefix was " + jsonRemoteIpPrefix);
+                return false;
+            }
+        }
+
+        // check remote group ID
+        JsonNode jsonRemoteGroupId = jsonNode.get(REMOTE_GROUP_ID);
+        if (jsonRemoteGroupId != null) {
+            String remoteGroupId = rule.remoteGroupId();
+            if (!jsonRemoteGroupId.asText().equals(remoteGroupId)) {
+                description.appendText("Remote group ID was " + jsonRemoteGroupId);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(rule.toString());
+    }
+
+    /**
+     * Factory to allocate an kubevirt security group rule matcher.
+     *
+     * @param rule kubevirt security group rule object we are looking for
+     * @return matcher
+     */
+    public static KubevirtSecurityGroupRuleJsonMatcher
+        matchesKubevirtSecurityGroupRule(KubevirtSecurityGroupRule rule) {
+        return new KubevirtSecurityGroupRuleJsonMatcher(rule);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtMm5WebResourceTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtMm5WebResourceTest.java
new file mode 100644
index 0000000..c1415fd
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtMm5WebResourceTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2022-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.kubevirtnetworking.web;
+
+import com.eclipsesource.json.Json;
+import com.eclipsesource.json.JsonArray;
+import com.eclipsesource.json.JsonObject;
+import com.google.common.collect.ImmutableSet;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouter;
+import org.onosproject.kubevirtnetworking.api.KubevirtRouterService;
+import org.onosproject.kubevirtnetworking.codec.KubevirtHostRouteCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtIpPoolCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtNetworkCodec;
+import org.onosproject.kubevirtnode.api.DefaultKubevirtNode;
+import org.onosproject.kubevirtnode.api.KubevirtNode;
+import org.onosproject.kubevirtnode.api.KubevirtNodeService;
+import org.onosproject.kubevirtnode.api.KubevirtNodeState;
+import org.onosproject.net.DeviceId;
+import org.onosproject.rest.resources.ResourceTest;
+
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Response;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+public class KubevirtMm5WebResourceTest extends ResourceTest {
+
+    final KubevirtRouterService mockRouterService = createMock(KubevirtRouterService.class);
+    final KubevirtNodeService mockNodeService = createMock(KubevirtNodeService.class);
+    final KubevirtNetworkService mockNetworkService = createMock(KubevirtNetworkService.class);
+
+    private static final String VR_STATUS_PATH = "api/mm5/v1/status/vr/";
+    private static final String NETWORK_PATH = "api/mm5/v1/network";
+
+    private static final String ROUTER_NAME = "router1";
+    private static final String HOST_NAME = "hostname1";
+
+
+    private static final KubevirtNetwork NETWORK_1 = DefaultKubevirtNetwork.builder()
+            .networkId("network")
+            .name("network")
+            .type(KubevirtNetwork.Type.FLAT)
+            .cidr("10.10.10.0/24")
+            .mtu(1500)
+            .gatewayIp(IpAddress.valueOf("10.10.10.1"))
+            .defaultRoute(true)
+            .ipPool(new KubevirtIpPool(IpAddress.valueOf("10.10.10.100"),
+                    IpAddress.valueOf("10.10.10.200")))
+            .build();
+
+    private static final KubevirtNode NODE_1 = DefaultKubevirtNode.builder()
+            .hostname(HOST_NAME)
+            .type(KubevirtNode.Type.GATEWAY)
+            .dataIp(IpAddress.valueOf("10.134.34.222"))
+            .managementIp(IpAddress.valueOf("10.134.231.30"))
+            .intgBridge(DeviceId.deviceId("of:00000000000000a1"))
+            .tunBridge(DeviceId.deviceId("of:00000000000000a2"))
+            .state(KubevirtNodeState.COMPLETE)
+            .build();
+
+
+    private static final KubevirtRouter ROUTER_1 = DefaultKubevirtRouter.builder()
+            .name(ROUTER_NAME).build();
+
+    /**
+     * Constructs a kubevirt mm5 resource test instance.
+     */
+    public KubevirtMm5WebResourceTest() {
+        super(ResourceConfig.forApplicationClass(KubevirtNetworkingWebApplication.class));
+    }
+
+    /**
+     * Sets up the global values for all the tests.
+     */
+    @Before
+    public void setUpTest() {
+        final CodecManager codecService = new CodecManager();
+        codecService.activate();
+        codecService.registerCodec(KubevirtNetwork.class, new KubevirtNetworkCodec());
+        codecService.registerCodec(KubevirtHostRoute.class, new KubevirtHostRouteCodec());
+        codecService.registerCodec(KubevirtIpPool.class, new KubevirtIpPoolCodec());
+
+        ServiceDirectory testDirectory =
+                new TestServiceDirectory()
+                        .add(KubevirtNodeService.class, mockNodeService)
+                        .add(KubevirtNetworkService.class, mockNetworkService)
+                        .add(KubevirtRouterService.class, mockRouterService)
+                        .add(CodecService.class, codecService);
+
+        setServiceDirectory(testDirectory);
+    }
+
+    @Test
+    public void testVrStatusWithExistingRouterOperation() {
+        expect(mockRouterService.routers()).andReturn(ImmutableSet.of(ROUTER_1)).anyTimes();
+        replay(mockRouterService);
+
+        final WebTarget wt = target();
+
+        final String stringResponse = wt.path(VR_STATUS_PATH + ROUTER_NAME).request().get(String.class);
+        final Response response = wt.path(VR_STATUS_PATH + ROUTER_NAME).request().get();
+
+        final JsonObject result = Json.parse(stringResponse).asObject();
+
+        assertThat(result, notNullValue());
+        assertThat(result.names(), hasSize(4));
+        assertThat(response.getStatus(), is(200));
+
+        verify(mockRouterService);
+    }
+
+    @Test
+    public void testVrStatusWithNonRouterOperation() {
+        expect(mockRouterService.routers()).andReturn(ImmutableSet.of(ROUTER_1)).anyTimes();
+        replay(mockRouterService);
+
+        final WebTarget wt = target();
+
+        final Response response = wt.path(VR_STATUS_PATH + "anyRouter").request().get();
+        assertThat(response.getStatus(), is(404));
+
+        verify(mockRouterService);
+    }
+
+    @Test
+    public void testGetNetworkOperation() {
+        expect(mockNetworkService.networks()).andReturn(ImmutableSet.of(NETWORK_1)).anyTimes();
+        replay(mockNetworkService);
+
+        final WebTarget wt = target();
+
+        final String response = wt.path(NETWORK_PATH).request().get(String.class);
+        final JsonObject result = Json.parse(response).asObject();
+
+        assertThat(result, notNullValue());
+        assertThat(result.names(), hasSize(1));
+        assertThat(result.names().get(0), is("networks"));
+
+        final JsonArray jsonNodes = result.get("networks").asArray();
+        assertThat(jsonNodes, notNullValue());
+        assertThat(jsonNodes.size(), is(1));
+
+        verify(mockNetworkService);
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkWebResourceTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkWebResourceTest.java
new file mode 100644
index 0000000..b453dad
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkWebResourceTest.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.web;
+
+import org.glassfish.jersey.server.ResourceConfig;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.osgi.TestServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.impl.CodecManager;
+import org.onosproject.kubevirtnetworking.api.DefaultKubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetworkAdminService;
+import org.onosproject.kubevirtnetworking.codec.KubevirtHostRouteCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtIpPoolCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtNetworkCodec;
+import org.onosproject.rest.resources.ResourceTest;
+
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.anyString;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Unit tests for kubevirt network REST API.
+ */
+public class KubevirtNetworkWebResourceTest extends ResourceTest {
+
+    final KubevirtNetworkAdminService mockAdminService = createMock(KubevirtNetworkAdminService.class);
+    private static final String PATH = "network";
+
+    private KubevirtNetwork network;
+
+    /**
+     * Constructs a kubernetes networking resource test instance.
+     */
+    public KubevirtNetworkWebResourceTest() {
+        super(ResourceConfig.forApplicationClass(KubevirtNetworkingWebApplication.class));
+    }
+
+    /**
+     * Sets up the global values for all the tests.
+     */
+    @Before
+    public void setUpTest() {
+        final CodecManager codecService = new CodecManager();
+        codecService.activate();
+        codecService.registerCodec(KubevirtHostRoute.class, new KubevirtHostRouteCodec());
+        codecService.registerCodec(KubevirtIpPool.class, new KubevirtIpPoolCodec());
+        codecService.registerCodec(KubevirtNetwork.class, new KubevirtNetworkCodec());
+        ServiceDirectory testDirectory =
+                new TestServiceDirectory()
+                        .add(KubevirtNetworkAdminService.class, mockAdminService)
+                        .add(CodecService.class, codecService);
+        setServiceDirectory(testDirectory);
+
+        network = DefaultKubevirtNetwork.builder()
+                .networkId("network")
+                .name("network")
+                .type(KubevirtNetwork.Type.FLAT)
+                .cidr("10.10.10.0/24")
+                .mtu(1500)
+                .gatewayIp(IpAddress.valueOf("10.10.10.1"))
+                .defaultRoute(true)
+                .ipPool(new KubevirtIpPool(IpAddress.valueOf("10.10.10.100"),
+                        IpAddress.valueOf("10.10.10.200")))
+                .build();
+    }
+
+    /**
+     * Tests the results of the REST API POST method with creating new network operation.
+     */
+    @Test
+    public void testCreateNetworkWithCreateOperation() {
+        mockAdminService.createNetwork(anyObject());
+        replay(mockAdminService);
+
+        final WebTarget wt = target();
+        InputStream jsonStream = KubevirtNetworkWebResourceTest.class
+                .getResourceAsStream("kubevirt-network.json");
+        Response response = wt.path(PATH).request(MediaType.APPLICATION_JSON_TYPE)
+                .post(Entity.json(jsonStream));
+        final int status = response.getStatus();
+
+        assertThat(status, is(201));
+
+        verify(mockAdminService);
+    }
+
+    /**
+     * Tests the results of the REST API PUT method with modifying the network.
+     */
+    @Test
+    public void testUpdateNetworkWithModifyOperation() {
+        mockAdminService.updateNetwork(anyObject());
+        replay(mockAdminService);
+
+        String location = PATH + "/network";
+
+        final WebTarget wt = target();
+        InputStream jsonStream = KubevirtNetworkWebResourceTest.class
+                .getResourceAsStream("kubevirt-network.json");
+        Response response = wt.path(location)
+                .request(MediaType.APPLICATION_JSON_TYPE)
+                .put(Entity.json(jsonStream));
+        final int status = response.getStatus();
+
+        assertThat(status, is(200));
+
+        verify(mockAdminService);
+    }
+
+    /**
+     * Tests the results of the REST API DELETE method with deleting the network.
+     */
+    @Test
+    public void testDeleteNetworkWithDeletionOperation() {
+        mockAdminService.removeNetwork(anyString());
+        replay(mockAdminService);
+
+        String location = PATH + "/network";
+
+        final WebTarget wt = target();
+        Response response = wt.path(location).request(
+                MediaType.APPLICATION_JSON_TYPE).delete();
+
+        final int status = response.getStatus();
+
+        assertThat(status, is(204));
+
+        verify(mockAdminService);
+    }
+
+    /**
+     * Tests the results of checking network existence.
+     */
+    @Test
+    public void testHasNetworkWithValidNetwork() {
+        expect(mockAdminService.network(anyString())).andReturn(network);
+        replay(mockAdminService);
+
+        String location = PATH + "/exist/network";
+
+        final WebTarget wt = target();
+        String response = wt.path(location).request(
+                MediaType.APPLICATION_JSON_TYPE).get(String.class);
+
+        assertThat(response, is("{\"result\":true}"));
+
+        verify((mockAdminService));
+    }
+
+    /**
+     * Tests the results of checking network existence.
+     */
+    @Test
+    public void testHasNetworkWithNullNetwork() {
+        expect(mockAdminService.network(anyString())).andReturn(null);
+        replay(mockAdminService);
+
+        String location = PATH + "/exist/network";
+
+        final WebTarget wt = target();
+        String response = wt.path(location).request(
+                MediaType.APPLICATION_JSON_TYPE).get(String.class);
+
+        assertThat(response, is("{\"result\":false}"));
+
+        verify((mockAdminService));
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingCodecRegisterTest.java b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingCodecRegisterTest.java
new file mode 100644
index 0000000..c3784ba
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/java/org/onosproject/kubevirtnetworking/web/KubevirtNetworkingCodecRegisterTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2021-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.kubevirtnetworking.web;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.junit.Test;
+import org.onlab.junit.TestUtils;
+import org.onosproject.codec.CodecService;
+import org.onosproject.codec.JsonCodec;
+import org.onosproject.kubevirtnetworking.api.KubevirtHostRoute;
+import org.onosproject.kubevirtnetworking.api.KubevirtIpPool;
+import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
+import org.onosproject.kubevirtnetworking.codec.KubevirtHostRouteCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtIpPoolCodec;
+import org.onosproject.kubevirtnetworking.codec.KubevirtNetworkCodec;
+
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for kubevirt networking codec register.
+ */
+public class KubevirtNetworkingCodecRegisterTest {
+
+    /**
+     * Tests codec register activation and deactivation.
+     */
+    @Test
+    public void testActivateDeactivate() {
+        KubevirtNetworkingCodecRegister register = new KubevirtNetworkingCodecRegister();
+        CodecService codecService = new TestCodecService();
+
+        TestUtils.setField(register, "codecService", codecService);
+        register.activate();
+
+        assertEquals(KubevirtNetworkCodec.class.getName(),
+                codecService.getCodec(KubevirtNetwork.class).getClass().getName());
+        assertEquals(KubevirtHostRouteCodec.class.getName(),
+                codecService.getCodec(KubevirtHostRoute.class).getClass().getName());
+        assertEquals(KubevirtIpPoolCodec.class.getName(),
+                codecService.getCodec(KubevirtIpPool.class).getClass().getName());
+
+        register.deactivate();
+    }
+
+    private static class TestCodecService implements CodecService {
+
+        private Map<String, JsonCodec> codecMap = Maps.newConcurrentMap();
+
+        @Override
+        public Set<Class<?>> getCodecs() {
+            return ImmutableSet.of();
+        }
+
+        @Override
+        public <T> JsonCodec<T> getCodec(Class<T> entityClass) {
+            return codecMap.get(entityClass.getName());
+        }
+
+        @Override
+        public <T> void registerCodec(Class<T> entityClass, JsonCodec<T> codec) {
+            codecMap.put(entityClass.getName(), codec);
+        }
+
+        @Override
+        public void unregisterCodec(Class<?> entityClass) {
+            codecMap.remove(entityClass.getName());
+        }
+    }
+}
diff --git a/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIp.json b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIp.json
new file mode 100644
index 0000000..93819ce
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtFloatingIp.json
@@ -0,0 +1,9 @@
+{
+  "id": "fip-1",
+  "routerName": "router-1",
+  "networkName": "flat-1",
+  "floatingIp": "10.10.10.10",
+  "podName": "pod-1",
+  "vmName": "vm-1",
+  "fixedIp": "20.20.20.20"
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancer.json b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancer.json
new file mode 100644
index 0000000..806fd73
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtLoadBalancer.json
@@ -0,0 +1,25 @@
+{
+  "name": "lb-1",
+  "description": "Example Load Balancer",
+  "networkId": "net-1",
+  "vip": "10.10.10.10",
+  "members": [
+    "10.10.10.11",
+    "10.10.10.12"
+  ],
+  "rules": [
+    {
+      "protocol": "tcp",
+      "portRangeMax": 8000,
+      "portRangeMin": 7000
+    },
+    {
+      "protocol": "udp",
+      "portRangeMax": 9000,
+      "portRangeMin": 8000
+    },
+    {
+      "protocol" : "icmp"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtNetwork.json b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtNetwork.json
new file mode 100644
index 0000000..293cfcb
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtNetwork.json
@@ -0,0 +1,22 @@
+{
+  "networkId": "network-1",
+  "name": "network-1",
+  "type": "FLAT",
+  "mtu": 1500,
+  "cidr": "10.10.0.0/24",
+  "gatewayIp": "10.10.0.1",
+  "defaultRoute": true,
+  "ipPool": {
+    "start": "10.10.10.100",
+    "end": "10.10.10.200"
+  },
+  "hostRoutes": [
+    {
+      "destination": "10.10.10.0/24",
+      "nexthop": "10.10.10.1"
+    }
+  ],
+  "dnses": [
+    "8.8.8.8"
+  ]
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtRouter.json b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtRouter.json
new file mode 100644
index 0000000..5d9d53c
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtRouter.json
@@ -0,0 +1,18 @@
+{
+  "name": "router-1",
+  "description": "Example Virtual Router",
+  "enableSnat": true,
+  "mac": "11:22:33:44:55:66",
+  "external": {
+    "ip": "192.168.10.5",
+    "network": "external-network"
+  },
+  "internal": [
+    "vxlan-network-1",
+    "vxlan-network-2"
+  ],
+  "peerRouter": {
+    "ip": "192.168.10.1"
+  },
+  "gateway": "gatewayNode"
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroup.json b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroup.json
new file mode 100644
index 0000000..365f5a3
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/codec/KubevirtSecurityGroup.json
@@ -0,0 +1,18 @@
+{
+  "id": "sg-1",
+  "name": "sg",
+  "description": "example-sg",
+  "rules": [
+    {
+      "id": "sgr-1",
+      "securityGroupId": "sg-1",
+      "direction": "ingress",
+      "etherType": "IPv4",
+      "portRangeMax": 80,
+      "portRangeMin": 0,
+      "protocol": "tcp",
+      "remoteIpPrefix": "0.0.0.0/0",
+      "remoteGroupId": "g-1"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/web/kubevirt-network.json b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/web/kubevirt-network.json
new file mode 100644
index 0000000..c65236c
--- /dev/null
+++ b/apps/kubevirt-networking/web/src/test/resources/org/onosproject/kubevirtnetworking/web/kubevirt-network.json
@@ -0,0 +1,13 @@
+{
+  "networkId": "network",
+  "name": "network",
+  "type": "FLAT",
+  "mtu": 1500,
+  "cidr": "10.10.10.0/24",
+  "gatewayIp": "10.10.10.1",
+  "defaultRoute": true,
+  "ipPool": {
+    "start": "10.10.10.100",
+    "end": "10.10.10.200"
+  }
+}
\ No newline at end of file