[ONOS-7731] Update api interface & implementation of openstack vtap app

Change-Id: I7c3c7888b00a7357b13e3b1756e9cd0a1bb6a5c0
diff --git a/apps/openstackvtap/app/BUCK b/apps/openstackvtap/app/BUCK
index dc3f55c..3656f6c 100644
--- a/apps/openstackvtap/app/BUCK
+++ b/apps/openstackvtap/app/BUCK
@@ -25,8 +25,8 @@
     deps = COMPILE_DEPS,
     test_deps = TEST_DEPS,
     web_context = '/onos/openstackvtap',
-    api_title = 'OpenStack Network vTap REST API',
+    api_title = 'OpenStack Vtap REST API',
     api_version = '1.0',
-    api_description = 'OpenStack Network vTap REST API',
+    api_description = 'OpenStack Vtap REST API',
     api_package = 'org.onosproject.openstackvtap.web',
 )
diff --git a/apps/openstackvtap/app/BUILD b/apps/openstackvtap/app/BUILD
index 078ed2b..7d2ceba 100644
--- a/apps/openstackvtap/app/BUILD
+++ b/apps/openstackvtap/app/BUILD
@@ -12,9 +12,9 @@
 ]
 
 osgi_jar_with_tests(
-    api_description = "OpenStack Network vTap REST API",
+    api_description = "Openstack Vtap REST API",
     api_package = "org.onosproject.openstackvtap.web",
-    api_title = "OpenStack Network vTap REST API",
+    api_title = "Openstack Vtap REST API",
     api_version = "1.0",
     test_deps = TEST_DEPS,
     web_context = "/onos/openstackvtap",
diff --git a/apps/openstackvtap/app/app.xml b/apps/openstackvtap/app/app.xml
index 45024d9..b077858 100644
--- a/apps/openstackvtap/app/app.xml
+++ b/apps/openstackvtap/app/app.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 <app name="org.onosproject.openstackvtap" origin="Open Networking Foundation"
-     version="${project.version}" category="Utility" title="OpenStack vTap App"
+     version="${project.version}" category="Utility" title="Openstack Vtap App"
      features="${project.artifactId}" apps="org.onosproject.openstacknetworking"
      featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features">
     <description>${project.description}</description>
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java
index 8d9955f..5272083 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapAddCommand.java
@@ -28,13 +28,13 @@
 import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString;
 
 /**
- * Command line interface for adding openstack vTap rule.
+ * Adds a openstack vtap rule.
  */
 @Command(scope = "onos", name = "openstack-vtap-add",
         description = "OpenstackVtap activate")
 public class OpenstackVtapAddCommand extends AbstractShellCommand {
 
-    private final OpenstackVtapAdminService vTapService =
+    private final OpenstackVtapAdminService vtapService =
                                             get(OpenstackVtapAdminService.class);
 
     @Argument(index = 0, name = "srcIp",
@@ -48,9 +48,9 @@
     String dstIp = "";
 
     @Argument(index = 2, name = "ipProto",
-            description = "IP protocol [tcp|udp|icmp|none]",
+            description = "IP protocol [any|tcp|udp|icmp]",
             required = false, multiValued = false)
-    String ipProto = "";
+    String ipProto = "any";
 
     @Argument(index = 3, name = "srcTpPort",
             description = "source transport layer port (0 is skip)",
@@ -63,43 +63,42 @@
     int dstTpPort = 0;
 
     @Argument(index = 5, name = "type",
-            description = "vTap type [all|tx|rx]",
+            description = "vtap type [all|rx|tx]",
             required = false, multiValued = false)
-    String vTapTypeStr = "all";
+    String vtapTypeStr = "all";
 
     @Override
     protected void execute() {
-        DefaultOpenstackVtapCriterion.Builder
-                    defaultVtapCriterionBuilder = DefaultOpenstackVtapCriterion.builder();
-        if (makeCriterion(defaultVtapCriterionBuilder)) {
-            OpenstackVtap.Type type = getVtapTypeFromString(vTapTypeStr);
+        DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder = DefaultOpenstackVtapCriterion.builder();
+        if (makeCriterion(vtapCriterionBuilder)) {
+            OpenstackVtap.Type type = getVtapTypeFromString(vtapTypeStr);
             if (type == null) {
-                print("Invalid vTap type");
+                print("Invalid vtap type");
                 return;
             }
 
-            OpenstackVtap vTap = vTapService.createVtap(type, defaultVtapCriterionBuilder.build());
-            if (vTap != null) {
-                print("Created OpenstackVtap with id { %s }", vTap.id().toString());
+            OpenstackVtap vtap = vtapService.createVtap(type, vtapCriterionBuilder.build());
+            if (vtap != null) {
+                print("Created OpenstackVtap with id { %s }", vtap.id().toString());
             } else {
                 print("Failed to create OpenstackVtap");
             }
         }
     }
 
-    private boolean makeCriterion(DefaultOpenstackVtapCriterion.Builder vTapCriterionBuilder) {
+    private boolean makeCriterion(DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder) {
         try {
-            vTapCriterionBuilder.srcIpPrefix(IpPrefix.valueOf(srcIp));
-            vTapCriterionBuilder.dstIpPrefix(IpPrefix.valueOf(dstIp));
+            vtapCriterionBuilder.srcIpPrefix(IpPrefix.valueOf(srcIp));
+            vtapCriterionBuilder.dstIpPrefix(IpPrefix.valueOf(dstIp));
         } catch (Exception e) {
             print("Inputted valid source IP & destination IP in CIDR (e.g., \"10.1.0.4/32\")");
             return false;
         }
 
-        vTapCriterionBuilder.ipProtocol(getProtocolTypeFromString(ipProto.toLowerCase()));
+        vtapCriterionBuilder.ipProtocol(getProtocolTypeFromString(ipProto.toLowerCase()));
 
-        vTapCriterionBuilder.srcTpPort(TpPort.tpPort(srcTpPort));
-        vTapCriterionBuilder.dstTpPort(TpPort.tpPort(dstTpPort));
+        vtapCriterionBuilder.srcTpPort(TpPort.tpPort(srcTpPort));
+        vtapCriterionBuilder.dstTpPort(TpPort.tpPort(dstTpPort));
 
         return true;
     }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java
index e4a167b..e54f6ac 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapDeleteCommand.java
@@ -22,25 +22,25 @@
 import org.onosproject.openstackvtap.api.OpenstackVtapId;
 
 /**
- * Command line interface for removing openstack vTap rule.
+ * Delete a openstack vtap rule from the existing vtaps.
  */
 @Command(scope = "onos", name = "openstack-vtap-del",
         description = "OpenstackVtap deactivate")
 public class OpenstackVtapDeleteCommand extends AbstractShellCommand {
 
-    private final OpenstackVtapAdminService vTapService = get(OpenstackVtapAdminService.class);
+    private final OpenstackVtapAdminService vtapService = get(OpenstackVtapAdminService.class);
 
-    @Argument(index = 0, name = "id", description = "vTap ID",
+    @Argument(index = 0, name = "id", description = "vtap ID",
             required = true, multiValued = false)
-    String vTapId = "";
+    String vtapId = "";
 
     @Override
     protected void execute() {
-        OpenstackVtap vTap = vTapService.removeVtap(OpenstackVtapId.vTapId(vTapId));
-        if (vTap != null) {
-            print("Removed OpenstackVtap with id { %s }", vTap.id().toString());
+        OpenstackVtap vtap = vtapService.removeVtap(OpenstackVtapId.vtapId(vtapId));
+        if (vtap != null) {
+            print("Removed OpenstackVtap with id { %s }", vtap.id().toString());
         } else {
-            print("Failed to remove OpenstackVtap with id { %s }", vTapId);
+            print("Failed to remove OpenstackVtap with id { %s }", vtapId);
         }
     }
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java
index afb9bbf..a0989b33d 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java
@@ -15,46 +15,66 @@
  */
 package org.onosproject.openstackvtap.cli;
 
+import com.google.common.collect.ImmutableSet;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.onosproject.openstackvtap.api.OpenstackVtap;
 import org.onosproject.openstackvtap.api.OpenstackVtapService;
 
+import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString;
 
 /**
- * Command line interface for listing openstack vTap rules.
+ * Lists openstack vtap rules.
  */
 @Command(scope = "onos", name = "openstack-vtap-list",
         description = "OpenstackVtap list")
 public class OpenstackVtapListCommand extends AbstractShellCommand {
 
-    private final OpenstackVtapService vTapService = get(OpenstackVtapService.class);
+    private final OpenstackVtapService vtapService = get(OpenstackVtapService.class);
+    private final OpenstackNodeService osNodeService = get(OpenstackNodeService.class);
 
     @Argument(index = 0, name = "type",
-            description = "vTap type [all|tx|rx]",
+            description = "vtap type [any|all|rx|tx]",
             required = false, multiValued = false)
-    String vTapType = "none";
+    String vtapType = "any";
 
     private static final String FORMAT = "ID { %s }: type [%s], srcIP [%s], dstIP [%s]";
-    private static final String FORMAT_TX_DEVICES  = "   tx devices: %s";
-    private static final String FORMAT_RX_DEVICES  = "   rx devices: %s";
+    private static final String FORMAT_TX_NODES = "   tx openstack nodes: %s";
+    private static final String FORMAT_RX_NODES = "   rx openstack nodes: %s";
 
     @Override
     protected void execute() {
-        OpenstackVtap.Type type = getVtapTypeFromString(vTapType);
-        Set<OpenstackVtap> openstackVtaps = vTapService.getVtaps(type);
-        for (OpenstackVtap vTap : openstackVtaps) {
+        OpenstackVtap.Type type = getVtapTypeFromString(vtapType);
+        Set<OpenstackVtap> openstackVtaps = vtapService.getVtaps(type);
+        for (OpenstackVtap vtap : openstackVtaps) {
             print(FORMAT,
-                    vTap.id().toString(),
-                    vTap.type().toString(),
-                    vTap.vTapCriterion().srcIpPrefix().toString(),
-                    vTap.vTapCriterion().dstIpPrefix().toString());
-            print(FORMAT_TX_DEVICES, vTap.txDeviceIds());
-            print(FORMAT_RX_DEVICES, vTap.rxDeviceIds());
+                    vtap.id().toString(),
+                    vtap.type().toString(),
+                    vtap.vtapCriterion().srcIpPrefix().toString(),
+                    vtap.vtapCriterion().dstIpPrefix().toString());
+            print(FORMAT_TX_NODES, osNodeNames(vtap.txDeviceIds()));
+            print(FORMAT_RX_NODES, osNodeNames(vtap.rxDeviceIds()));
         }
     }
+
+    private Set<String> osNodeNames(Set<DeviceId> deviceIds) {
+        if (deviceIds == null) {
+            return ImmutableSet.of();
+        } else {
+            return deviceIds.parallelStream()
+                    .map(osNodeService::node)
+                    .filter(Objects::nonNull)
+                    .map(OpenstackNode::hostname)
+                    .collect(Collectors.toSet());
+        }
+    }
+
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapNetworkListCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapNetworkListCommand.java
new file mode 100644
index 0000000..68a94f8
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapNetworkListCommand.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018-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.openstackvtap.cli;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Lists openstack vtap networks.
+ */
+@Command(scope = "onos", name = "openstack-vtap-network-list",
+        description = "OpenstackVtap network list")
+public class OpenstackVtapNetworkListCommand extends AbstractShellCommand {
+
+    private final OpenstackVtapAdminService osVtapAdminService = get(OpenstackVtapAdminService.class);
+    private final OpenstackNodeService osNodeService = get(OpenstackNodeService.class);
+
+    private static final String FORMAT = "mode [%s], networkId [%d], serverIp [%s]";
+    private static final String FORMAT_NODES = "   openstack nodes: %s";
+
+    @Override
+    protected void execute() {
+        OpenstackVtapNetwork vtapNetwork = osVtapAdminService.getVtapNetwork();
+        if (vtapNetwork != null) {
+            print(FORMAT,
+                    vtapNetwork.mode().toString(),
+                    vtapNetwork.networkId() != null ? vtapNetwork.networkId() : "N/A",
+                    vtapNetwork.serverIp().toString());
+            print(FORMAT_NODES, osNodeNames(osVtapAdminService.getVtapNetworkDevices()));
+        }
+    }
+
+    private Set<String> osNodeNames(Set<DeviceId> deviceIds) {
+        if (deviceIds == null) {
+            return ImmutableSet.of();
+        } else {
+            return deviceIds.parallelStream()
+                    .map(osNodeService::node)
+                    .filter(Objects::nonNull)
+                    .map(OpenstackNode::hostname)
+                    .collect(Collectors.toSet());
+        }
+    }
+
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapOutputCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapOutputCommand.java
deleted file mode 100644
index 257cb9a..0000000
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapOutputCommand.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2018-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.openstackvtap.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.onlab.packet.VlanId;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.openstackvtap.api.OpenstackVtap;
-import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
-
-import static org.onlab.packet.VlanId.UNTAGGED;
-import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString;
-
-/**
- * Command line interface for set openstack vTap output.
- */
-@Command(scope = "onos", name = "openstack-vtap-output",
-        description = "OpenstackVtap output setup")
-public class OpenstackVtapOutputCommand extends AbstractShellCommand {
-
-    private final DeviceService deviceService = get(DeviceService.class);
-    private final OpenstackVtapAdminService vTapAdminService =
-                                            get(OpenstackVtapAdminService.class);
-
-    @Argument(index = 0, name = "deviceId", description = "device id",
-            required = true, multiValued = false)
-    String id = "";
-
-    @Argument(index = 1, name = "port", description = "output port number",
-            required = true, multiValued = false)
-    int port = 0;
-
-    @Argument(index = 2, name = "vlan", description = "vlan id",
-            required = false, multiValued = false)
-    int vlan = UNTAGGED;
-
-    @Argument(index = 3, name = "type", description = "vTap type [all|tx|rx]",
-            required = false, multiValued = false)
-    String vTapTypeStr = "all";
-
-    @Override
-    protected void execute() {
-        try {
-            Device device = deviceService.getDevice(DeviceId.deviceId(id));
-            if (device != null) {
-                OpenstackVtap.Type type = getVtapTypeFromString(vTapTypeStr);
-
-                vTapAdminService.setVtapOutput(device.id(), type,
-                        PortNumber.portNumber(port), VlanId.vlanId((short) vlan));
-                print("Set OpenstackVtap output deviceId { %s }, port=%s, vlan=%s",
-                        device.id().toString(),
-                        PortNumber.portNumber(port).toString(),
-                        VlanId.vlanId((short) vlan).toString());
-            } else {
-                print("Invalid device id");
-            }
-        } catch (Exception e) {
-            print("Invalid parameter: %s", e.toString());
-        }
-    }
-}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java
index 3d0f747..3bfcf2b 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java
@@ -28,10 +28,10 @@
     @Override
     protected List<String> choices() {
         List<String> strings = Lists.newArrayList();
+        strings.add("any");
         strings.add("tcp");
         strings.add("udp");
         strings.add("icmp");
-        strings.add("none");
         return strings;
     }
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java
index c53098d..dad6e8e 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.java
@@ -27,11 +27,11 @@
 import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString;
 
 /**
- * vTap ID completer.
+ * Vtap ID completer.
  */
 public class VtapIdCompleter implements Completer {
 
-    private static final String VTAP_TYPE = "none";
+    private static final String VTAP_TYPE = "any";
 
     @Override
     public int complete(String buffer, int cursor, List<String> candidates) {
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java
index 2784833..c77c913 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java
@@ -21,16 +21,17 @@
 import java.util.List;
 
 /**
- * vTap type completer.
+ * Vtap type completer.
  */
 public class VtapTypeCompleter extends AbstractChoicesCompleter {
 
     @Override
     protected List<String> choices() {
         List<String> strings = Lists.newArrayList();
-        strings.add("tx");
-        strings.add("rx");
         strings.add("all");
+        strings.add("rx");
+        strings.add("tx");
+        strings.add("any");
         return strings;
     }
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java
index 772bbc8..3b3dec1 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * Console commands for OpenStack vtap.
+ * Console commands for openstack vtap.
  */
 package org.onosproject.openstackvtap.cli;
\ No newline at end of file
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/OpenstackVtapNetworkCodec.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/OpenstackVtapNetworkCodec.java
new file mode 100644
index 0000000..6dd6b5c
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/OpenstackVtapNetworkCodec.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2018-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.openstackvtap.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.openstackvtap.api.OpenstackVtapNetwork;
+import org.onosproject.openstackvtap.impl.DefaultOpenstackVtapNetwork;
+import org.slf4j.Logger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Openstack vtap network codec used for serializing and de-serializing JSON string.
+ */
+public final class OpenstackVtapNetworkCodec extends JsonCodec<OpenstackVtapNetwork> {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String MODE = "mode";
+    private static final String NETWORK_ID = "networkId";
+    private static final String SERVER_IP = "serverIp";
+    private static final String NODES = "nodes";
+
+    private static final String JSON_NULL_MESSAGE = "% cannot be null";
+    private static final String JSON_MISSING_MESSAGE = "% is required";
+    private static final String JSON_TYPE_MESSAGE = "% is not json object type";
+
+    @Override
+    public ObjectNode encode(OpenstackVtapNetwork network, CodecContext context) {
+        checkNotNull(network, JSON_NULL_MESSAGE, "OpenstackVtapNetwork object");
+
+        ObjectNode result = context.mapper().createObjectNode()
+                .put(MODE, network.mode().toString())
+                .put(NETWORK_ID, network.networkId())
+                .put(SERVER_IP, network.serverIp().toString());
+
+        return result;
+    }
+
+    @Override
+    public OpenstackVtapNetwork decode(ObjectNode json, CodecContext context) {
+        checkNotNull(json, JSON_NULL_MESSAGE, "OpenstackVtapNetwork json");
+        checkState(json.isObject(), JSON_TYPE_MESSAGE, "OpenstackVtapNetwork json");
+
+        DefaultOpenstackVtapNetwork.Builder builder = DefaultOpenstackVtapNetwork.builder()
+                .mode(OpenstackVtapNetwork.Mode.valueOf(checkNotNull(json.get(MODE).asText(null),
+                        JSON_MISSING_MESSAGE, MODE)))
+                .networkId(json.get(NETWORK_ID).asInt(0))
+                .serverIp(IpAddress.valueOf(checkNotNull(json.get(SERVER_IP).asText(null),
+                        JSON_MISSING_MESSAGE, SERVER_IP)));
+
+        log.debug("OpenstackVtapNetwork is {}", builder.build().toString());
+        return builder.build();
+    }
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/package-info.java
new file mode 100644
index 0000000..f76f7a9
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/codec/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-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 openstack vtap entity JSON codecs.
+ */
+package org.onosproject.openstackvtap.codec;
\ No newline at end of file
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUI.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUI.java
new file mode 100644
index 0000000..c68c338
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUI.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2018-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.openstackvtap.gui;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.ui.UiExtensionService;
+import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiTopoOverlayFactory;
+import org.onosproject.ui.UiView;
+import org.onosproject.ui.UiViewHidden;
+import org.onosproject.ui.UiExtension;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Mechanism to stream data to the GUI.
+ */
+@Component(immediate = true, enabled = true)
+@Service(value = OpenstackVtapUI.class)
+public class OpenstackVtapUI {
+    private static final String OPENSTACK_VTAP_ID = "openstackvtap";
+    private static final String RESOURCE_PATH = "gui";
+    private static final ClassLoader CL = OpenstackVtapUI.class.getClassLoader();
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected UiExtensionService uiExtensionService;
+
+    // Factory for UI message handlers
+    private final UiMessageHandlerFactory messageHandlerFactory =
+            () -> ImmutableList.of(new OpenstackVtapViewMessageHandler());
+
+    // List of application views
+    private final List<UiView> views = ImmutableList.of(
+            new UiViewHidden(OPENSTACK_VTAP_ID)
+    );
+
+    // Factory for UI topology overlays
+    private final UiTopoOverlayFactory topoOverlayFactory =
+            () -> ImmutableList.of(
+                    new OpenstackVtapUiTopovOverlay()
+            );
+
+    // Application UI extension
+    private final UiExtension uiExtension =
+            new UiExtension.Builder(CL, views)
+                    .messageHandlerFactory(messageHandlerFactory)
+                    .resourcePath(RESOURCE_PATH)
+                    .topoOverlayFactory(topoOverlayFactory)
+                    .build();
+
+    @Activate
+    protected void activate() {
+        uiExtensionService.register(uiExtension);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        uiExtensionService.unregister(uiExtension);
+        log.info("Stopped");
+    }
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUiTopovOverlay.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUiTopovOverlay.java
new file mode 100644
index 0000000..287aa99
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapUiTopovOverlay.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018-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.openstackvtap.gui;
+
+import org.onosproject.ui.UiTopoOverlay;
+
+public class OpenstackVtapUiTopovOverlay extends UiTopoOverlay {
+    private static final String OVERLAY_ID = "vtap-overlay";
+
+    public OpenstackVtapUiTopovOverlay() {
+        super(OVERLAY_ID);
+    }
+
+    @Override
+    public void activate() {
+        super.activate();
+        log.debug("Openstack VtapOverlay Activated");
+    }
+
+    @Override
+    public void deactivate() {
+        super.deactivate();
+        log.debug("Openstack VtapOverlay Deactivated");
+    }
+
+}
+
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapViewMessageHandler.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapViewMessageHandler.java
new file mode 100644
index 0000000..f95bbc4
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/OpenstackVtapViewMessageHandler.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2018-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.openstackvtap.gui;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.ui.RequestHandler;
+import org.onosproject.ui.UiConnection;
+import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.util.Collection;
+import java.util.Set;
+import java.util.Iterator;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.TpPort;
+import org.onosproject.openstackvtap.api.OpenstackVtap;
+import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
+import org.onosproject.openstackvtap.impl.DefaultOpenstackVtapCriterion;
+
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getProtocolTypeFromString;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString;
+
+
+import static org.onosproject.net.HostId.hostId;
+
+/**
+ * Message handler for Openstack Vtap related messages.
+ */
+public class OpenstackVtapViewMessageHandler extends UiMessageHandler {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String OSV_IS_ACTIVATED_REQ = "openstackVtapIsActivatedRequest";
+    private static final String OSV_IS_ACTIVATED_RESP = "openstackVtapIsActivatedResponse";
+    private static final String OSV_CREATE_REQ = "openstackVtapCreateRequest";
+    private static final String OSV_CREATE_RESP = "openstackVtapCreateResponse";
+
+    private static final String SOURCE = "src";
+    private static final String DESTINATION = "dst";
+    private static final String SOURCE_IP = "srcIp";
+    private static final String DESTINATION_IP = "dstIp";
+    private static final String SOURCE_TRANSPORT_PORT = "srcPort";
+    private static final String DESTINATION_TRANSPORT_PORT = "dstPort";
+    private static final String SOURCE_HOST_NAME = "srcName";
+    private static final String DESTINATION_HOST_NAME = "dstName";
+    private static final String IP_PROTOCOL = "ipProto";
+    private static final String VTAP_TYPE = "vtapType";
+    private static final String IP_PROTOCOL_LIST = "ipProtoList";
+    private static final String VTAP_TYPE_LIST = "vtapTypeList";
+
+    private static final String RESULT = "result";
+    private static final String VALUE = "value";
+    private static final String SUCCESS = "Success";
+    private static final String FAILED = "Failed";
+    private static final String INVALID_VTAP_TYPE = "Invalid vtap type";
+    private static final String FAILED_TO_CREATE_VTAP = "Failed to create OpenstackVtap";
+    private static final String WRONG_IP_ADDRESS =
+            "Inputted valid source & destination IP in CIDR (e.g., \"10.1.0.4/32\")";
+    private static final String INVALID_TRANSPORT_PORT =
+            "Invalid source & destination transport port has been entered";
+
+    private static final String[] IP_PROTOCOL_ARRAY = {"Any", "TCP", "UDP", "ICMP"};
+    private static final String[] VTAP_TYPE_ARRAY = {"All", "RX", "TX"};
+
+    private HostService hostService;
+    private OpenstackVtapAdminService vtapService;
+
+    @Override
+    public void init(UiConnection connection, ServiceDirectory directory) {
+        super.init(connection, directory);
+
+        hostService = directory.get(HostService.class);
+        vtapService = directory.get(OpenstackVtapAdminService.class);
+    }
+
+    @Override
+    protected Collection<RequestHandler> createRequestHandlers() {
+        return ImmutableSet.of(
+                new VtapIsActivatedRequestHandler(),
+                new VtapCreateRequestHandler()
+        );
+
+    }
+
+    private final class VtapIsActivatedRequestHandler extends RequestHandler {
+
+        private VtapIsActivatedRequestHandler() {
+            super(OSV_IS_ACTIVATED_REQ);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            String srcId = string(payload, SOURCE, null);
+            String dstId = string(payload, DESTINATION, null);
+
+            if (srcId != null && dstId != null) {
+                HostId sHostId = hostId(srcId);
+                HostId dHostId = hostId(dstId);
+
+                Host sHost = hostService.getHost(sHostId);
+                Host dHost = hostService.getHost(dHostId);
+
+                if (sHost != null && dHost != null) {
+                    ArrayNode ipProtos = arrayNode();
+                    ArrayNode types = arrayNode();
+                    String sHostName = ipForHost(sHost);
+                    String dHostName = ipForHost(dHost);
+
+                    for (String proto : IP_PROTOCOL_ARRAY) {
+                        ipProtos.add(proto);
+                    }
+
+                    for (String type : VTAP_TYPE_ARRAY) {
+                        types.add(type);
+                    }
+
+                    payload.put(SOURCE_HOST_NAME, sHostName);
+                    payload.put(DESTINATION_HOST_NAME, dHostName);
+                    payload.put(IP_PROTOCOL_LIST, ipProtos);
+                    payload.put(VTAP_TYPE_LIST, types);
+
+                    sendMessage(OSV_IS_ACTIVATED_RESP, payload);
+                }
+            }
+        }
+
+        // Returns the first of the given host's set of IP addresses as a string.
+        private String ipForHost(Host host) {
+            Set<IpAddress> ipAddresses = host.ipAddresses();
+            Iterator<IpAddress> it = ipAddresses.iterator();
+            return it.hasNext() ? it.next().toString() + "/32" : "unknown";
+        }
+    }
+
+    private final class VtapCreateRequestHandler extends RequestHandler {
+        private String srcIp;
+        private String dstIp;
+        private String ipProto;
+        private String srcTpPort;
+        private String dstTpPort;
+        private String vtapTypeStr;
+        private ObjectNode result = objectNode();
+
+        private VtapCreateRequestHandler() {
+            super(OSV_CREATE_REQ);
+        }
+
+        @Override
+        public void process(ObjectNode payload) {
+            srcIp = string(payload, SOURCE_IP, null);
+            dstIp = string(payload, DESTINATION_IP, null);
+            ipProto = string(payload, IP_PROTOCOL, null);
+            srcTpPort = string(payload, SOURCE_TRANSPORT_PORT, null);
+            dstTpPort = string(payload, DESTINATION_TRANSPORT_PORT, null);
+            vtapTypeStr = string(payload, VTAP_TYPE, null);
+            log.trace("VtapCreateRequestHandler payload srcIp:{}, dstIp:{}, ipPro:{}, " +
+                            "srcTpPort:{}, dstTpPort:{}, vtapType:{}", srcIp, dstIp, ipProto,
+                    srcTpPort, dstTpPort, vtapTypeStr);
+
+            DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder = DefaultOpenstackVtapCriterion.builder();
+            if (makeCriterion(vtapCriterionBuilder)) {
+                OpenstackVtap.Type type = getVtapTypeFromString(vtapTypeStr.toLowerCase());
+                if (type == null) {
+                    log.warn(INVALID_VTAP_TYPE);
+                    result.put(RESULT, FAILED);
+                    result.put(VALUE, INVALID_VTAP_TYPE);
+                    sendMessage(OSV_CREATE_RESP, result);
+                    return;
+                }
+
+                OpenstackVtap vtap = vtapService.createVtap(type, vtapCriterionBuilder.build());
+                if (vtap != null) {
+                    log.info("Created OpenstackVtap with id {}", vtap.id().toString());
+                    result.put(RESULT, SUCCESS);
+                    result.put(VALUE, "vtap id: " + vtap.id().toString());
+                } else {
+                    log.warn(FAILED_TO_CREATE_VTAP);
+                    result.put(RESULT, FAILED);
+                    result.put(VALUE, FAILED_TO_CREATE_VTAP);
+                }
+            }
+            sendMessage(OSV_CREATE_RESP, result);
+        }
+
+        private boolean makeCriterion(DefaultOpenstackVtapCriterion.Builder vtapCriterionBuilder) {
+            try {
+                vtapCriterionBuilder.srcIpPrefix(IpPrefix.valueOf(srcIp));
+                vtapCriterionBuilder.dstIpPrefix(IpPrefix.valueOf(dstIp));
+            } catch (Exception e) {
+                log.warn(WRONG_IP_ADDRESS);
+                result.put(RESULT, FAILED);
+                result.put(VALUE, WRONG_IP_ADDRESS);
+                return false;
+            }
+
+            vtapCriterionBuilder.ipProtocol(getProtocolTypeFromString(ipProto.toLowerCase()));
+
+            try {
+                vtapCriterionBuilder.srcTpPort(TpPort.tpPort(Integer.valueOf(srcTpPort)));
+                vtapCriterionBuilder.dstTpPort(TpPort.tpPort(Integer.valueOf(dstTpPort)));
+            } catch (Exception e) {
+                log.warn(INVALID_TRANSPORT_PORT);
+                result.put(RESULT, FAILED);
+                result.put(VALUE, INVALID_TRANSPORT_PORT);
+                return false;
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/package-info.java
new file mode 100644
index 0000000..7521561
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/gui/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2018-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 GUI for the control plane manager.
+ */
+package org.onosproject.openstackvtap.gui;
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java
index d5c842f..cc258c0 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtap.java
@@ -15,9 +15,7 @@
  */
 package org.onosproject.openstackvtap.impl;
 
-import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
 import org.onosproject.net.AbstractDescription;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.DeviceId;
@@ -26,33 +24,47 @@
 import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
 import org.onosproject.openstackvtap.api.OpenstackVtapId;
 
+import java.util.Objects;
 import java.util.Set;
 
 import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Default implementation of an immutable openstack vTap.
+ * Default implementation of an immutable OpenstackVtap.
  */
 public final class DefaultOpenstackVtap extends AbstractDescription implements OpenstackVtap {
 
     private final OpenstackVtapId id;
     private final Type type;
-    private final OpenstackVtapCriterion vTapCriterion;
+    private final OpenstackVtapCriterion vtapCriterion;
     private final Set<DeviceId> txDeviceIds;
     private final Set<DeviceId> rxDeviceIds;
 
-    // private constructor not intended to use from external
-    private DefaultOpenstackVtap(OpenstackVtapId id, Type type,
-                                 OpenstackVtapCriterion vTapCriterion,
-                                 Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
+    /**
+     * Creates an DefaultOpenstackVtap using the supplied information.
+     *
+     * @param id            vtap identifier
+     * @param type          type of vtap (all,rx,tx)
+     * @param vtapCriterion criterion of vtap
+     * @param txDeviceIds   device identifiers applied by vtap tx
+     * @param rxDeviceIds   device identifiers applied by vtap rx
+     * @param annotations   optional key/value annotations
+     */
+    private DefaultOpenstackVtap(OpenstackVtapId id,
+                                 Type type,
+                                 OpenstackVtapCriterion vtapCriterion,
+                                 Set<DeviceId> txDeviceIds,
+                                 Set<DeviceId> rxDeviceIds,
                                  SparseAnnotations... annotations) {
         super(annotations);
-        this.id = id;
-        this.type = type;
-        this.vTapCriterion = vTapCriterion;
-        this.txDeviceIds = txDeviceIds;
-        this.rxDeviceIds = rxDeviceIds;
+        this.id = checkNotNull(id);
+        this.type = checkNotNull(type);
+        this.vtapCriterion = checkNotNull(vtapCriterion);
+        this.txDeviceIds = Objects.nonNull(txDeviceIds) ?
+                ImmutableSet.copyOf(txDeviceIds) : ImmutableSet.of();
+        this.rxDeviceIds = Objects.nonNull(rxDeviceIds) ?
+                ImmutableSet.copyOf(rxDeviceIds) : ImmutableSet.of();
     }
 
     @Override
@@ -66,129 +78,184 @@
     }
 
     @Override
-    public OpenstackVtapCriterion vTapCriterion() {
-        return vTapCriterion;
+    public OpenstackVtapCriterion vtapCriterion() {
+        return vtapCriterion;
     }
 
     @Override
     public Set<DeviceId> txDeviceIds() {
-        return ImmutableSet.copyOf(txDeviceIds);
+        return txDeviceIds;
     }
 
     @Override
     public Set<DeviceId> rxDeviceIds() {
-        return ImmutableSet.copyOf(rxDeviceIds);
+        return rxDeviceIds;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, type, vtapCriterion, txDeviceIds, rxDeviceIds);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultOpenstackVtap) {
+            final DefaultOpenstackVtap other = (DefaultOpenstackVtap) obj;
+            return Objects.equals(this.id, other.id) &&
+                    Objects.equals(this.type, other.type) &&
+                    Objects.equals(this.vtapCriterion, other.vtapCriterion) &&
+                    Objects.equals(this.txDeviceIds(), other.txDeviceIds()) &&
+                    Objects.equals(this.rxDeviceIds(), other.rxDeviceIds()) &&
+                    Objects.equals(this.annotations(), other.annotations());
+        }
+        return false;
     }
 
     @Override
     public String toString() {
         return toStringHelper(this)
-                .add("id", id)
-                .add("type", type)
-                .add("vTapCriterion", vTapCriterion)
-                .add("txDeviceIds", txDeviceIds)
-                .add("rxDeviceIds", rxDeviceIds)
+                .add("id", id())
+                .add("type", type())
+                .add("vtapCriterion", vtapCriterion())
+                .add("txDeviceIds", txDeviceIds())
+                .add("rxDeviceIds", rxDeviceIds())
+                .add("annotations", annotations())
                 .toString();
     }
 
-    @Override
-    public int hashCode() {
-        return Objects.hashCode(id, type, vTapCriterion, txDeviceIds, rxDeviceIds);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-
-        DefaultOpenstackVtap that = (DefaultOpenstackVtap) o;
-        return Objects.equal(this.id, that.id)
-                    && Objects.equal(this.type, that.type)
-                    && Objects.equal(this.vTapCriterion, that.vTapCriterion)
-                    && Objects.equal(this.txDeviceIds, that.txDeviceIds)
-                    && Objects.equal(this.rxDeviceIds, that.rxDeviceIds);
-    }
-
     /**
-     * Creates a new default openstack vTap builder.
+     * Creates OpenstackVtap builder with default parameters.
      *
-     * @return default openstack vTap builder
+     * @return builder
      */
     public static Builder builder() {
         return new Builder();
     }
 
     /**
+     * Creates OpenstackVtap builder inheriting with default parameters,
+     * from specified OpenstackVtap.
+     *
+     * @param vtap to inherit default from
+     * @return builder
+     */
+    public static Builder builder(OpenstackVtap vtap) {
+        return new Builder(vtap);
+    }
+
+    /**
      * Builder for DefaultOpenstackVtap object.
      */
     public static class Builder implements OpenstackVtap.Builder {
-        private static final SparseAnnotations EMPTY = DefaultAnnotations.builder().build();
-
         private OpenstackVtapId id;
-        private Type type;
-        private OpenstackVtapCriterion vTapCriterion;
+        private Type type = Type.VTAP_ALL;
+        private OpenstackVtapCriterion vtapCriterion;
         private Set<DeviceId> txDeviceIds;
         private Set<DeviceId> rxDeviceIds;
-        private SparseAnnotations annotations = EMPTY;
+        private SparseAnnotations annotations = DefaultAnnotations.EMPTY;
 
-        // private constructor not intended to use from external
+        // Private constructor not intended to use from external
         Builder() {
         }
 
+        Builder(OpenstackVtap description) {
+            this.id = description.id();
+            this.type = description.type();
+            this.vtapCriterion = description.vtapCriterion();
+            this.type = description.type();
+            this.txDeviceIds = description.txDeviceIds();
+            this.rxDeviceIds = description.rxDeviceIds();
+            this.annotations  = (SparseAnnotations) description.annotations();
+        }
+
+        /**
+         * Sets mandatory field id.
+         *
+         * @param id to set
+         * @return self
+         */
         @Override
         public Builder id(OpenstackVtapId id) {
             this.id = id;
             return this;
         }
 
+        /**
+         * Sets mandatory field type.
+         *
+         * @param type of the vtap
+         * @return self
+         */
         @Override
         public Builder type(Type type) {
             this.type = type;
             return this;
         }
 
+        /**
+         * Sets mandatory field criterion.
+         *
+         * @param vtapCriterion for the vtap
+         * @return self
+         */
         @Override
-        public Builder vTapCriterion(OpenstackVtapCriterion vTapCriterion) {
-            this.vTapCriterion = vTapCriterion;
+        public Builder vtapCriterion(OpenstackVtapCriterion vtapCriterion) {
+            this.vtapCriterion = vtapCriterion;
             return this;
         }
 
+        /**
+         * Sets a tx deviceId set.
+         *
+         * @param txDeviceIds deviceId set for tx
+         * @return builder
+         */
         @Override
         public Builder txDeviceIds(Set<DeviceId> txDeviceIds) {
-            if (txDeviceIds != null) {
-                this.txDeviceIds = ImmutableSet.copyOf(txDeviceIds);
-            } else {
-                this.txDeviceIds = Sets.newHashSet();
-            }
+            this.txDeviceIds = txDeviceIds;
             return this;
         }
 
+        /**
+         * Sets a rx deviceId set.
+         *
+         * @param rxDeviceIds deviceId set for rx
+         * @return builder
+         */
         @Override
         public Builder rxDeviceIds(Set<DeviceId> rxDeviceIds) {
-            if (rxDeviceIds != null) {
-                this.rxDeviceIds = ImmutableSet.copyOf(rxDeviceIds);
-            } else {
-                this.rxDeviceIds = Sets.newHashSet();
-            }
+            this.rxDeviceIds = rxDeviceIds;
             return this;
         }
 
+        /**
+         * Sets annotations.
+         *
+         * @param annotations of the vtap
+         * @return self
+         */
         @Override
-        public Builder annotations(SparseAnnotations... annotations) {
-            checkArgument(annotations.length <= 1,
-                    "Only one set of annotations is expected");
-            this.annotations = annotations.length == 1 ? annotations[0] : EMPTY;
+        public Builder annotations(SparseAnnotations annotations) {
+            this.annotations = annotations;
             return this;
         }
 
+        /**
+         * Builds a DefaultOpenstackVtap instance.
+         *
+         * @return DefaultOpenstackVtap
+         */
         @Override
         public DefaultOpenstackVtap build() {
-            return new DefaultOpenstackVtap(id, type, vTapCriterion,
-                                            txDeviceIds, rxDeviceIds, annotations);
+            return new DefaultOpenstackVtap(checkNotNull(id),
+                    checkNotNull(type),
+                    checkNotNull(vtapCriterion),
+                    txDeviceIds,
+                    rxDeviceIds,
+                    checkNotNull(annotations));
         }
     }
 
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java
index d53bf25..287f763 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapCriterion.java
@@ -24,16 +24,15 @@
 import static com.google.common.base.Preconditions.checkArgument;
 
 /**
- * Default implementation of an immutable openstack vTap criterion.
+ * Default implementation of an immutable openstack vtap criterion.
  */
 public final class DefaultOpenstackVtapCriterion implements OpenstackVtapCriterion {
+
     private final IpPrefix srcIpPrefix;
     private final IpPrefix dstIpPrefix;
-    private final byte     ipProtocol;
+    private final byte ipProtocol;
     private final TpPort srcTpPort;
-    private final TpPort   dstTpPort;
-
-    private static final String NOT_NULL_MSG = "Element % cannot be null";
+    private final TpPort dstTpPort;
 
     // private constructor not intended to use from external
     private DefaultOpenstackVtapCriterion(IpPrefix srcIpPrefix,
@@ -107,18 +106,20 @@
     }
 
     /**
-     * Creates a new default openstack vTap criterion builder.
+     * Creates a new default openstack vtap criterion builder.
      *
-     * @return default openstack vTap criterion builder
+     * @return default openstack vtap criterion builder
      */
     public static Builder builder() {
         return new Builder();
     }
 
     /**
-     * A builder class for openstack vTap criterion builder.
+     * A builder class for openstack vtap criterion builder.
      */
     public static final class Builder implements OpenstackVtapCriterion.Builder {
+        private static final String NOT_NULL_MSG = "OpenstackVtapCriterion % cannot be null";
+
         private IpPrefix srcIpPrefix;
         private IpPrefix dstIpPrefix;
         private byte     ipProtocol;
@@ -168,4 +169,5 @@
             return this;
         }
     }
+
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapNetwork.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapNetwork.java
new file mode 100644
index 0000000..af086a2
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapNetwork.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2018-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.openstackvtap.impl;
+
+import org.onlab.packet.IpAddress;
+import org.onosproject.net.AbstractDescription;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of an immutable OpenstackVtapNetwork.
+ */
+public class DefaultOpenstackVtapNetwork extends AbstractDescription implements OpenstackVtapNetwork {
+
+    private final Mode mode;
+    private final Integer networkId;
+    private final IpAddress serverIp;
+
+    /**
+     * Creates an DefaultOpenstackVtapNetwork using the supplied information.
+     *
+     * @param mode        mode of vtap network
+     * @param networkId   network id of the vtap tunneling network
+     * @param serverIp    server IP address used for tunneling
+     * @param annotations optional key/value annotations
+     */
+    protected DefaultOpenstackVtapNetwork(Mode mode,
+                                          Integer networkId,
+                                          IpAddress serverIp,
+                                          SparseAnnotations... annotations) {
+        super(annotations);
+        this.mode = checkNotNull(mode);
+        this.networkId = networkId;
+        this.serverIp = checkNotNull(serverIp);
+    }
+
+    @Override
+    public Mode mode() {
+        return mode;
+    }
+
+    @Override
+    public Integer networkId() {
+        return networkId;
+    }
+
+    @Override
+    public IpAddress serverIp() {
+        return serverIp;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mode, networkId, serverIp);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof DefaultOpenstackVtapNetwork) {
+            final DefaultOpenstackVtapNetwork other = (DefaultOpenstackVtapNetwork) obj;
+            return Objects.equals(this.mode, other.mode) &&
+                    Objects.equals(this.networkId, other.networkId) &&
+                    Objects.equals(this.serverIp, other.serverIp) &&
+                    Objects.equals(this.annotations(), other.annotations());
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("mode", mode())
+                .add("networkId", networkId())
+                .add("serverIp", serverIp())
+                .add("annotations", annotations())
+                .toString();
+    }
+
+    /**
+     * Creates OpenstackVtapNetwork builder with default parameters.
+     *
+     * @return builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Creates OpenstackVtapNetwork builder inheriting with default parameters,
+     * from specified OpenstackVtapNetwork.
+     *
+     * @param vtapNetwork to inherit default from
+     * @return builder
+     */
+    public static Builder builder(OpenstackVtapNetwork vtapNetwork) {
+        return new Builder(vtapNetwork);
+    }
+
+    /**
+     * Builder for DefaultOpenstackVtapNetwork object.
+     */
+    public static class Builder implements OpenstackVtapNetwork.Builder {
+        private Mode mode;
+        private Integer networkId;
+        private IpAddress serverIp;
+        private SparseAnnotations annotations = DefaultAnnotations.EMPTY;
+
+        // private constructor not intended to use from external
+        private Builder() {
+        }
+
+        Builder(OpenstackVtapNetwork description) {
+            this.mode = description.mode();
+            this.networkId = description.networkId();
+            this.serverIp = description.serverIp();
+            this.annotations  = (SparseAnnotations) description.annotations();
+        }
+
+        /**
+         * Sets mandatory field mode.
+         *
+         * @param mode of vtap network
+         * @return self
+         */
+        @Override
+        public Builder mode(Mode mode) {
+            this.mode = mode;
+            return this;
+        }
+
+        /**
+         * Sets mandatory field networkId.
+         *
+         * @param networkId of the vtap tunneling network
+         * @return self
+         */
+        @Override
+        public Builder networkId(Integer networkId) {
+            this.networkId = networkId;
+            return this;
+        }
+
+        /**
+         * Sets mandatory field serverIp.
+         *
+         * @param serverIp address used for tunneling
+         * @return self
+         */
+        @Override
+        public Builder serverIp(IpAddress serverIp) {
+            this.serverIp = serverIp;
+            return this;
+        }
+
+        /**
+         * Sets annotations.
+         *
+         * @param annotations of the vtap network
+         * @return self
+         */
+        @Override
+        public Builder annotations(SparseAnnotations annotations) {
+            this.annotations = annotations;
+            return this;
+        }
+
+        /**
+         * Builds a DefaultOpenstackVtapNetwork instance.
+         *
+         * @return DefaultOpenstackVtapNetwork
+         */
+        @Override
+        public DefaultOpenstackVtapNetwork build() {
+            return new DefaultOpenstackVtapNetwork(checkNotNull(mode),
+                    networkId,
+                    checkNotNull(serverIp),
+                    checkNotNull(annotations));
+        }
+    }
+
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java
index 141af0b..9f2ee12 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/DistributedOpenstackVtapStore.java
@@ -29,9 +29,10 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.SparseAnnotations;
 import org.onosproject.openstackvtap.api.OpenstackVtap;
-import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
+import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
 import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
 import org.onosproject.openstackvtap.api.OpenstackVtapId;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
 import org.onosproject.openstackvtap.api.OpenstackVtapStore;
 import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
 import org.onosproject.store.AbstractStore;
@@ -42,6 +43,7 @@
 import org.onosproject.store.service.MapEventListener;
 import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
 import org.slf4j.Logger;
 
 import java.util.Comparator;
@@ -53,465 +55,570 @@
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.net.DefaultAnnotations.merge;
+import static org.onosproject.store.service.Versioned.valueOrNull;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Manages the inventory of users using a {@code ConsistentMap}.
+ * Manages the inventory of openstack vtap and openstack vtap network using a {@code ConsistentMap}.
  */
 @Component(immediate = true)
 @Service
 public class DistributedOpenstackVtapStore
         extends AbstractStore<OpenstackVtapEvent, OpenstackVtapStoreDelegate>
         implements OpenstackVtapStore {
+
     private final Logger log = getLogger(getClass());
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected StorageService storageService;
 
-    private ConsistentMap<OpenstackVtapId, DefaultOpenstackVtap> vTapConsistentMap;
-    private MapEventListener<OpenstackVtapId, DefaultOpenstackVtap>
-                                            vTapListener = new VtapEventListener();
-    private Map<OpenstackVtapId, DefaultOpenstackVtap> vTapMap;
+    private ConsistentMap<OpenstackVtapId, DefaultOpenstackVtap> vtapConsistentMap;
+    private MapEventListener<OpenstackVtapId, DefaultOpenstackVtap> vtapListener =
+            new VtapEventListener();
+    private Map<OpenstackVtapId, DefaultOpenstackVtap> vtapMap;
+
+    private ConsistentMap<Integer, DefaultOpenstackVtapNetwork> vtapNetworkConsistentMap;
+    private MapEventListener<Integer, DefaultOpenstackVtapNetwork> vtapNetworkListener =
+            new VtapNetworkEventListener();
+    private Map<Integer, DefaultOpenstackVtapNetwork> vtapNetworkMap;
+
+    private ConsistentMap<Integer, Set<DeviceId>> vtapNetworkDevicesConsistentMap;
 
     private static final Serializer SERIALIZER = Serializer
             .using(new KryoNamespace.Builder().register(KryoNamespaces.API)
                     .register(OpenstackVtapId.class)
                     .register(UUID.class)
                     .register(DefaultOpenstackVtap.class)
-                    .register(OpenstackVtap.Type.class)
+                    .register(Type.class)
                     .register(DefaultOpenstackVtapCriterion.class)
+                    .register(DefaultOpenstackVtapNetwork.class)
+                    .register(OpenstackVtapNetwork.Mode.class)
                     .nextId(KryoNamespaces.BEGIN_USER_CUSTOM_ID)
                     .build());
 
     private Map<DeviceId, Set<OpenstackVtapId>>
-                                    vTapIdsByTxDeviceId = Maps.newConcurrentMap();
+                                    vtapIdsByTxDeviceId = Maps.newConcurrentMap();
     private Map<DeviceId, Set<OpenstackVtapId>>
-                                    vTapIdsByRxDeviceId = Maps.newConcurrentMap();
+                                    vtapIdsByRxDeviceId = Maps.newConcurrentMap();
 
     private ScheduledExecutorService eventExecutor;
+    private Consumer<Status> vtapStatusListener;
 
-    private Consumer<Status> vTapStatusListener;
-
-    public static final String INVALID_DESCRIPTION = "Invalid create/update parameter";
+    private static final String ERR_NOT_FOUND = "ID {} does not exist";
+    private static final String ERR_DUPLICATE = "ID {} already exists";
 
     @Activate
     public void activate() {
         eventExecutor = newSingleThreadScheduledExecutor(
                 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
 
-        vTapConsistentMap = storageService.<OpenstackVtapId, DefaultOpenstackVtap>
+        // vtap network data
+        vtapNetworkConsistentMap = storageService.<Integer, DefaultOpenstackVtapNetwork>
                 consistentMapBuilder()
-                .withName("vTapMap")
+                .withName("vtapNetworkMap")
+                .withSerializer(SERIALIZER)
+                .build();
+        vtapNetworkMap = vtapNetworkConsistentMap.asJavaMap();
+        vtapNetworkConsistentMap.addListener(vtapNetworkListener);
+
+        // vtap network devices data
+        vtapNetworkDevicesConsistentMap = storageService.<Integer, Set<DeviceId>>
+                consistentMapBuilder()
+                .withName("vtapNetworkDevicesMap")
                 .withSerializer(SERIALIZER)
                 .build();
 
-        vTapMap = vTapConsistentMap.asJavaMap();
-        vTapConsistentMap.addListener(vTapListener);
+        // vtap data
+        vtapConsistentMap = storageService.<OpenstackVtapId, DefaultOpenstackVtap>
+                consistentMapBuilder()
+                .withName("vtapMap")
+                .withSerializer(SERIALIZER)
+                .build();
+        vtapMap = vtapConsistentMap.asJavaMap();
+        vtapConsistentMap.addListener(vtapListener);
 
-        vTapStatusListener = status -> {
+        // initialize vtap data
+        vtapStatusListener = status -> {
             if (status == Status.ACTIVE) {
                 eventExecutor.execute(this::loadVtapIds);
             }
         };
-        vTapConsistentMap.addStatusChangeListener(vTapStatusListener);
+        vtapConsistentMap.addStatusChangeListener(vtapStatusListener);
 
-        log.info("Started {} - {}", this.getClass().getSimpleName());
+        log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
-        vTapConsistentMap.removeStatusChangeListener(vTapStatusListener);
-        vTapConsistentMap.removeListener(vTapListener);
+        vtapConsistentMap.removeStatusChangeListener(vtapStatusListener);
+        vtapConsistentMap.removeListener(vtapListener);
+        vtapNetworkConsistentMap.removeListener(vtapNetworkListener);
+
         eventExecutor.shutdown();
 
-        log.info("Stopped {} - {}", this.getClass().getSimpleName());
+        log.info("Stopped");
     }
 
-    @Override
-    public OpenstackVtap createOrUpdateVtap(OpenstackVtapId vTapId,
-                                            OpenstackVtap description,
-                                            boolean replaceFlag) {
-
-        return vTapMap.compute(vTapId, (id, existing) -> {
-            if (existing == null &&
-                    (description.type() == null ||
-                     description.vTapCriterion() == null ||
-                     description.txDeviceIds() == null ||
-                     description.rxDeviceIds() == null)) {
-                checkState(false, INVALID_DESCRIPTION);
-                return null;
-            }
-
-            if (shouldUpdate(existing, description, replaceFlag)) {
-                // Replace items
-                OpenstackVtap.Type type =
-                        (description.type() == null ? existing.type() : description.type());
-                OpenstackVtapCriterion vTapCriterion =
-                        (description.vTapCriterion() == null ?
-                        existing.vTapCriterion() : description.vTapCriterion());
-
-                // Replace or add devices
-                Set<DeviceId> txDeviceIds;
-                if (description.txDeviceIds() == null) {
-                    txDeviceIds = existing.txDeviceIds();
-                } else {
-                    if (existing == null || replaceFlag) {
-                        txDeviceIds = ImmutableSet.copyOf(description.txDeviceIds());
-                    } else {
-                        txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
-                        txDeviceIds.addAll(description.txDeviceIds());
-                    }
-                }
-
-                Set<DeviceId> rxDeviceIds;
-                if (description.rxDeviceIds() == null) {
-                    rxDeviceIds = existing.rxDeviceIds();
-                } else {
-                    if (existing == null || replaceFlag) {
-                        rxDeviceIds = ImmutableSet.copyOf(description.rxDeviceIds());
-                    } else {
-                        rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
-                        rxDeviceIds.addAll(description.rxDeviceIds());
-                    }
-                }
-
-                // Replace or add annotations
-                SparseAnnotations annotations;
-                if (existing != null) {
-                    annotations = merge((DefaultAnnotations) existing.annotations(),
-                            (SparseAnnotations) description.annotations());
-                } else {
-                    annotations = (SparseAnnotations) description.annotations();
-                }
-
-                // Make new changed vTap and return
-                return DefaultOpenstackVtap.builder()
-                        .id(vTapId)
-                        .type(type)
-                        .vTapCriterion(vTapCriterion)
-                        .txDeviceIds(txDeviceIds)
-                        .rxDeviceIds(rxDeviceIds)
-                        .annotations(annotations)
-                        .build();
-            }
-            return existing;
-        });
-    }
-
-    @Override
-    public OpenstackVtap removeVtapById(OpenstackVtapId vTapId) {
-        return vTapMap.remove(vTapId);
-    }
-
-    @Override
-    public boolean addDeviceToVtap(OpenstackVtapId vTapId,
-                                   OpenstackVtap.Type type,
-                                   DeviceId deviceId) {
-        checkNotNull(vTapId);
-        checkNotNull(deviceId);
-
-        OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> {
-            if (existing == null) {
-                return null;
-            }
-            if (!existing.type().isValid(type)) {
-                log.error("Not valid OpenstackVtap type {} for requested type {}",
-                        existing.type(), type);
-                return existing;
-            }
-
-            Set<DeviceId> txDeviceIds = null;
-            if (type.isValid(OpenstackVtap.Type.VTAP_TX) &&
-                    !existing.txDeviceIds().contains(deviceId)) {
-                txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
-                txDeviceIds.add(deviceId);
-            }
-
-            Set<DeviceId> rxDeviceIds = null;
-            if (type.isValid(OpenstackVtap.Type.VTAP_RX) &&
-                    !existing.rxDeviceIds().contains(deviceId)) {
-                rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
-                rxDeviceIds.add(deviceId);
-            }
-
-            if (txDeviceIds != null || rxDeviceIds != null) {
-                //updateVTapIdFromDeviceId(existing.id(), deviceId);    // execute from event listener
-
-                return DefaultOpenstackVtap.builder()
-                        .id(vTapId)
-                        .type(existing.type())
-                        .vTapCriterion(existing.vTapCriterion())
-                        .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds())
-                        .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds())
-                        .annotations(existing.annotations())
-                        .build();
-            }
-            return existing;
-        });
-        return (vTap != null);
-    }
-
-    @Override
-    public boolean removeDeviceFromVtap(OpenstackVtapId vTapId,
-                                        OpenstackVtap.Type type,
-                                        DeviceId deviceId) {
-        checkNotNull(vTapId);
-        checkNotNull(deviceId);
-
-        OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> {
-            if (existing == null) {
-                return null;
-            }
-            if (!existing.type().isValid(type)) {
-                log.error("Not valid OpenstackVtap type {} for requested type {}",
-                        existing.type(), type);
-                return existing;
-            }
-
-            Set<DeviceId> txDeviceIds = null;
-            if (type.isValid(OpenstackVtap.Type.VTAP_TX) &&
-                    existing.txDeviceIds().contains(deviceId)) {
-                txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
-                txDeviceIds.remove(deviceId);
-            }
-
-            Set<DeviceId> rxDeviceIds = null;
-            if (type.isValid(OpenstackVtap.Type.VTAP_RX) &&
-                    existing.rxDeviceIds().contains(deviceId)) {
-                rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
-                rxDeviceIds.remove(deviceId);
-            }
-
-            if (txDeviceIds != null || rxDeviceIds != null) {
-                //removeVTapIdFromDeviceId(existing.id(), deviceId);    // execute from event listener
-
-                return DefaultOpenstackVtap.builder()
-                        .id(vTapId)
-                        .type(existing.type())
-                        .vTapCriterion(existing.vTapCriterion())
-                        .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds())
-                        .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds())
-                        .annotations(existing.annotations())
-                        .build();
-            }
-            return existing;
-        });
-        return (vTap != null);
-    }
-
-    @Override
-    public boolean updateDeviceForVtap(OpenstackVtapId vTapId,
-                                       Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
-                                       boolean replaceDevices) {
-        checkNotNull(vTapId);
-        checkNotNull(txDeviceIds);
-        checkNotNull(rxDeviceIds);
-
-        OpenstackVtap vTap = vTapMap.compute(vTapId, (id, existing) -> {
-            if (existing == null) {
-                return null;
-            }
-
-            // Replace or add devices
-            Set<DeviceId> txDS = null;
-            if (replaceDevices) {
-                if (!existing.txDeviceIds().equals(txDeviceIds)) {
-                    txDS = ImmutableSet.copyOf(txDeviceIds);
-                }
-            } else {
-                if (!existing.txDeviceIds().containsAll(txDeviceIds)) {
-                    txDS = Sets.newHashSet(existing.txDeviceIds());
-                    txDS.addAll(txDeviceIds);
-                }
-            }
-
-            Set<DeviceId> rxDS = null;
-            if (replaceDevices) {
-                if (!existing.rxDeviceIds().equals(rxDeviceIds)) {
-                    rxDS = ImmutableSet.copyOf(rxDeviceIds);
-                }
-            } else {
-                if (!existing.rxDeviceIds().containsAll(rxDeviceIds)) {
-                    rxDS = Sets.newHashSet(existing.rxDeviceIds());
-                    rxDS.addAll(rxDeviceIds);
-                }
-            }
-
-            if (txDS != null || rxDS != null) {
-
-                return DefaultOpenstackVtap.builder()
-                        .id(vTapId)
-                        .type(existing.type())
-                        .vTapCriterion(existing.vTapCriterion())
-                        .txDeviceIds(txDS != null ? txDS : existing.txDeviceIds())
-                        .rxDeviceIds(rxDS != null ? rxDS : existing.rxDeviceIds())
-                        .annotations(existing.annotations())
-                        .build();
-            }
-            return existing;
-        });
-        return (vTap != null);
-    }
-
-    @Override
-    public int getVtapCount(OpenstackVtap.Type type) {
-        return (int) vTapMap.values().parallelStream()
-                .filter(vTap -> vTap.type().isValid(type))
-                .count();
-    }
-
-    @Override
-    public Set<OpenstackVtap> getVtaps(OpenstackVtap.Type type) {
-        return ImmutableSet.copyOf(
-                vTapMap.values().parallelStream()
-                        .filter(vTap -> vTap.type().isValid(type))
-                        .collect(Collectors.toSet()));
-    }
-
-    @Override
-    public OpenstackVtap getVtap(OpenstackVtapId vTapId) {
-        return vTapMap.get(vTapId);
-    }
-
-    @Override
-    public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type,
-                                                 DeviceId deviceId) {
-        Set<OpenstackVtapId> vtapIds = Sets.newHashSet();
-        if (type.isValid(OpenstackVtap.Type.VTAP_TX)) {
-            if (vTapIdsByTxDeviceId.get(deviceId) != null) {
-                vtapIds.addAll(vTapIdsByTxDeviceId.get(deviceId));
-            }
-        }
-        if (type.isValid(OpenstackVtap.Type.VTAP_RX)) {
-            if (vTapIdsByRxDeviceId.get(deviceId) != null) {
-                vtapIds.addAll(vTapIdsByRxDeviceId.get(deviceId));
-            }
-        }
-
-        return ImmutableSet.copyOf(
-                vtapIds.parallelStream()
-                        .map(vTapId -> vTapMap.get(vTapId))
-                        .filter(Objects::nonNull)
-                        .collect(Collectors.toSet()));
-    }
-
-    private void loadVtapIds() {
-        vTapIdsByTxDeviceId.clear();
-        vTapIdsByRxDeviceId.clear();
-        vTapMap.values().forEach(vTap -> refreshDeviceIdsByVtap(null, vTap));
-    }
-
-    private boolean shouldUpdate(DefaultOpenstackVtap existing,
-                                 OpenstackVtap description,
-                                 boolean replaceDevices) {
+    private boolean shouldUpdateVtapNetwork(DefaultOpenstackVtapNetwork existing,
+                                            OpenstackVtapNetwork description) {
         if (existing == null) {
             return true;
         }
 
-        if ((description.type() != null && !description.type().equals(existing.type()))
-                || (description.vTapCriterion() != null &&
-                !description.vTapCriterion().equals(existing.vTapCriterion()))) {
+        if (!Objects.equals(existing.mode(), description.mode()) ||
+                !Objects.equals(existing.networkId(), description.networkId()) ||
+                !Objects.equals(existing.serverIp(), description.serverIp())) {
             return true;
         }
 
-        if (description.txDeviceIds() != null) {
-            if (replaceDevices) {
-                if (!existing.txDeviceIds().equals(description.txDeviceIds())) {
-                    return true;
-                }
-            } else {
-                if (!existing.txDeviceIds().containsAll(description.txDeviceIds())) {
-                    return true;
-                }
-            }
-        }
-
-        if (description.rxDeviceIds() != null) {
-            if (replaceDevices) {
-                if (!existing.rxDeviceIds().equals(description.rxDeviceIds())) {
-                    return true;
-                }
-            } else {
-                if (!existing.rxDeviceIds().containsAll(description.rxDeviceIds())) {
-                    return true;
-                }
-            }
-        }
-
-        // check to see if any of the annotations provided by vTap
-        // differ from those in the existing vTap
+        // check to see if any of the annotations provided by description
+        // differ from those in the existing vtap network
         return description.annotations().keys().stream()
                 .anyMatch(k -> !Objects.equals(description.annotations().value(k),
                         existing.annotations().value(k)));
     }
 
+    private OpenstackVtapNetwork createOrUpdateVtapNetwork(boolean update,
+                                                           Integer key,
+                                                           OpenstackVtapNetwork description) {
+        DefaultOpenstackVtapNetwork result =
+                vtapNetworkMap.compute(key, (id, existing) -> {
+                    // Check create or update validity
+                    if (update && existing == null) {
+                        return null;
+                    } else if (!update && existing != null) {
+                        return existing;
+                    }
+
+                    if (shouldUpdateVtapNetwork(existing, description)) {
+                        // Replace or add annotations
+                        final SparseAnnotations annotations;
+                        if (existing != null) {
+                            annotations = merge((DefaultAnnotations) existing.annotations(),
+                                    (SparseAnnotations) description.annotations());
+                        } else {
+                            annotations = (SparseAnnotations) description.annotations();
+                        }
+
+                        return DefaultOpenstackVtapNetwork.builder(description)
+                                .annotations(annotations)
+                                .build();
+                    }
+                    return existing;
+                });
+        return result;
+    }
+
+    @Override
+    public OpenstackVtapNetwork createVtapNetwork(Integer key, OpenstackVtapNetwork description) {
+        if (getVtapNetwork(key) == null) {
+            OpenstackVtapNetwork vtapNetwork = createOrUpdateVtapNetwork(false, key, description);
+            if (Objects.equals(vtapNetwork, description)) {
+                return vtapNetwork;
+            }
+        }
+        log.error(ERR_DUPLICATE, key);
+        return null;
+    }
+
+    @Override
+    public OpenstackVtapNetwork updateVtapNetwork(Integer key, OpenstackVtapNetwork description) {
+        OpenstackVtapNetwork vtapNetwork = createOrUpdateVtapNetwork(true, key, description);
+        if (vtapNetwork == null) {
+            log.error(ERR_NOT_FOUND, key);
+        }
+        return vtapNetwork;
+    }
+
+    @Override
+    public OpenstackVtapNetwork removeVtapNetwork(Integer key) {
+        return vtapNetworkMap.remove(key);
+    }
+
+    @Override
+    public void clearVtapNetworks() {
+        vtapNetworkMap.clear();
+    }
+
+    @Override
+    public int getVtapNetworkCount() {
+        return vtapNetworkMap.size();
+    }
+
+    @Override
+    public OpenstackVtapNetwork getVtapNetwork(Integer key) {
+        return vtapNetworkMap.get(key);
+    }
+
+    @Override
+    public boolean addDeviceToVtapNetwork(Integer key, DeviceId deviceId) {
+        Versioned<Set<DeviceId>> result =
+                vtapNetworkDevicesConsistentMap.compute(key, (id, existing) -> {
+                    // Add deviceId to deviceIds
+                    if (existing == null) {
+                        return Sets.newHashSet(deviceId);
+                    } else if (!existing.contains(deviceId)) {
+                        Set<DeviceId> deviceIds = Sets.newHashSet(existing);
+                        deviceIds.add(deviceId);
+                        return deviceIds;
+                    } else {
+                        return existing;
+                    }
+                });
+        return Objects.nonNull(valueOrNull(result));
+    }
+
+    @Override
+    public boolean removeDeviceFromVtapNetwork(Integer key, DeviceId deviceId) {
+        Versioned<Set<DeviceId>> result =
+                vtapNetworkDevicesConsistentMap.compute(key, (id, existing) -> {
+                    // Remove deviceId from deviceIds
+                    if (existing != null && existing.contains(deviceId)) {
+                        Set<DeviceId> deviceIds = Sets.newHashSet(existing);
+                        deviceIds.remove(deviceId);
+                        return deviceIds;
+                    } else {
+                        return existing;
+                    }
+                });
+        return Objects.nonNull(valueOrNull(result));
+    }
+
+    @Override
+    public Set<DeviceId> getVtapNetworkDevices(Integer key) {
+        return valueOrNull(vtapNetworkDevicesConsistentMap.get(key));
+    }
+
+    private boolean shouldUpdateVtap(DefaultOpenstackVtap existing,
+                                     OpenstackVtap description,
+                                     boolean replaceDevices) {
+        if (existing == null) {
+            return true;
+        }
+
+        if (!Objects.equals(existing.type(), description.type()) ||
+                !Objects.equals(existing.vtapCriterion(), description.vtapCriterion())) {
+            return true;
+        }
+
+        if (replaceDevices) {
+            if (!Objects.equals(description.txDeviceIds(), existing.txDeviceIds()) ||
+                    !Objects.equals(description.rxDeviceIds(), existing.rxDeviceIds())) {
+                return true;
+            }
+        } else {
+            if (!existing.txDeviceIds().containsAll(description.txDeviceIds()) ||
+                    !existing.rxDeviceIds().containsAll(description.rxDeviceIds())) {
+                return true;
+            }
+        }
+
+        // check to see if any of the annotations provided by description
+        // differ from those in the existing vtap
+        return description.annotations().keys().stream()
+                .anyMatch(k -> !Objects.equals(description.annotations().value(k),
+                        existing.annotations().value(k)));
+    }
+
+    private OpenstackVtap createOrUpdateVtap(boolean update,
+                                             OpenstackVtap description,
+                                             boolean replaceDevices) {
+        DefaultOpenstackVtap result =
+                vtapMap.compute(description.id(), (id, existing) -> {
+                    // Check create or update validity
+                    if (update && existing == null) {
+                        return null;
+                    } else if (!update && existing != null) {
+                        return existing;
+                    }
+
+                    if (shouldUpdateVtap(existing, description, replaceDevices)) {
+                        // Replace or add devices
+                        final Set<DeviceId> txDeviceIds;
+                        if (existing == null || replaceDevices) {
+                            txDeviceIds = description.txDeviceIds();
+                        } else {
+                            txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
+                            txDeviceIds.addAll(description.txDeviceIds());
+                        }
+
+                        final Set<DeviceId> rxDeviceIds;
+                        if (existing == null || replaceDevices) {
+                            rxDeviceIds = description.rxDeviceIds();
+                        } else {
+                            rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
+                            rxDeviceIds.addAll(description.rxDeviceIds());
+                        }
+
+                        // Replace or add annotations
+                        final SparseAnnotations annotations;
+                        if (existing != null) {
+                            annotations = merge((DefaultAnnotations) existing.annotations(),
+                                    (SparseAnnotations) description.annotations());
+                        } else {
+                            annotations = (SparseAnnotations) description.annotations();
+                        }
+
+                        return DefaultOpenstackVtap.builder(description)
+                                .txDeviceIds(txDeviceIds)
+                                .rxDeviceIds(rxDeviceIds)
+                                .annotations(annotations)
+                                .build();
+                    }
+                    return existing;
+                });
+        return result;
+    }
+
+    @Override
+    public OpenstackVtap createVtap(OpenstackVtap description) {
+        if (getVtap(description.id()) == null) {
+            OpenstackVtap vtap = createOrUpdateVtap(false, description, true);
+            if (Objects.equals(vtap, description)) {
+                return vtap;
+            }
+        }
+        log.error(ERR_DUPLICATE, description.id());
+        return null;
+    }
+
+    @Override
+    public OpenstackVtap updateVtap(OpenstackVtap description, boolean replaceDevices) {
+        OpenstackVtap vtap = createOrUpdateVtap(true, description, replaceDevices);
+        if (vtap == null) {
+            log.error(ERR_NOT_FOUND, description.id());
+        }
+        return vtap;
+    }
+
+    @Override
+    public OpenstackVtap removeVtap(OpenstackVtapId vtapId) {
+        return vtapMap.remove(vtapId);
+    }
+
+    @Override
+    public void clearVtaps() {
+        vtapMap.clear();
+    }
+
+    @Override
+    public int getVtapCount(Type type) {
+        return (int) vtapMap.values().parallelStream()
+                .filter(vtap -> vtap.type().isValid(type))
+                .count();
+    }
+
+    @Override
+    public Set<OpenstackVtap> getVtaps(Type type) {
+        return ImmutableSet.copyOf(
+                vtapMap.values().parallelStream()
+                        .filter(vtap -> vtap.type().isValid(type))
+                        .collect(Collectors.toSet()));
+    }
+
+    @Override
+    public OpenstackVtap getVtap(OpenstackVtapId vtapId) {
+        return vtapMap.get(vtapId);
+    }
+
+    @Override
+    public boolean addDeviceToVtap(OpenstackVtapId vtapId, Type type, DeviceId deviceId) {
+        OpenstackVtap result =
+                vtapMap.compute(vtapId, (id, existing) -> {
+                    if (existing == null) {
+                        return null;
+                    }
+
+                    // Check type validate
+                    if (!existing.type().isValid(type)) {
+                        log.error("Not valid OpenstackVtap type {} for requested type {}", existing.type(), type);
+                        return existing;
+                    }
+
+                    // Add deviceId to txDeviceIds
+                    final Set<DeviceId> txDeviceIds;
+                    if (existing.type().isValid(Type.VTAP_TX) &&
+                            (type.isValid(Type.VTAP_TX) || type == Type.VTAP_ANY) &&
+                            !existing.txDeviceIds().contains(deviceId)) {
+                        txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
+                        txDeviceIds.add(deviceId);
+                    } else {
+                        txDeviceIds = null;
+                    }
+
+                    // Add deviceId to rxDeviceIds
+                    final Set<DeviceId> rxDeviceIds;
+                    if (existing.type().isValid(Type.VTAP_RX) &&
+                            (type.isValid(Type.VTAP_RX) || type == Type.VTAP_ANY) &&
+                            !existing.rxDeviceIds().contains(deviceId)) {
+                        rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
+                        rxDeviceIds.add(deviceId);
+                    } else {
+                        rxDeviceIds = null;
+                    }
+
+                    if (txDeviceIds != null || rxDeviceIds != null) {
+                        return DefaultOpenstackVtap.builder()
+                                .id(id)
+                                .type(existing.type())
+                                .vtapCriterion(existing.vtapCriterion())
+                                .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds())
+                                .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds())
+                                .annotations(existing.annotations())
+                                .build();
+                    } else {
+                        return existing;
+                    }
+                });
+        return Objects.nonNull(result);
+    }
+
+    @Override
+    public boolean removeDeviceFromVtap(OpenstackVtapId vtapId, OpenstackVtap.Type type, DeviceId deviceId) {
+        OpenstackVtap result =
+                vtapMap.compute(vtapId, (id, existing) -> {
+                    if (existing == null) {
+                        return null;
+                    }
+
+                    // Check type validate
+                    if (!existing.type().isValid(type)) {
+                        log.error("Not valid OpenstackVtap type {} for requested type {}",
+                                existing.type(), type);
+                        return existing;
+                    }
+
+                    // Remove deviceId from txDeviceIds
+                    final Set<DeviceId> txDeviceIds;
+                    if (existing.type().isValid(Type.VTAP_TX) &&
+                            (type.isValid(Type.VTAP_TX) || type == Type.VTAP_ANY) &&
+                            existing.txDeviceIds().contains(deviceId)) {
+                        txDeviceIds = Sets.newHashSet(existing.txDeviceIds());
+                        txDeviceIds.remove(deviceId);
+                    } else {
+                        txDeviceIds = null;
+                    }
+
+                    // Remove deviceId from rxDeviceIds
+                    final Set<DeviceId> rxDeviceIds;
+                    if (existing.type().isValid(Type.VTAP_RX) &&
+                            (type.isValid(Type.VTAP_RX) || type == Type.VTAP_ANY) &&
+                            existing.rxDeviceIds().contains(deviceId)) {
+                        rxDeviceIds = Sets.newHashSet(existing.rxDeviceIds());
+                        rxDeviceIds.remove(deviceId);
+                    } else {
+                        rxDeviceIds = null;
+                    }
+
+                    if (txDeviceIds != null || rxDeviceIds != null) {
+                        return DefaultOpenstackVtap.builder()
+                                .id(id)
+                                .type(existing.type())
+                                .vtapCriterion(existing.vtapCriterion())
+                                .txDeviceIds(txDeviceIds != null ? txDeviceIds : existing.txDeviceIds())
+                                .rxDeviceIds(rxDeviceIds != null ? rxDeviceIds : existing.rxDeviceIds())
+                                .annotations(existing.annotations())
+                                .build();
+                    } else {
+                        return existing;
+                    }
+                });
+        return Objects.nonNull(result);
+    }
+
+    @Override
+    public Set<OpenstackVtap> getVtapsByDeviceId(DeviceId deviceId) {
+        Set<OpenstackVtapId> vtapIds = Sets.newHashSet();
+        Set<OpenstackVtapId> txIds = vtapIdsByTxDeviceId.get(deviceId);
+        if (txIds != null) {
+            vtapIds.addAll(txIds);
+        }
+        Set<OpenstackVtapId> rxIds = vtapIdsByRxDeviceId.get(deviceId);
+        if (rxIds != null) {
+            vtapIds.addAll(rxIds);
+        }
+        return ImmutableSet.copyOf(
+                vtapIds.parallelStream()
+                        .map(vtapId -> vtapMap.get(vtapId))
+                        .filter(Objects::nonNull)
+                        .collect(Collectors.toSet()));
+    }
+
     private class VtapComparator implements Comparator<OpenstackVtap> {
         @Override
         public int compare(OpenstackVtap v1, OpenstackVtap v2) {
             int diff = (v2.type().compareTo(v1.type()));
             if (diff == 0) {
-                return (v2.vTapCriterion().ipProtocol() - v1.vTapCriterion().ipProtocol());
+                return (v2.vtapCriterion().ipProtocol() - v1.vtapCriterion().ipProtocol());
             }
             return diff;
         }
     }
 
-    private static Set<OpenstackVtapId> addVTapIds(OpenstackVtapId vTapId) {
+    private void loadVtapIds() {
+        vtapIdsByTxDeviceId.clear();
+        vtapIdsByRxDeviceId.clear();
+        vtapMap.values().forEach(vtap -> refreshDeviceIdsByVtap(null, vtap));
+    }
+
+    private static Set<OpenstackVtapId> addVTapIds(OpenstackVtapId vtapId) {
         Set<OpenstackVtapId> vtapIds = Sets.newConcurrentHashSet();
-        vtapIds.add(vTapId);
+        vtapIds.add(vtapId);
         return vtapIds;
     }
 
     private static Set<OpenstackVtapId> updateVTapIds(Set<OpenstackVtapId> existingVtapIds,
-                                                      OpenstackVtapId vTapId) {
-        existingVtapIds.add(vTapId);
+                                                      OpenstackVtapId vtapId) {
+        existingVtapIds.add(vtapId);
         return existingVtapIds;
     }
 
     private static Set<OpenstackVtapId> removeVTapIds(Set<OpenstackVtapId> existingVtapIds,
-                                                      OpenstackVtapId vTapId) {
-        existingVtapIds.remove(vTapId);
+                                                      OpenstackVtapId vtapId) {
+        existingVtapIds.remove(vtapId);
         if (existingVtapIds.isEmpty()) {
             return null;
         }
         return existingVtapIds;
     }
 
-    private void updateVTapIdFromTxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
-        vTapIdsByTxDeviceId.compute(deviceId, (k, v) -> v == null ?
-                addVTapIds(vTapId) : updateVTapIds(v, vTapId));
+    private void updateVTapIdFromTxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) {
+        vtapIdsByTxDeviceId.compute(deviceId, (k, v) -> v == null ?
+                addVTapIds(vtapId) : updateVTapIds(v, vtapId));
     }
 
-    private void removeVTapIdFromTxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
-        vTapIdsByTxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vTapId));
+    private void removeVTapIdFromTxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) {
+        vtapIdsByTxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vtapId));
     }
 
-    private void updateVTapIdFromRxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
-        vTapIdsByRxDeviceId.compute(deviceId, (k, v) -> v == null ?
-                addVTapIds(vTapId) : updateVTapIds(v, vTapId));
+    private void updateVTapIdFromRxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) {
+        vtapIdsByRxDeviceId.compute(deviceId, (k, v) -> v == null ?
+                addVTapIds(vtapId) : updateVTapIds(v, vtapId));
     }
 
-    private void removeVTapIdFromRxDeviceId(OpenstackVtapId vTapId, DeviceId deviceId) {
-        vTapIdsByRxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vTapId));
+    private void removeVTapIdFromRxDeviceId(OpenstackVtapId vtapId, DeviceId deviceId) {
+        vtapIdsByRxDeviceId.computeIfPresent(deviceId, (k, v) -> removeVTapIds(v, vtapId));
     }
 
-    private void refreshDeviceIdsByVtap(OpenstackVtap oldOpenstackVtap,
-                                        OpenstackVtap newOpenstackVtap) {
+    private void refreshDeviceIdsByVtap(OpenstackVtap newOpenstackVtap,
+                                        OpenstackVtap oldOpenstackVtap) {
+        if (Objects.equals(newOpenstackVtap, oldOpenstackVtap)) {
+            return;
+        }
+
         if (oldOpenstackVtap != null) {
             Set<DeviceId> removeDeviceIds;
 
-            // Remove TX vTap
+            // Remove TX vtap
             removeDeviceIds = (newOpenstackVtap != null) ?
                     Sets.difference(oldOpenstackVtap.txDeviceIds(),
                             newOpenstackVtap.txDeviceIds()) : oldOpenstackVtap.txDeviceIds();
             removeDeviceIds.forEach(id -> removeVTapIdFromTxDeviceId(oldOpenstackVtap.id(), id));
 
-            // Remove RX vTap
+            // Remove RX vtap
             removeDeviceIds = (newOpenstackVtap != null) ?
                     Sets.difference(oldOpenstackVtap.rxDeviceIds(),
                             newOpenstackVtap.rxDeviceIds()) : oldOpenstackVtap.rxDeviceIds();
@@ -521,13 +628,13 @@
         if (newOpenstackVtap != null) {
             Set<DeviceId> addDeviceIds;
 
-            // Add TX vTap
+            // Add TX vtap
             addDeviceIds = (oldOpenstackVtap != null) ?
                     Sets.difference(newOpenstackVtap.txDeviceIds(),
                             oldOpenstackVtap.txDeviceIds()) : newOpenstackVtap.txDeviceIds();
             addDeviceIds.forEach(id -> updateVTapIdFromTxDeviceId(newOpenstackVtap.id(), id));
 
-            // Add RX vTap
+            // Add RX vtap
             addDeviceIds = (oldOpenstackVtap != null) ?
                     Sets.difference(newOpenstackVtap.rxDeviceIds(),
                             oldOpenstackVtap.rxDeviceIds()) : newOpenstackVtap.rxDeviceIds();
@@ -544,26 +651,24 @@
             DefaultOpenstackVtap oldValue =
                     event.oldValue() != null ? event.oldValue().value() : null;
 
-            log.debug("VtapEventListener {} -> {}, {}", event.type(), oldValue, newValue);
+            log.trace("VtapEventListener {}: {} -> {}", event.type(), oldValue, newValue);
             switch (event.type()) {
                 case INSERT:
-                    refreshDeviceIdsByVtap(oldValue, newValue);
+                    refreshDeviceIdsByVtap(newValue, oldValue);
                     notifyDelegate(new OpenstackVtapEvent(
-                            OpenstackVtapEvent.Type.VTAP_ADDED, newValue));
+                            OpenstackVtapEvent.Type.VTAP_ADDED, newValue, null));
                     break;
 
                 case UPDATE:
-                    if (!Objects.equals(newValue, oldValue)) {
-                        refreshDeviceIdsByVtap(oldValue, newValue);
-                        notifyDelegate(new OpenstackVtapEvent(
-                                OpenstackVtapEvent.Type.VTAP_UPDATED, newValue, oldValue));
-                    }
+                    refreshDeviceIdsByVtap(newValue, oldValue);
+                    notifyDelegate(new OpenstackVtapEvent(
+                            OpenstackVtapEvent.Type.VTAP_UPDATED, newValue, oldValue));
                     break;
 
                 case REMOVE:
-                    refreshDeviceIdsByVtap(oldValue, newValue);
+                    refreshDeviceIdsByVtap(newValue, oldValue);
                     notifyDelegate(new OpenstackVtapEvent(
-                            OpenstackVtapEvent.Type.VTAP_REMOVED, oldValue));
+                            OpenstackVtapEvent.Type.VTAP_REMOVED, null, oldValue));
                     break;
 
                 default:
@@ -571,4 +676,37 @@
             }
         }
     }
+
+    private class VtapNetworkEventListener
+            implements MapEventListener<Integer, DefaultOpenstackVtapNetwork> {
+        @Override
+        public void event(MapEvent<Integer, DefaultOpenstackVtapNetwork> event) {
+            DefaultOpenstackVtapNetwork newValue =
+                    event.newValue() != null ? event.newValue().value() : null;
+            DefaultOpenstackVtapNetwork oldValue =
+                    event.oldValue() != null ? event.oldValue().value() : null;
+
+            log.trace("VtapNetworkEventListener {}: {} -> {}", event.type(), oldValue, newValue);
+            switch (event.type()) {
+                case INSERT:
+                    notifyDelegate(new OpenstackVtapEvent(
+                            OpenstackVtapEvent.Type.VTAP_NETWORK_ADDED, newValue, null));
+                    break;
+
+                case UPDATE:
+                    notifyDelegate(new OpenstackVtapEvent(
+                            OpenstackVtapEvent.Type.VTAP_NETWORK_UPDATED, newValue, oldValue));
+                    break;
+
+                case REMOVE:
+                    notifyDelegate(new OpenstackVtapEvent(
+                            OpenstackVtapEvent.Type.VTAP_NETWORK_REMOVED, null, oldValue));
+                    break;
+
+                default:
+                    log.warn("Unknown map event type: {}", event.type());
+            }
+        }
+    }
+
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java
index 02a6579..6370de6 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/OpenstackVtapManager.java
@@ -22,12 +22,15 @@
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
-import org.onlab.packet.VlanId;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
 import org.onosproject.cluster.NodeId;
@@ -35,21 +38,21 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.core.GroupId;
 import org.onosproject.event.AbstractListenerManager;
-import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostLocation;
+import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.DefaultTunnelDescription;
 import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.behaviour.InterfaceConfig;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.behaviour.TunnelEndPoints;
+import org.onosproject.net.behaviour.TunnelKey;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DefaultDriverData;
-import org.onosproject.net.driver.DefaultDriverHandler;
-import org.onosproject.net.driver.Driver;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultFlowRule;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -59,6 +62,7 @@
 import org.onosproject.net.flow.FlowRuleService;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.ExtensionPropertyException;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
 import org.onosproject.net.group.DefaultGroupBucket;
 import org.onosproject.net.group.DefaultGroupDescription;
@@ -69,6 +73,7 @@
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
+import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeEvent;
 import org.onosproject.openstacknode.api.OpenstackNodeListener;
 import org.onosproject.openstacknode.api.OpenstackNodeService;
@@ -79,17 +84,19 @@
 import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
 import org.onosproject.openstackvtap.api.OpenstackVtapId;
 import org.onosproject.openstackvtap.api.OpenstackVtapListener;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork.Mode;
 import org.onosproject.openstackvtap.api.OpenstackVtapService;
 import org.onosproject.openstackvtap.api.OpenstackVtapStore;
 import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
+import java.util.Dictionary;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.ScheduledExecutorService;
-import java.util.function.BiFunction;
 import java.util.stream.Collectors;
 import java.util.stream.StreamSupport;
 
@@ -99,9 +106,10 @@
 import static org.onlab.packet.IPv4.PROTOCOL_ICMP;
 import static org.onlab.packet.IPv4.PROTOCOL_TCP;
 import static org.onlab.packet.IPv4.PROTOCOL_UDP;
-import static org.onlab.packet.VlanId.UNTAGGED;
 import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
 import static org.onosproject.openstacknetworking.api.Constants.DHCP_ARP_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.FLAT_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
@@ -114,12 +122,20 @@
 import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_GROUP_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_MIRROR_TABLE;
 import static org.onosproject.openstacknetworking.api.Constants.VTAP_OUTBOUND_TABLE;
+import static org.onosproject.openstacknode.api.Constants.INTEGRATION_BRIDGE;
+import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
 import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.containsIp;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.dumpStackTrace;
 import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getGroupKey;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getTunnelName;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getTunnelType;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.hostCompareIp;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.isValidHost;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Provides basic implementation of the user APIs.
+ * Provides implementation of the openstack vtap and openstack vtap network APIs.
  */
 @Component(immediate = true)
 @Service
@@ -139,12 +155,6 @@
     protected LeadershipService leadershipService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DriverService driverService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowRuleService flowRuleService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -154,36 +164,59 @@
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackVtapStore store;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService osNodeService;
+    protected ComponentConfigService componentConfigService;
+
+    private static final boolean DEFAULT_TUNNEL_NICRA = false;
+    @Property(name = TUNNEL_NICIRA, boolValue = DEFAULT_TUNNEL_NICRA,
+            label = "Use nicra extension for tunneling")
+    private boolean tunnelNicira = DEFAULT_TUNNEL_NICRA;
 
     public static final String APP_ID = "org.onosproject.openstackvtap";
-
-    public static final String VTAP_ID_NULL = "OpenstackVtap ID cannot be null";
-    public static final String VTAP_DESC_NULL = "OpenstackVtap fields cannot be null";
-    public static final String DEVICE_ID_NULL = "Device ID cannot be null";
+    public static final String VTAP_DESC_NULL = "vtap field %s cannot be null";
 
     private static final int PRIORITY_VTAP_RULE = 50000;
-    private static final int PRIORITY_VTAP_OUTPORT_RULE = 1000;
-    private static final int PRIORITY_VTAP_DROP = 0;
+    private static final int PRIORITY_VTAP_OUTPUT_RULE = 1000;
+    private static final int PRIORITY_VTAP_OUTPUT_DROP = 0;
 
-    private static final int NONE_TABLE = -1;
     private static final int INBOUND_NEXT_TABLE = DHCP_ARP_TABLE;
     private static final int FLAT_OUTBOUND_NEXT_TABLE = FLAT_TABLE;
     private static final int OUTBOUND_NEXT_TABLE = FORWARDING_TABLE;
 
+    private static final int[][] VTAP_TABLES = {
+            {VTAP_INBOUND_TABLE, VTAP_INBOUND_GROUP_TABLE,
+                    INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE},
+            {VTAP_FLAT_OUTBOUND_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
+                    FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE},
+            {VTAP_OUTBOUND_TABLE, VTAP_OUTBOUND_GROUP_TABLE,
+                    OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE}};
+    private static final int VTAP_TABLE_INBOUND_IDX = 0;
+    private static final int VTAP_TABLE_FLAT_OUTBOUND_IDX = 1;
+    private static final int VTAP_TABLE_OUTBOUND_IDX = 2;
+    private static final int VTAP_TABLE_INPUT_IDX = 0;
+    private static final int VTAP_TABLE_GROUP_IDX = 1;
+    private static final int VTAP_TABLE_NEXT_IDX = 2;
+    private static final int VTAP_TABLE_OUTPUT_IDX = 3;
+
     private static final IpPrefix ARBITRARY_IP_PREFIX =
                     IpPrefix.valueOf(IpAddress.valueOf("0.0.0.0"), 0);
-    private static final String TABLE_PROPERTY_KEY = "table";
+    private static final String TABLE_EXTENSION = "table";
+    private static final String TUNNEL_DST_EXTENSION = "tunnelDst";
+    private static final String TUNNEL_NICIRA = "tunnelNicira";
+
+    private static final int VTAP_NETWORK_KEY = 0;
 
     private final DeviceListener deviceListener = new InternalDeviceListener();
-    private final HostListener hostListener = new InternalHostListener();
     private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
+    private final HostListener hostListener = new InternalHostListener();
 
     private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate();
 
@@ -191,12 +224,16 @@
     private NodeId localNodeId;
     private ScheduledExecutorService eventExecutor;
 
+    private final Object syncInterface = new Object();              // notification of tunnel interface
+    private static final int INTERFACE_MANIPULATION_TIMEOUT = 1000; // 1000msec
+    private static final int INTERFACE_MANIPULATION_RETRY = 10;     // 10 times (totally 10sec)
 
     @Activate
     public void activate(ComponentContext context) {
         appId = coreService.registerApplication(APP_ID);
         localNodeId = clusterService.getLocalNode().id();
         leadershipService.runForLeadership(appId.name());
+        componentConfigService.registerProperties(getClass());
 
         eventExecutor = newSingleThreadScheduledExecutor(
                 groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
@@ -205,29 +242,200 @@
         eventDispatcher.addSink(OpenstackVtapEvent.class, listenerRegistry);
 
         deviceService.addListener(deviceListener);
-        hostService.addListener(hostListener);
         osNodeService.addListener(osNodeListener);
+        hostService.addListener(hostListener);
 
-        initFlowAndGroupForCompNodes();
+        initVtap();
 
-        log.info("Started {} - {}", appId.name(), this.getClass().getSimpleName());
+        log.info("Started");
     }
 
     @Deactivate
     public void deactivate() {
-        clearFlowAndGroupForCompNodes();
+        clearVtap();
 
-        osNodeService.removeListener(osNodeListener);
         hostService.removeListener(hostListener);
+        osNodeService.removeListener(osNodeListener);
         deviceService.removeListener(deviceListener);
 
         eventDispatcher.removeSink(OpenstackVtapEvent.class);
         store.unsetDelegate(delegate);
 
         eventExecutor.shutdown();
+
+        componentConfigService.unregisterProperties(getClass(), false);
         leadershipService.withdraw(appId.name());
 
-        log.info("Stopped {} - {}", appId.name(), this.getClass().getSimpleName());
+        log.info("Stopped");
+    }
+
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+
+        boolean updatedTunnelNicira = Tools.isPropertyEnabled(properties, TUNNEL_NICIRA);
+        if (tunnelNicira != updatedTunnelNicira) {
+            if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
+                // Update the tunnel flow rule by reflecting the change.
+                osNodeService.completeNodes(COMPUTE)
+                        .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, false));
+                tunnelNicira = updatedTunnelNicira;
+                osNodeService.completeNodes(COMPUTE).stream()
+                        .filter(osNode -> osNode.state() == COMPLETE)
+                        .forEach(osNode -> applyVtapNetwork(getVtapNetwork(), osNode, true));
+                log.debug("Apply {} nicira extension for tunneling", tunnelNicira ? "enable" : "disable");
+            } else {
+                tunnelNicira = updatedTunnelNicira;
+            }
+        }
+
+        log.info("Modified");
+    }
+
+    /**
+     * Initializes the flow rules and group tables, tunneling interface for all completed compute nodes.
+     */
+    @Override
+    public void initVtap() {
+        if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
+            osNodeService.completeNodes(COMPUTE).stream()
+                    .filter(osNode -> osNode.state() == COMPLETE)
+                    .forEach(osNode -> initVtapForNode(osNode));
+            log.trace("{} flow rules, groups, tunnel interface are initialized", appId.name());
+        }
+    }
+
+    /**
+     * Clears the flow rules and group tables, tunneling interface for all compute nodes.
+     */
+    @Override
+    public void clearVtap() {
+        if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
+            osNodeService.completeNodes(COMPUTE).stream()
+                    .forEach(osNode -> clearVtapForNode(osNode));
+            log.trace("{} flow rules, groups, tunnel interface are cleared", appId.name());
+        }
+    }
+
+    /**
+     * Purges all flow rules and group tables, tunneling interface for openstack vtap.
+     */
+    @Override
+    public void purgeVtap() {
+        // Remove all flow rules
+        flowRuleService.removeFlowRulesById(appId);
+
+        // Remove all groups and tunnel interfaces
+        osNodeService.completeNodes(COMPUTE).stream()
+                .filter(osNode -> osNode.state() == COMPLETE)
+                .forEach(osNode -> {
+                    groupService.getGroups(osNode.intgBridge(), appId)
+                            .forEach(group ->
+                                    groupService.removeGroup(osNode.intgBridge(), group.appCookie(), appId));
+
+                    OpenstackVtapNetwork vtapNetwork = getVtapNetwork();
+                    setTunnelInterface(osNode, vtapNetwork, false);
+                });
+
+        log.trace("{} all flow rules, groups, tunnel interface are purged", appId.name());
+    }
+
+    private void initVtapForNode(OpenstackNode osNode) {
+        // Make base vtap network
+        initVtapNetwork(osNode);
+
+        // Make vtap connections by OpenstackVtap config
+        getVtapsByDeviceId(osNode.intgBridge())
+                .forEach(vtap -> applyVtap(vtap, osNode, true));
+
+        // Make vtap networks by OpenstackVtapNetwork config
+        applyVtapNetwork(getVtapNetwork(), osNode, true);
+    }
+
+    private void clearVtapForNode(OpenstackNode osNode) {
+        // Clear vtap networks by OpenstackVtapNetwork config
+        applyVtapNetwork(getVtapNetwork(), osNode, false);
+
+        // Clear vtap connections by OpenstackVtap config
+        getVtapsByDeviceId(osNode.intgBridge())
+                .forEach(vtap -> applyVtap(vtap, osNode, false));
+
+        // Clear base vtap network
+        clearVtapNetwork(osNode);
+    }
+
+    /**
+     * Initializes vtap pipeline of the given device.
+     *
+     * @param osNode device identifier
+     */
+    private void initVtapNetwork(OpenstackNode osNode) {
+        // Create default output tables
+        for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
+            setOutputTableForDrop(osNode.intgBridge(),
+                    VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], true);
+        }
+
+        // Create group tables
+        for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
+            createGroupTable(osNode.intgBridge(),
+                    VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX],
+                    ImmutableList.of(VTAP_TABLES[idx][VTAP_TABLE_NEXT_IDX],
+                            VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX]),
+                    null);
+        }
+    }
+
+    /**
+     * Clear vtap pipeline of the given device.
+     *
+     * @param osNode device identifier
+     */
+    private void clearVtapNetwork(OpenstackNode osNode) {
+        // Clear group tables
+        for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
+            removeGroupTable(osNode.intgBridge(),
+                    VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX]);
+        }
+
+        // Clear default output tables
+        for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
+            setOutputTableForDrop(osNode.intgBridge(),
+                    VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX], false);
+        }
+    }
+
+    @Override
+    public OpenstackVtapNetwork getVtapNetwork() {
+        return store.getVtapNetwork(VTAP_NETWORK_KEY);
+    }
+
+    @Override
+    public OpenstackVtapNetwork createVtapNetwork(Mode mode, Integer networkId, IpAddress serverIp) {
+        checkNotNull(mode, VTAP_DESC_NULL, "mode");
+        checkNotNull(serverIp, VTAP_DESC_NULL, "serverIp");
+        DefaultOpenstackVtapNetwork vtapNetwork = DefaultOpenstackVtapNetwork.builder()
+                .mode(mode)
+                .networkId(networkId)
+                .serverIp(serverIp)
+                .build();
+        return store.createVtapNetwork(VTAP_NETWORK_KEY, vtapNetwork);
+    }
+
+    @Override
+    public OpenstackVtapNetwork updateVtapNetwork(OpenstackVtapNetwork description) {
+        checkNotNull(description, VTAP_DESC_NULL, "vtapNetwork");
+        return store.updateVtapNetwork(VTAP_NETWORK_KEY, description);
+    }
+
+    @Override
+    public OpenstackVtapNetwork removeVtapNetwork() {
+        return store.removeVtapNetwork(VTAP_NETWORK_KEY);
+    }
+
+    @Override
+    public Set<DeviceId> getVtapNetworkDevices() {
+        return store.getVtapNetworkDevices(VTAP_NETWORK_KEY);
     }
 
     @Override
@@ -241,87 +449,54 @@
     }
 
     @Override
-    public OpenstackVtap getVtap(OpenstackVtapId vTapId) {
-        checkNotNull(vTapId, VTAP_ID_NULL);
-        return store.getVtap(vTapId);
+    public OpenstackVtap getVtap(OpenstackVtapId vtapId) {
+        return store.getVtap(vtapId);
     }
 
     @Override
-    public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type,
-                                                 DeviceId deviceId) {
-        checkNotNull(deviceId, DEVICE_ID_NULL);
-        return store.getVtapsByDeviceId(type, deviceId);
+    public Set<OpenstackVtap> getVtapsByDeviceId(DeviceId deviceId) {
+        return store.getVtapsByDeviceId(deviceId);
     }
 
     @Override
-    public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vTapCriterion) {
-        checkNotNull(vTapCriterion, VTAP_DESC_NULL);
+    public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vtapCriterion) {
+        checkNotNull(type, VTAP_DESC_NULL, "type");
+        checkNotNull(vtapCriterion, VTAP_DESC_NULL, "vtapCriterion");
 
         Set<DeviceId> txDevices = type.isValid(Type.VTAP_TX) ?
-                getEdgeDevice(type, vTapCriterion) : ImmutableSet.of();
+                getEdgeDevice(Type.VTAP_TX, vtapCriterion) : ImmutableSet.of();
         Set<DeviceId> rxDevices = type.isValid(Type.VTAP_RX) ?
-                getEdgeDevice(type, vTapCriterion) : ImmutableSet.of();
+                getEdgeDevice(Type.VTAP_RX, vtapCriterion) : ImmutableSet.of();
 
-        OpenstackVtap description =
-                            DefaultOpenstackVtap.builder()
-                                                .id(OpenstackVtapId.vTapId())
-                                                .type(type)
-                                                .vTapCriterion(vTapCriterion)
-                                                .txDeviceIds(txDevices)
-                                                .rxDeviceIds(rxDevices)
-                                                .build();
-        return store.createOrUpdateVtap(description.id(), description, true);
+        DefaultOpenstackVtap description = DefaultOpenstackVtap.builder()
+                .id(OpenstackVtapId.vtapId())
+                .type(type)
+                .vtapCriterion(vtapCriterion)
+                .txDeviceIds(txDevices)
+                .rxDeviceIds(rxDevices)
+                .build();
+        return store.createVtap(description);
     }
 
     @Override
-    public OpenstackVtap updateVtap(OpenstackVtapId vTapId, OpenstackVtap vTap) {
-        checkNotNull(vTapId, VTAP_ID_NULL);
-        checkNotNull(vTap, VTAP_DESC_NULL);
+    public OpenstackVtap updateVtap(OpenstackVtap description) {
+        checkNotNull(description, VTAP_DESC_NULL, "vtap");
 
-        if (store.getVtap(vTapId) == null) {
-            return null;
-        }
+        Set<DeviceId> txDevices = description.type().isValid(Type.VTAP_TX) ?
+                getEdgeDevice(Type.VTAP_TX, description.vtapCriterion()) : ImmutableSet.of();
+        Set<DeviceId> rxDevices = description.type().isValid(Type.VTAP_RX) ?
+                getEdgeDevice(Type.VTAP_RX, description.vtapCriterion()) : ImmutableSet.of();
 
-        Set<DeviceId> txDevices = vTap.type().isValid(Type.VTAP_TX) ?
-                getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of();
-        Set<DeviceId> rxDevices = vTap.type().isValid(Type.VTAP_RX) ?
-                getEdgeDevice(vTap.type(), vTap.vTapCriterion()) : ImmutableSet.of();
-
-        DefaultOpenstackVtap description =
-                            DefaultOpenstackVtap.builder()
-                                                .id(vTapId)
-                                                .type(vTap.type())
-                                                .vTapCriterion(vTap.vTapCriterion())
-                                                .txDeviceIds(txDevices)
-                                                .rxDeviceIds(rxDevices)
-                                                .build();
-        return store.createOrUpdateVtap(vTapId, description, true);
+        DefaultOpenstackVtap vtap = DefaultOpenstackVtap.builder(description)
+                .txDeviceIds(txDevices)
+                .rxDeviceIds(rxDevices)
+                .build();
+        return store.updateVtap(vtap, true);
     }
 
     @Override
-    public OpenstackVtap removeVtap(OpenstackVtapId vTapId) {
-        checkNotNull(vTapId, VTAP_ID_NULL);
-        return store.removeVtapById(vTapId);
-    }
-
-    @Override
-    public void setVtapOutput(DeviceId deviceId, OpenstackVtap.Type type,
-                              PortNumber portNumber, VlanId vlanId) {
-
-        // Make output table
-        if (type.isValid(Type.VTAP_TX)) {
-            createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, portNumber, vlanId);
-        }
-
-        if (type.isValid(Type.VTAP_RX)) {
-            createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, portNumber, vlanId);
-            createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, portNumber, vlanId);
-        }
-    }
-
-    @Override
-    public void setVtapOutput(DeviceId deviceId, Type type, PortNumber portNumber, int vni) {
-        // TODO: need to provide implementation
+    public OpenstackVtap removeVtap(OpenstackVtapId vtapId) {
+        return store.removeVtap(vtapId);
     }
 
     /**
@@ -329,274 +504,138 @@
      * Note that, in most of cases target host is attached to one device,
      * however, in some cases, the host can be attached to multiple devices.
      *
-     * @param type          vTap type
-     * @param criterion     vTap criterion
+     * @param type          vtap type
+     * @param criterion     vtap criterion
      * @return a collection of device identifiers
      */
     private Set<DeviceId> getEdgeDevice(Type type, OpenstackVtapCriterion criterion) {
         Set<DeviceId> deviceIds = Sets.newConcurrentHashSet();
         StreamSupport.stream(hostService.getHosts().spliterator(), true)
-            .forEach(host -> {
-                if (host.ipAddresses().stream()
-                        .anyMatch(ip -> containsIp(type, criterion, ip))) {
-                    deviceIds.addAll(host.locations().stream()
-                                         .map(HostLocation::deviceId)
-                                         .collect(Collectors.toSet()));
-                }
-            });
+                .filter(host -> isValidHost(host) &&
+                        host.ipAddresses().stream().anyMatch(ip -> containsIp(type, criterion, ip)))
+                .forEach(host -> {
+                    Set<DeviceId> hostDeviceIds =
+                            host.locations().stream()
+                                    .map(HostLocation::deviceId)
+                                    .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId)))
+                                    .collect(Collectors.toSet());
+                    deviceIds.addAll(hostDeviceIds);
+                });
         return deviceIds;
     }
 
     /**
-     * Checks whether the given IP address is included in vTap criterion.
-     * We both check the TX and RX directions.
-     *
-     * @param type          vTap type
-     * @param criterion     vTap criterion
-     * @param ip            IP address
-     * @return boolean value indicates the check result
-     */
-    private boolean containsIp(Type type, OpenstackVtapCriterion criterion, IpAddress ip) {
-        boolean isTxEdge = type.isValid(Type.VTAP_TX) &&
-                                             criterion.srcIpPrefix().contains(ip);
-        boolean isRxEdge = type.isValid(Type.VTAP_RX) &&
-                                             criterion.dstIpPrefix().contains(ip);
-
-        return isTxEdge || isRxEdge;
-    }
-
-    /**
-     * Updates device list of vTaps with respect to the host changes.
+     * Updates device list of vtaps with respect to the host changes.
      *
      * @param newHost   new host instance
      * @param oldHost   old host instance
      */
+    private void updateHostbyType(Type type, Host newHost, Host oldHost) {
+        getVtaps(type).forEach(vtap -> {
+            IpPrefix prefix = (type == Type.VTAP_TX) ?
+                    vtap.vtapCriterion().srcIpPrefix() :
+                    vtap.vtapCriterion().dstIpPrefix();
+
+            int hostDiff = hostCompareIp(newHost, oldHost, prefix);
+            if (hostDiff < 0) {
+                oldHost.locations().stream()
+                        .map(HostLocation::deviceId)
+                        .forEach(deviceId ->
+                                store.removeDeviceFromVtap(vtap.id(), type, deviceId));
+            } else if (hostDiff > 0) {
+                newHost.locations().stream()
+                        .map(HostLocation::deviceId)
+                        .filter(deviceId -> Objects.nonNull(osNodeService.node(deviceId)))
+                        .forEach(deviceId ->
+                                store.addDeviceToVtap(vtap.id(), type, deviceId));
+            }
+        });
+    }
+
     private void updateHost(Host newHost, Host oldHost) {
-        // update devices for vTap tx
-        getVtaps(Type.VTAP_TX).parallelStream().forEach(vTap -> {
+        // update devices for vtap tx
+        updateHostbyType(Type.VTAP_TX, newHost, oldHost);
 
-            if (hostDiff(oldHost, newHost, vTap.vTapCriterion().srcIpPrefix())) {
-                oldHost.locations().stream().map(HostLocation::deviceId)
-                        .forEach(deviceId ->
-                                store.removeDeviceFromVtap(vTap.id(), Type.VTAP_TX,
-                                        oldHost.location().deviceId()));
-            }
-
-            if (hostDiff(newHost, oldHost, vTap.vTapCriterion().srcIpPrefix())) {
-                newHost.locations().stream().map(HostLocation::deviceId)
-                        .forEach(deviceId ->
-                                store.addDeviceToVtap(vTap.id(), Type.VTAP_TX,
-                                        newHost.location().deviceId()));
-            }
-        });
-
-        // update devices for vTap rx
-        getVtaps(Type.VTAP_RX).parallelStream().forEach(vTap -> {
-
-            if (hostDiff(oldHost, newHost, vTap.vTapCriterion().dstIpPrefix())) {
-                oldHost.locations().stream().map(HostLocation::deviceId)
-                        .forEach(deviceId ->
-                                store.removeDeviceFromVtap(vTap.id(), Type.VTAP_RX,
-                                        oldHost.location().deviceId()));
-            }
-
-            if (hostDiff(newHost, oldHost, vTap.vTapCriterion().dstIpPrefix())) {
-                newHost.locations().stream().map(HostLocation::deviceId)
-                        .forEach(deviceId ->
-                                store.addDeviceToVtap(vTap.id(), Type.VTAP_RX,
-                                        newHost.location().deviceId()));
-            }
-        });
+        // update devices for vtap rx
+        updateHostbyType(Type.VTAP_RX, newHost, oldHost);
     }
 
-    /**
-     * Checks whether the given IP prefix is contained in the first host rather
-     * than in the second host.
-     *
-     * @param host1     first host instance
-     * @param host2     second host instance
-     * @param ipPrefix  IP prefix to be looked up
-     * @return boolean value
-     */
-    private boolean hostDiff(Host host1, Host host2, IpPrefix ipPrefix) {
-        return ((host1 != null && host1.ipAddresses().stream().anyMatch(ipPrefix::contains)) &&
-                (host2 == null || host2.ipAddresses().stream().noneMatch(ipPrefix::contains)));
-    }
+    private void applyFlowRule(FlowRule flowRule, boolean install) {
+        FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
 
-    /**
-     * Initializes the flow rules and group tables for all completed compute nodes.
-     */
-    private void initFlowAndGroupForCompNodes() {
-        osNodeService.completeNodes(COMPUTE).forEach(node ->
-                                initFlowAndGroupByDeviceId(node.intgBridge()));
-    }
-
-    /**
-     * Initializes the flow rules and group table of the given device identifier.
-     *
-     * @param deviceId device identifier
-     */
-    private void initFlowAndGroupByDeviceId(DeviceId deviceId) {
-        // Make vTap pipeline
-        // TODO: need to selective creation by store device consistentMap
-        initVtapPipeline(deviceId);
-
-        // Install tx filter
-        getVtapsByDeviceId(Type.VTAP_TX, deviceId).forEach(vTap -> {
-            connectTables(deviceId,
-                    VTAP_INBOUND_TABLE, NONE_TABLE, VTAP_INBOUND_GROUP_TABLE,
-                    vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
-        });
-
-        // Install rx filter
-        getVtapsByDeviceId(Type.VTAP_RX, deviceId).forEach(vTap -> {
-            connectTables(deviceId,
-                    VTAP_FLAT_OUTBOUND_TABLE, NONE_TABLE, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
-                    vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
-            connectTables(deviceId,
-                    VTAP_OUTBOUND_TABLE, NONE_TABLE, VTAP_OUTBOUND_GROUP_TABLE,
-                    vTap.vTapCriterion(), PRIORITY_VTAP_RULE, true);
-        });
-    }
-
-    /**
-     * Initializes vTap pipeline of the given device.
-     *
-     * @param deviceId device identifier
-     */
-    private void initVtapPipeline(DeviceId deviceId) {
-        // Make output table
-        createOutputTable(deviceId, VTAP_INBOUND_MIRROR_TABLE, null, null);
-        createOutputTable(deviceId, VTAP_FLAT_OUTBOUND_MIRROR_TABLE, null, null);
-        createOutputTable(deviceId, VTAP_OUTBOUND_MIRROR_TABLE, null, null);
-
-        // Make tx group table
-        createGroupTable(deviceId, VTAP_INBOUND_GROUP_TABLE,
-                ImmutableList.of(INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE),
-                ImmutableList.of());
-
-        // Make rx group table
-        createGroupTable(deviceId, VTAP_FLAT_OUTBOUND_GROUP_TABLE,
-                ImmutableList.of(FLAT_OUTBOUND_NEXT_TABLE, VTAP_FLAT_OUTBOUND_MIRROR_TABLE),
-                ImmutableList.of());
-        createGroupTable(deviceId, VTAP_OUTBOUND_GROUP_TABLE,
-                ImmutableList.of(OUTBOUND_NEXT_TABLE, VTAP_OUTBOUND_MIRROR_TABLE),
-                ImmutableList.of());
-    }
-
-    /**
-     * Purges all flow rules and group tables for completed compute nodes.
-     */
-    private void clearFlowAndGroupForCompNodes() {
-        osNodeService.completeNodes(COMPUTE).forEach(node ->
-                clearFlowAndGroupByDeviceId(node.intgBridge()));
-    }
-
-    /**
-     * Purges all flow rules and group tables using the given device identifier.
-     *
-     * @param deviceId  device identifier
-     */
-    private void clearFlowAndGroupByDeviceId(DeviceId deviceId) {
-        Set<FlowRule> purgedRules = Sets.newConcurrentHashSet();
-        for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
-            if (flowRule.deviceId().equals(deviceId)) {
-                purgedRules.add(flowRule);
-            }
+        if (install) {
+            flowOpsBuilder.add(flowRule);
+        } else {
+            flowOpsBuilder.remove(flowRule);
         }
 
-        flowRuleService.removeFlowRules(purgedRules.toArray(new FlowRule[0]));
+        flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                log.debug("Installed flow rules for vtap");
+            }
 
-        groupService.getGroups(deviceId, appId).forEach(group -> {
-            groupService.removeGroup(deviceId, group.appCookie(), appId);
-        });
-        log.info("OpenstackVtap flow rules and groups are purged");
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.warn("Failed to install flow rules for vtap");
+            }
+        }));
     }
 
-    private void installFilterRule(Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
-                                   OpenstackVtapCriterion vTapCriterion, boolean install) {
-        final int inbound = 0;
-        final int flatOutbound = 1;
-        final int outbound = 2;
-
-        BiFunction<Set<DeviceId>, Integer, Void> installFlow = (deviceIds, table) -> {
-            int inTable = (table == inbound ? VTAP_INBOUND_TABLE :
-                    (table == flatOutbound ? VTAP_FLAT_OUTBOUND_TABLE :
-                                             VTAP_OUTBOUND_TABLE));
-
-            int outGroup = (table == inbound ? VTAP_INBOUND_GROUP_TABLE :
-                    (table == flatOutbound ? VTAP_FLAT_OUTBOUND_GROUP_TABLE :
-                                             VTAP_OUTBOUND_GROUP_TABLE));
-
-            deviceIds.stream()
-                    .filter(deviceId -> mastershipService.isLocalMaster(deviceId))
-                    .forEach(deviceId -> {
-                        connectTables(deviceId, inTable, NONE_TABLE, outGroup,
-                                vTapCriterion, PRIORITY_VTAP_RULE, install);
-                    });
-            return null;
-        };
-
-        installFlow.apply(txDeviceIds, inbound);
-        installFlow.apply(rxDeviceIds, flatOutbound);
-        installFlow.apply(rxDeviceIds, outbound);
-    }
-
-    private void connectTables(DeviceId deviceId, int fromTable, int toTable, int toGroup,
-                               OpenstackVtapCriterion vTapCriterion, int rulePriority,
-                               boolean install) {
-        log.trace("Table Transition: table[{}] -> table[{}] or group[{}]", fromTable, toTable, toGroup);
+    private void connectTables(DeviceId deviceId,
+                               int fromTable,
+                               int toTableOrGroup, boolean isGroup,
+                               OpenstackVtapCriterion vtapCriterion,
+                               int rulePriority, boolean install) {
+        log.debug("Table Transition: table[{}] -> table/group[{}]", fromTable, toTableOrGroup);
 
         TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(TYPE_IPV4);
 
         // if the IpPrefix is "0.0.0.0/0", we do not include such a match into the flow rule
-        if (!vTapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
-            selectorBuilder.matchIPSrc(vTapCriterion.srcIpPrefix());
+        if (!vtapCriterion.srcIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
+            selectorBuilder.matchIPSrc(vtapCriterion.srcIpPrefix());
         }
 
-        if (!vTapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
-            selectorBuilder.matchIPDst(vTapCriterion.dstIpPrefix());
+        if (!vtapCriterion.dstIpPrefix().equals(ARBITRARY_IP_PREFIX)) {
+            selectorBuilder.matchIPDst(vtapCriterion.dstIpPrefix());
         }
 
-        switch (vTapCriterion.ipProtocol()) {
+        switch (vtapCriterion.ipProtocol()) {
             case PROTOCOL_TCP:
-                selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
+                selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
 
                 // Add port match only if the port number is greater than zero
-                if (vTapCriterion.srcTpPort().toInt() > 0) {
-                    selectorBuilder.matchTcpSrc(vTapCriterion.srcTpPort());
+                if (vtapCriterion.srcTpPort().toInt() > 0) {
+                    selectorBuilder.matchTcpSrc(vtapCriterion.srcTpPort());
                 }
-                if (vTapCriterion.dstTpPort().toInt() > 0) {
-                    selectorBuilder.matchTcpDst(vTapCriterion.dstTpPort());
+                if (vtapCriterion.dstTpPort().toInt() > 0) {
+                    selectorBuilder.matchTcpDst(vtapCriterion.dstTpPort());
                 }
                 break;
             case PROTOCOL_UDP:
-                selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
+                selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
 
                 // Add port match only if the port number is greater than zero
-                if (vTapCriterion.srcTpPort().toInt() > 0) {
-                    selectorBuilder.matchUdpSrc(vTapCriterion.srcTpPort());
+                if (vtapCriterion.srcTpPort().toInt() > 0) {
+                    selectorBuilder.matchUdpSrc(vtapCriterion.srcTpPort());
                 }
-                if (vTapCriterion.dstTpPort().toInt() > 0) {
-                    selectorBuilder.matchUdpDst(vTapCriterion.dstTpPort());
+                if (vtapCriterion.dstTpPort().toInt() > 0) {
+                    selectorBuilder.matchUdpDst(vtapCriterion.dstTpPort());
                 }
                 break;
             case PROTOCOL_ICMP:
-                selectorBuilder.matchIPProtocol(vTapCriterion.ipProtocol());
+                selectorBuilder.matchIPProtocol(vtapCriterion.ipProtocol());
                 break;
             default:
                 break;
         }
 
         TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
-        if (toTable != NONE_TABLE) {
-            treatmentBuilder.transition(toTable);
-        } else if (toGroup != NONE_TABLE) {
-            treatmentBuilder.group(GroupId.valueOf(toGroup));
+        if (isGroup) {
+            treatmentBuilder.group(GroupId.valueOf(toTableOrGroup));
         } else {
-            log.warn("Not specified toTable or toGroup value");
-            return;
+            treatmentBuilder.transition(toTableOrGroup);
         }
 
         FlowRule flowRule = DefaultFlowRule.builder()
@@ -612,69 +651,246 @@
         applyFlowRule(flowRule, install);
     }
 
-    private void createOutputTable(DeviceId deviceId, int tableId,
-                                   PortNumber outPort, VlanId vlanId) {
-        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-
-        // Set output port & vlan
-        int priority = PRIORITY_VTAP_DROP;
-        if (vlanId != null && vlanId.toShort() != UNTAGGED) {
-            treatment.pushVlan().setVlanId(vlanId);
+    /**
+     * Creates/Removes a tunnel interface in a given openstack node by vtap network information.
+     *
+     * @param osNode openstack node
+     * @param vtapNetwork openstack vtap network for making
+     *
+     */
+    private boolean setTunnelInterface(OpenstackNode osNode,
+                                       OpenstackVtapNetwork vtapNetwork,
+                                       boolean install) {
+        String tunnelName = getTunnelName(vtapNetwork.mode());
+        if (tunnelName == null) {
+            return false;
         }
-        if (outPort != null) {
-            treatment.setOutput(outPort);
-            priority = PRIORITY_VTAP_OUTPORT_RULE;
+
+        if (!deviceService.isAvailable(osNode.ovsdb())) {
+            log.warn("Not available osNode {} ovs {}", osNode.hostname(), osNode.ovsdb());
+            return false;
+        }
+
+        if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) {
+            log.warn("Already {} {} interface on osNode ovs {}, bridge {}",
+                    install ? "add" : "remove",
+                    tunnelName, osNode.ovsdb(), osNode.intgBridge());
+            return true;
+        }
+
+        Device device = deviceService.getDevice(osNode.ovsdb());
+        if (device == null || !device.is(InterfaceConfig.class)) {
+            log.warn("Not able to get InterfaceConfig on osNode ovs {}", osNode.ovsdb());
+            return false;
+        }
+
+        InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
+        if (install) {
+            TunnelDescription.Builder tunnelDesc = DefaultTunnelDescription.builder()
+                    .deviceId(INTEGRATION_BRIDGE)
+                    .ifaceName(tunnelName)
+                    .type(getTunnelType(vtapNetwork.mode()))
+                    .key((vtapNetwork.networkId() == 0) ? null : new TunnelKey<>(vtapNetwork.networkId()))
+                    .remote(TunnelEndPoints.ipTunnelEndpoint(vtapNetwork.serverIp()));
+            if (!ifaceConfig.addTunnelMode(tunnelName, tunnelDesc.build())) {
+                log.error("Fail to create {} interface on osNode ovs {}", tunnelName, osNode.ovsdb());
+                return false;
+            }
+        } else {
+            if (!ifaceConfig.removeTunnelMode(tunnelName)) {
+                log.error("Fail to remove {} interface on osNode ovs {}", tunnelName, osNode.ovsdb());
+                return false;
+            }
+        }
+
+        // Wait for tunnel interface create/remove complete
+        synchronized (syncInterface) {
+            for (int i = 0; i < INTERFACE_MANIPULATION_RETRY; i++) {
+                try {
+                    syncInterface.wait(INTERFACE_MANIPULATION_TIMEOUT);
+                    if (install == isInterfaceEnabled(osNode.intgBridge(), tunnelName)) {
+                        log.debug("Success to {} {} interface on osNode ovs {}, bridge {}",
+                                install ? "add" : "remove",
+                                tunnelName, osNode.ovsdb(), osNode.intgBridge());
+                        return true;
+                    }
+                } catch (InterruptedException e) {
+                    break;
+                }
+            }
+        }
+        log.warn("Fail to {} {} interface on osNode ovs {}, bridge {}",
+                install ? "add" : "remove",
+                tunnelName, osNode.ovsdb(), osNode.intgBridge());
+        return false;
+    }
+
+    /**
+     * Checks whether a given network interface in a given openstack node is enabled or not.
+     *
+     * @param deviceId openstack node
+     * @param interfaceName network interface name
+     * @return true if the given interface is enabled, false otherwise
+     */
+    private boolean isInterfaceEnabled(DeviceId deviceId, String interfaceName) {
+        return deviceService.isAvailable(deviceId) &&
+                deviceService.getPorts(deviceId).parallelStream().anyMatch(port ->
+                        Objects.equals(port.annotations().value(PORT_NAME), interfaceName) && port.isEnabled());
+    }
+
+    private PortNumber portNumber(DeviceId deviceId, String interfaceName) {
+        Port port = deviceService.getPorts(deviceId).stream()
+                .filter(p -> p.isEnabled() &&
+                        Objects.equals(p.annotations().value(PORT_NAME), interfaceName))
+                .findAny().orElse(null);
+        return port != null ? port.number() : null;
+    }
+
+    private void setOutputTableForTunnel(DeviceId deviceId, int tableId,
+                                         PortNumber outPort, IpAddress serverIp,
+                                         boolean install) {
+        log.debug("setOutputTableForTunnel[{}]: deviceId={}, tableId={}, outPort={}, serverIp={}",
+                install ? "add" : "remove", deviceId, tableId, outPort, serverIp);
+
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+                .setOutput(outPort);
+
+        if (tunnelNicira) {
+            ExtensionTreatment extensionTreatment = buildTunnelExtension(deviceId, serverIp);
+            if (extensionTreatment == null) {
+                return;
+            }
+            treatment.extension(extensionTreatment, deviceId);
         }
 
         FlowRule flowRule = DefaultFlowRule.builder()
                 .forDevice(deviceId)
                 .withSelector(selector.build())
                 .withTreatment(treatment.build())
-                .withPriority(priority)
+                .withPriority(PRIORITY_VTAP_OUTPUT_RULE)
                 .makePermanent()
                 .forTable(tableId)
                 .fromApp(appId)
                 .build();
-        applyFlowRule(flowRule, true);
+
+        log.debug("setOutputTableForTunnel flowRule={}, install={}", flowRule, install);
+        applyFlowRule(flowRule, install);
     }
 
-    private ExtensionTreatment buildNiciraExtension(DeviceId id, int tableId) {
-        Driver driver = driverService.getDriver(id);
-        DriverHandler driverHandler =
-                    new DefaultDriverHandler(new DefaultDriverData(driver, id));
-        ExtensionTreatmentResolver resolver =
-                    driverHandler.behaviour(ExtensionTreatmentResolver.class);
+    private void setOutputTableForDrop(DeviceId deviceId, int tableId,
+                                       boolean install) {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
 
-        ExtensionTreatment extensionInstruction =
-                    resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
+        FlowRule flowRule = DefaultFlowRule.builder()
+                .forDevice(deviceId)
+                .withSelector(selector.build())
+                .withTreatment(treatment.build())
+                .withPriority(PRIORITY_VTAP_OUTPUT_DROP)
+                .makePermanent()
+                .forTable(tableId)
+                .fromApp(appId)
+                .build();
+        applyFlowRule(flowRule, install);
+    }
 
-        try {
-            extensionInstruction.setPropertyValue(TABLE_PROPERTY_KEY, ((short) tableId));
-        } catch (Exception e) {
-            log.error("Failed to set extension treatment for resubmit table {}", id);
+    private void setOutputTable(DeviceId deviceId, Mode mode,
+                                IpAddress serverIp, boolean install) {
+        log.debug("setOutputTable[{}]: deviceId={}, mode={}, serverIp={}",
+                install ? "add" : "remove", deviceId, mode, serverIp);
+
+        if (deviceId == null) {
+            return;
         }
 
-        return extensionInstruction;
+        switch (mode) {
+            case GRE:
+            case VXLAN:
+                String tunnelName = getTunnelName(mode);
+                PortNumber vtapPort = portNumber(deviceId, tunnelName);
+                if (vtapPort != null) {
+                    for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
+                        setOutputTableForTunnel(deviceId, VTAP_TABLES[idx][VTAP_TABLE_OUTPUT_IDX],
+                                vtapPort, serverIp, install);
+                    }
+                } else {
+                    log.warn("Vtap tunnel port {} doesn't exist", tunnelName);
+                }
+                break;
+            default:
+                log.warn("Invalid vtap network mode {}", mode);
+                break;
+        }
+    }
+
+    /**
+     * Returns tunnel destination extension treatment object.
+     *
+     * @param deviceId device id to apply this treatment
+     * @param remoteIp tunnel destination ip address
+     * @return extension treatment
+     */
+    private ExtensionTreatment buildTunnelExtension(DeviceId deviceId, IpAddress remoteIp) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
+            log.warn("Nicira extension treatment is not supported");
+            return null;
+        }
+
+        ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+        ExtensionTreatment treatment =
+                resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+        try {
+            treatment.setPropertyValue(TUNNEL_DST_EXTENSION, remoteIp.getIp4Address());
+            return treatment;
+        } catch (ExtensionPropertyException e) {
+            log.error("Failed to set nicira tunnelDst extension treatment for {}", deviceId);
+            return null;
+        }
+    }
+
+    private ExtensionTreatment buildResubmitExtension(DeviceId deviceId, int tableId) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device == null || !device.is(ExtensionTreatmentResolver.class)) {
+            log.warn("Nicira extension treatment is not supported");
+            return null;
+        }
+
+        ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+        ExtensionTreatment treatment =
+                resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
+
+        try {
+            treatment.setPropertyValue(TABLE_EXTENSION, ((short) tableId));
+            return treatment;
+        } catch (ExtensionPropertyException e) {
+            log.error("Failed to set nicira resubmit extension treatment for {}", deviceId);
+            return null;
+        }
     }
 
     private void createGroupTable(DeviceId deviceId, int groupId,
                                   List<Integer> tableIds, List<PortNumber> ports) {
         List<GroupBucket> buckets = Lists.newArrayList();
-        tableIds.forEach(tableId -> {
-            TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
-                    .extension(buildNiciraExtension(deviceId, tableId), deviceId);
-            GroupBucket bucket = DefaultGroupBucket
-                    .createAllGroupBucket(treatment.build());
-            buckets.add(bucket);
-        });
-        ports.forEach(port -> {
-            TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
-                    .setOutput(port);
-            GroupBucket bucket = DefaultGroupBucket
-                    .createAllGroupBucket(treatment.build());
-            buckets.add(bucket);
-        });
+        if (tableIds != null) {
+            tableIds.forEach(tableId -> {
+                TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+                        .extension(buildResubmitExtension(deviceId, tableId), deviceId);
+                GroupBucket bucket = DefaultGroupBucket
+                        .createAllGroupBucket(treatment.build());
+                buckets.add(bucket);
+            });
+        }
+        if (ports != null) {
+            ports.forEach(port -> {
+                TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
+                        .setOutput(port);
+                GroupBucket bucket = DefaultGroupBucket
+                        .createAllGroupBucket(treatment.build());
+                buckets.add(bucket);
+            });
+        }
 
         GroupDescription groupDescription = new DefaultGroupDescription(deviceId,
                 GroupDescription.Type.ALL,
@@ -685,42 +901,36 @@
         groupService.addGroup(groupDescription);
     }
 
-    private void applyFlowRule(FlowRule flowRule, boolean install) {
-        FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
-
-        flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
-
-        flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
-            @Override
-            public void onSuccess(FlowRuleOperations ops) {
-                log.debug("Installed flow rules for tapping");
-            }
-
-            @Override
-            public void onError(FlowRuleOperations ops) {
-                log.debug("Failed to install flow rules for tapping");
-            }
-        }));
+    private void removeGroupTable(DeviceId deviceId, int groupId) {
+        groupService.removeGroup(deviceId, getGroupKey(groupId), appId);
     }
 
+    /**
+     * Internal listener for device events.
+     */
     private class InternalDeviceListener implements DeviceListener {
-        @Override
-        public boolean isRelevant(DeviceEvent event) {
-            // do not allow to proceed without Mastership
-            DeviceId deviceId = event.subject().id();
-            return mastershipService.isLocalMaster(deviceId) &&
-                    event.subject().type() == Device.Type.SWITCH;
-        }
 
         @Override
         public void event(DeviceEvent event) {
             DeviceEvent.Type type = event.type();
-            DeviceId deviceId = event.subject().id();
-            log.trace("InternalDeviceListener deviceId={}, type={}", deviceId, type);
+            Device device = event.subject();
 
             switch (type) {
-                case DEVICE_ADDED:
-                    eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
+                case PORT_ADDED:
+                case PORT_UPDATED:
+                case PORT_REMOVED:
+                    String portName = event.port().annotations().value(PORT_NAME);
+                    if (portName.equals(getTunnelName(Mode.GRE)) ||
+                            portName.equals(getTunnelName(Mode.VXLAN))) {
+                        log.trace("InternalDeviceListener type={}, host={}", type, device);
+                        synchronized (syncInterface) {
+                            try {
+                                syncInterface.notifyAll();
+                            } catch (IllegalMonitorStateException e) {
+                                log.warn("Already syncInterface exited");
+                            }
+                        }
+                    }
                     break;
                 default:
                     break;
@@ -728,9 +938,60 @@
         }
     }
 
+    /**
+     * Internal listener for openstack node events.
+     */
+    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNodeEvent event) {
+            // do not allow to proceed without leadership and compute node
+            NodeId leader = leadershipService.getLeader(appId.name());
+            OpenstackNode osNode = event.subject();
+
+            return Objects.equals(localNodeId, leader) && osNode.type() == COMPUTE;
+        }
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNodeEvent.Type type = event.type();
+            OpenstackNode osNode = event.subject();
+            log.trace("InternalOpenstackNodeListener type={}, osNode={}", type, osNode);
+
+            eventExecutor.execute(() -> {
+                try {
+                    switch (type) {
+                        case OPENSTACK_NODE_COMPLETE:
+                            initVtapForNode(osNode);
+                            break;
+
+                        case OPENSTACK_NODE_REMOVED:
+                            clearVtapForNode(osNode);
+                            break;
+
+                        default:
+                            break;
+                    }
+                } catch (Exception e) {
+                    dumpStackTrace(log, e);
+                }
+            });
+        }
+    }
+
+    /**
+     * Internal listener for host events.
+     */
     private class InternalHostListener implements HostListener {
+
         @Override
         public boolean isRelevant(HostEvent event) {
+            Host host = event.subject();
+            if (!isValidHost(host)) {
+                log.debug("Invalid host detected, ignore it {}", host);
+                return false;
+            }
+
             // do not allow to proceed without leadership
             NodeId leader = leadershipService.getLeader(appId.name());
             return Objects.equals(localNodeId, leader);
@@ -740,102 +1001,173 @@
         public void event(HostEvent event) {
             HostEvent.Type type = event.type();
             Host host = event.subject();
-            log.trace("InternalHostListener hostId={}, type={}", host.id(), type);
+            Host prevHost = event.prevSubject();
+            log.trace("InternalHostListener {}: {} -> {}", type, prevHost, host);
 
-            switch (type) {
-                case HOST_ADDED:
-                    eventExecutor.execute(() -> updateHost(host, null));
-                    break;
+            eventExecutor.execute(() -> {
+                try {
+                    switch (event.type()) {
+                        case HOST_ADDED:
+                            updateHost(host, null);
+                            break;
 
-                case HOST_REMOVED:
-                    eventExecutor.execute(() -> updateHost(null, host));
-                    break;
+                        case HOST_REMOVED:
+                            updateHost(null, host);
+                            break;
 
-                case HOST_UPDATED:
-                case HOST_MOVED:
-                    eventExecutor.execute(() -> updateHost(host, event.prevSubject()));
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
+                        case HOST_MOVED:
+                        case HOST_UPDATED:
+                            updateHost(host, prevHost);
+                            break;
 
-    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
-
-        @Override
-        public boolean isRelevant(OpenstackNodeEvent event) {
-            // do not allow to proceed without leadership
-            NodeId leader = leadershipService.getLeader(appId.name());
-            return Objects.equals(localNodeId, leader) && event.subject().type() == COMPUTE;
-        }
-
-        @Override
-        public void event(OpenstackNodeEvent event) {
-            DeviceId deviceId = event.subject().intgBridge();
-            switch (event.type()) {
-                case OPENSTACK_NODE_CREATED:
-                case OPENSTACK_NODE_UPDATED:
-                    eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
-                    break;
-                case OPENSTACK_NODE_REMOVED:
-                    eventExecutor.execute(() -> clearFlowAndGroupByDeviceId(deviceId));
-                    break;
-                default:
-                    break;
-            }
+                        default:
+                            break;
+                    }
+                } catch (Exception e) {
+                    dumpStackTrace(log, e);
+                }
+            });
         }
     }
 
     // Store delegate to re-post events emitted from the store.
     private class InternalStoreDelegate implements OpenstackVtapStoreDelegate {
+
         @Override
         public void notify(OpenstackVtapEvent event) {
             OpenstackVtapEvent.Type type = event.type();
-            OpenstackVtap vTap = event.subject();
-            log.trace("vTapStoreDelegate vTap={}, type={}", vTap, type);
+            log.trace("InternalStoreDelegate {}: {} -> {}", type, event.prevSubject(), event.subject());
 
-            switch (type) {
-                case VTAP_ADDED:
-                    eventExecutor.execute(() -> {
-                        // Add new devices
-                        installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
-                                vTap.vTapCriterion(), true);
-                    });
-                    break;
+            if (Objects.equals(localNodeId, leadershipService.getLeader(appId.name()))) {
+                eventExecutor.execute(() -> {
+                    try {
+                        switch (type) {
+                            case VTAP_NETWORK_ADDED:
+                            case VTAP_NETWORK_UPDATED:
+                            case VTAP_NETWORK_REMOVED:
+                                // Update network
+                                updateVtapNetwork(event.openstackVtapNetwork(),
+                                        event.prevOpenstackVtapNetwork());
+                                break;
 
-                case VTAP_UPDATED:
-                    OpenstackVtap oldOpenstackVtap = event.prevSubject();
-                    eventExecutor.execute(() -> {
-                        // Remove excluded devices
-                        installFilterRule(
-                                Sets.difference(oldOpenstackVtap.txDeviceIds(),
-                                                            vTap.txDeviceIds()),
-                                Sets.difference(oldOpenstackVtap.rxDeviceIds(),
-                                                            vTap.rxDeviceIds()),
-                                oldOpenstackVtap.vTapCriterion(), false);
+                            case VTAP_ADDED:
+                            case VTAP_UPDATED:
+                            case VTAP_REMOVED:
+                                // Update vtap rule
+                                updateVtap(event.openstackVtap(),
+                                        event.prevOpenstackVtap());
+                                break;
 
-                        // Add new devices
-                        installFilterRule(
-                                Sets.difference(vTap.txDeviceIds(),
-                                                oldOpenstackVtap.txDeviceIds()),
-                                Sets.difference(vTap.rxDeviceIds(),
-                                                oldOpenstackVtap.rxDeviceIds()),
-                                vTap.vTapCriterion(), true);
-                    });
-                    break;
-
-                case VTAP_REMOVED:
-                    eventExecutor.execute(() -> {
-                        // Remove excluded devices
-                        installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
-                                vTap.vTapCriterion(), false);
-                    });
-                    break;
-                default:
-                    break;
+                            default:
+                                break;
+                        }
+                    } catch (Exception e) {
+                        dumpStackTrace(log, e);
+                    }
+                });
             }
             post(event);
         }
     }
+
+    private void applyVtap(OpenstackVtap vtap,
+                           OpenstackNode osNode,
+                           boolean install) {
+        if (vtap == null || osNode == null) {
+            return;
+        }
+
+        log.debug("applyVtap vtap={}, osNode={}, install={}", vtap, osNode, install);
+
+        DeviceId deviceId = osNode.intgBridge();
+        for (int idx = 0; idx < VTAP_TABLES.length; idx++) {
+            if ((idx == VTAP_TABLE_INBOUND_IDX &&
+                        vtap.type().isValid(Type.VTAP_TX) &&
+                        vtap.txDeviceIds().contains(deviceId)) ||
+                (idx != VTAP_TABLE_INBOUND_IDX &&
+                        vtap.type().isValid(Type.VTAP_RX) &&
+                        vtap.rxDeviceIds().contains(deviceId))) {
+                connectTables(deviceId,
+                        VTAP_TABLES[idx][VTAP_TABLE_INPUT_IDX],
+                        VTAP_TABLES[idx][VTAP_TABLE_GROUP_IDX],
+                        true,
+                        vtap.vtapCriterion(), PRIORITY_VTAP_RULE, install);
+            }
+        }
+    }
+
+    private void updateVtap(OpenstackVtap vtap,
+                            OpenstackVtap prevVtap) {
+        if (Objects.equals(vtap, prevVtap)) {
+            return;
+        }
+
+        Set<DeviceId> prevTxDeviceIds = (prevVtap != null ? prevVtap.txDeviceIds() : ImmutableSet.of());
+        Set<DeviceId> txDeviceIds = (vtap != null ? vtap.txDeviceIds() : ImmutableSet.of());
+        Set<DeviceId> prevRxDeviceIds = (prevVtap != null ? prevVtap.rxDeviceIds() : ImmutableSet.of());
+        Set<DeviceId> rxDeviceIds = (vtap != null ? vtap.rxDeviceIds() : ImmutableSet.of());
+
+        // Remake all vtap rule
+        if (prevVtap != null) {
+            Set<DeviceId> deviceIds = Sets.newHashSet();
+            deviceIds.addAll(Sets.difference(prevTxDeviceIds, txDeviceIds));
+            deviceIds.addAll(Sets.difference(prevRxDeviceIds, rxDeviceIds));
+            deviceIds.stream()
+                    .map(deviceId -> osNodeService.node(deviceId))
+                    .filter(osNode -> Objects.nonNull(osNode) &&
+                            osNode.type() == COMPUTE)
+                    .forEach(osNode -> applyVtap(prevVtap, osNode, false));
+        }
+        if (vtap != null) {
+            Set<DeviceId> deviceIds = Sets.newHashSet();
+            deviceIds.addAll(Sets.difference(txDeviceIds, prevTxDeviceIds));
+            deviceIds.addAll(Sets.difference(rxDeviceIds, prevRxDeviceIds));
+            deviceIds.stream()
+                    .map(deviceId -> osNodeService.node(deviceId))
+                    .filter(osNode -> Objects.nonNull(osNode) &&
+                            osNode.type() == COMPUTE && osNode.state() == COMPLETE)
+                    .forEach(osNode -> applyVtap(vtap, osNode, true));
+        }
+    }
+
+    // create/remove tunnel interface and output table
+    private boolean applyVtapNetwork(OpenstackVtapNetwork vtapNetwork,
+                                     OpenstackNode osNode,
+                                     boolean install) {
+        if (vtapNetwork == null || osNode == null) {
+            return false;
+        }
+
+        if (install) {
+            if (setTunnelInterface(osNode, vtapNetwork, true)) {
+                setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), true);
+                store.addDeviceToVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge());
+                return true;
+            }
+        } else {
+            Set<DeviceId> deviceIds = getVtapNetworkDevices();
+            if (deviceIds != null && deviceIds.contains(osNode.intgBridge())) {
+                store.removeDeviceFromVtapNetwork(VTAP_NETWORK_KEY, osNode.intgBridge());
+                setOutputTable(osNode.intgBridge(), vtapNetwork.mode(), vtapNetwork.serverIp(), false);
+                setTunnelInterface(osNode, vtapNetwork, false);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void updateVtapNetwork(OpenstackVtapNetwork network,
+                                   OpenstackVtapNetwork prevNetwork) {
+        // Remake all output tables
+        if (prevNetwork != null) {
+            osNodeService.completeNodes(COMPUTE)
+                    .forEach(osNode -> applyVtapNetwork(prevNetwork, osNode, false));
+        }
+        if (network != null) {
+            osNodeService.completeNodes(COMPUTE).stream()
+                    .filter(osNode -> osNode.state() == COMPLETE)
+                    .forEach(osNode -> applyVtapNetwork(network, osNode, true));
+        }
+    }
+
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java
index cc5feb7..7790c8a 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/impl/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * Implementation for Openstack vtap.
+ * Implementation for openstack vtap.
  */
 package org.onosproject.openstackvtap.impl;
\ No newline at end of file
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java
index 1e67693..69919be 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java
@@ -16,15 +16,31 @@
 package org.onosproject.openstackvtap.util;
 
 import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.Host;
+import org.onosproject.net.behaviour.TunnelDescription;
 import org.onosproject.net.group.DefaultGroupKey;
 import org.onosproject.net.group.GroupKey;
 import org.onosproject.openstackvtap.api.OpenstackVtap;
+import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
+import org.slf4j.Logger;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.charset.StandardCharsets;
+
+import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_NETWORK_ID;
+import static org.onosproject.openstacknetworking.api.Constants.ANNOTATION_PORT_ID;
 
 /**
- * An utility that used in openstack vTap app.
+ * An utilities that used in openstack vtap app.
  */
 public final class OpenstackVtapUtil {
 
+    private static final String VTAP_TUNNEL_GRE = "vtap_gre";
+    private static final String VTAP_TUNNEL_VXLAN = "vtap_vxlan";
     private static final String VTAP_GROUP_KEY = "VTAP_GROUP_KEY";
 
     /**
@@ -34,31 +50,10 @@
     }
 
     /**
-     * Obtains vTap type from the given string.
-     *
-     * @param str string
-     * @return vTap type
-     */
-    public static OpenstackVtap.Type getVtapTypeFromString(String str) {
-        switch (str) {
-            case "all":
-                return OpenstackVtap.Type.VTAP_ALL;
-            case "tx":
-                return OpenstackVtap.Type.VTAP_TX;
-            case "rx":
-                return OpenstackVtap.Type.VTAP_RX;
-            case "none":
-                return OpenstackVtap.Type.VTAP_NONE;
-            default:
-                throw new IllegalArgumentException("Invalid vTap type string");
-        }
-    }
-
-    /**
      * Obtains IP protocol type from the given string.
      *
-     * @param str string
-     * @return vTap type
+     * @param str protocol string
+     * @return IP protocol number
      */
     public static byte getProtocolTypeFromString(String str) {
         switch (str) {
@@ -68,12 +63,132 @@
                 return IPv4.PROTOCOL_UDP;
             case "icmp":
                 return IPv4.PROTOCOL_ICMP;
-            default:
+            case "any":
                 return 0;
+            default:
+                throw new IllegalArgumentException("Invalid vtap protocol string");
         }
     }
 
+    /**
+     * Obtains openstack vtap type from the given string.
+     *
+     * @param str vtap type string
+     * @return vtap type
+     */
+    public static OpenstackVtap.Type getVtapTypeFromString(String str) {
+        switch (str) {
+            case "all":
+                return OpenstackVtap.Type.VTAP_ALL;
+            case "rx":
+                return OpenstackVtap.Type.VTAP_RX;
+            case "tx":
+                return OpenstackVtap.Type.VTAP_TX;
+            case "any":
+                return OpenstackVtap.Type.VTAP_ANY;
+            default:
+                throw new IllegalArgumentException("Invalid vtap type string");
+        }
+    }
+
+    /**
+     * Checks whether the given IP address is included in vtap criterion with
+     * TX and RX directions by given vtap type.
+     *
+     * @param type      vtap type
+     * @param criterion vtap criterion
+     * @param ip        IP address to check
+     * @return true on match address, false otherwise
+     */
+    public static boolean containsIp(OpenstackVtap.Type type, OpenstackVtapCriterion criterion, IpAddress ip) {
+        boolean isTxEdge = type.isValid(OpenstackVtap.Type.VTAP_TX) &&
+                criterion.srcIpPrefix().contains(ip);
+        boolean isRxEdge = type.isValid(OpenstackVtap.Type.VTAP_RX) &&
+                criterion.dstIpPrefix().contains(ip);
+        return isTxEdge || isRxEdge;
+    }
+
+    /**
+     * Checks the host validation from annotation information.
+     *
+     * @param host host to check
+     * @return true on validate, false otherwise
+     */
+    public static boolean isValidHost(Host host) {
+        return !host.ipAddresses().isEmpty() &&
+                host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
+                host.annotations().value(ANNOTATION_PORT_ID) != null;
+    }
+
+    /**
+     * Checks whether the given IP prefix is contained in the first host rather
+     * than in the second host.
+     *
+     * @param host1     first host instance
+     * @param host2     second host instance
+     * @param ipPrefix  IP prefix to be looked up
+     * @return a negative integer, zero, or a positive integer as the
+     *         first argument is less than, equal to, or greater than the
+     *         second.
+     */
+    public static int hostCompareIp(Host host1, Host host2, IpPrefix ipPrefix) {
+        if ((host1 == null || host1.ipAddresses().stream().noneMatch(ipPrefix::contains)) &&
+                (host2 != null || host2.ipAddresses().stream().anyMatch(ipPrefix::contains))) {
+            return -1;
+        } else if ((host1 != null && host1.ipAddresses().stream().anyMatch(ipPrefix::contains)) &&
+                (host2 == null || host2.ipAddresses().stream().noneMatch(ipPrefix::contains))) {
+            return 1;
+        }
+        return 0;
+    }
+
+    /**
+     * Obtains flow group key from the given id.
+     *
+     * @param groupId flow group identifier
+     * @return flow group key
+     */
     public static GroupKey getGroupKey(int groupId) {
         return new DefaultGroupKey((VTAP_GROUP_KEY + Integer.toString(groupId)).getBytes());
     }
+
+    /**
+     * Obtains tunnel interface name from the given openstack vtap network mode.
+     *
+     * @param mode vtap network mode
+     * @return tunnel interface name
+     */
+    public static String getTunnelName(OpenstackVtapNetwork.Mode mode) {
+        switch (mode) {
+            case GRE:
+                return VTAP_TUNNEL_GRE;
+            case VXLAN:
+                return VTAP_TUNNEL_VXLAN;
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Obtains tunnel description type from the given openstack vtap network mode.
+     *
+     * @param mode vtap network mode
+     * @return tunnel description type
+     */
+    public static TunnelDescription.Type getTunnelType(OpenstackVtapNetwork.Mode mode) {
+        return TunnelDescription.Type.valueOf(mode.toString());
+    }
+
+    /**
+     * Print stack trace of given exception.
+     *
+     * @param log logger for using print
+     * @param e   exception to print
+     */
+    public static void dumpStackTrace(Logger log, Exception e) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        e.printStackTrace(new PrintStream(outputStream));
+        log.error("\n{}", new String(outputStream.toByteArray(), StandardCharsets.UTF_8));
+    }
+
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java
index 905d1c4..b9a3906 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/package-info.java
@@ -15,6 +15,6 @@
  */
 
 /**
- * Openstack vTap utility package.
+ * Utility package that used in openstack vtap app.
  */
 package org.onosproject.openstackvtap.util;
\ No newline at end of file
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkCodecRegister.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkCodecRegister.java
new file mode 100644
index 0000000..7ac689a
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkCodecRegister.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2018-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.openstackvtap.web;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.codec.CodecService;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
+import org.onosproject.openstackvtap.codec.OpenstackVtapNetworkCodec;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the JSON codec brokering service for openstack vtap network.
+ */
+@Component(immediate = true)
+public class OpenstackVtapNetworkCodecRegister {
+
+    private final org.slf4j.Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CodecService codecService;
+
+    @Activate
+    protected void activate() {
+        codecService.registerCodec(OpenstackVtapNetwork.class, new OpenstackVtapNetworkCodec());
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        codecService.unregisterCodec(OpenstackVtapNetwork.class);
+
+        log.info("Stopped");
+    }
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkWebResource.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkWebResource.java
new file mode 100644
index 0000000..3900bbd
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapNetworkWebResource.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2018-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.openstackvtap.web;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
+import org.onosproject.openstackvtap.api.OpenstackVtapNetwork;
+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.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.io.InputStream;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static org.onlab.util.Tools.readTreeFromStream;
+
+/**
+ * Handles REST API call of openstack vtap network.
+ */
+@Path("vtap-network")
+public class OpenstackVtapNetworkWebResource extends AbstractWebResource {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String MESSAGE_NETWORK = "Received openstackVtapNetwork {} request";
+    private static final String ERROR_DUPLICATE = "Already has data {}";
+    private static final String ERROR_NOTFOUND = "No data to update {}";
+    private static final String CREATE = "CREATE";
+    private static final String READ = "READ";
+    private static final String UPDATE = "UPDATE";
+    private static final String DELETE = "DELETE";
+
+    private static final String NETWORK = "network";
+
+    private final OpenstackVtapAdminService vtapAdminService = get(OpenstackVtapAdminService.class);
+
+    /**
+     * Creates a openstack vtap network from the JSON input stream.
+     *
+     * @param input openstack vtap network JSON input stream
+     * @return 200 OK if the JSON is correct
+     *         400 BAD_REQUEST if the JSON is malformed
+     *         409 CONFLICT if already the vtap network exists
+     * @onos.rsModel OpenstackVtapNetwork
+     */
+    @POST
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response createNetwork(InputStream input) {
+        log.info(MESSAGE_NETWORK, CREATE);
+
+        OpenstackVtapNetwork vtapNetwork = readNetworkConfiguration(input);
+        if (vtapNetwork == null) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+
+        if (vtapAdminService.createVtapNetwork(vtapNetwork.mode(),
+                vtapNetwork.networkId(), vtapNetwork.serverIp()) == null) {
+            log.warn(ERROR_DUPLICATE, vtapNetwork);
+            return Response.status(Response.Status.CONFLICT).build();
+        }
+        return Response.ok().build();
+    }
+
+    /**
+     * Updates openstack vtap network from the JSON input stream.
+     *
+     * @param input openstack vtap network JSON input stream
+     * @return 200 OK if the JSON is correct
+     *         400 BAD_REQUEST if the JSON is malformed
+     *         404 NOT_FOUND if vtap network is not exists
+     * @onos.rsModel OpenstackVtapNetwork
+     */
+    @PUT
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response updateNetwork(InputStream input) {
+        log.info(MESSAGE_NETWORK, UPDATE);
+
+        OpenstackVtapNetwork vtapNetwork = readNetworkConfiguration(input);
+        if (vtapNetwork == null) {
+            return Response.status(Response.Status.BAD_REQUEST).build();
+        }
+
+        if (vtapAdminService.updateVtapNetwork(vtapNetwork) == null) {
+            log.warn(ERROR_NOTFOUND, vtapNetwork);
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+        return Response.ok().build();
+    }
+
+    /**
+     * Get openstack vtap network.
+     *
+     * @return 200 OK with openstack vtap network
+     *         404 NOT_FOUND if openstackvtap network is not exists
+     * @onos.rsModel OpenstackVtapNetwork
+     */
+    @GET
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getNetwork() {
+        log.info(MESSAGE_NETWORK, READ);
+
+        OpenstackVtapNetwork vtapNetwork = vtapAdminService.getVtapNetwork();
+        if (vtapNetwork == null) {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+
+        JsonNode jsonNode = codec(OpenstackVtapNetwork.class).encode(vtapNetwork, this);
+        return Response.ok(jsonNode, MediaType.APPLICATION_JSON_TYPE).build();
+    }
+
+    /**
+     * Removes openstack network.
+     *
+     * @return 200 OK on removing success
+     *         404 NOT_FOUND if openstackvtap network is not exists
+     * @onos.rsModel OpenstackVtapNetwork
+     */
+    @DELETE
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response deleteNetwork() {
+        log.info(MESSAGE_NETWORK, DELETE);
+
+        if (vtapAdminService.removeVtapNetwork() == null) {
+            return Response.status(Response.Status.NOT_FOUND).build();
+        }
+
+        return Response.ok().build();
+    }
+
+    private OpenstackVtapNetwork readNetworkConfiguration(InputStream input) {
+        try {
+            JsonNode jsonTree = readTreeFromStream(mapper().enable(INDENT_OUTPUT), input);
+            ObjectNode vtap = (ObjectNode) jsonTree.get(NETWORK);
+            return codec(OpenstackVtapNetwork.class).decode(vtap, this);
+        } catch (Exception e) {
+            log.error(e.toString());
+            return null;
+        }
+    }
+
+}
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java
index 5d0caf2..96aa9a7 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebApplication.java
@@ -20,11 +20,12 @@
 import java.util.Set;
 
 /**
- * Openstack vTap REST APIs web application.
+ * Openstack vtap REST APIs web application.
  */
 public class OpenstackVtapWebApplication extends AbstractWebApplication {
     @Override
     public Set<Class<?>> getClasses() {
-        return getClasses(OpenstackVtapWebResource.class);
+        return getClasses(OpenstackVtapWebResource.class,
+                OpenstackVtapNetworkWebResource.class);
     }
 }
diff --git a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java
index 89468ac..f4c3084 100644
--- a/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/web/OpenstackVtapWebResource.java
@@ -36,8 +36,6 @@
      * OpenstackVtapServiceImpl method.
      *
      * @return 200 OK
-     *
-     * @onos.rsModel dummy
      */
     @GET
     @Produces(MediaType.APPLICATION_JSON)
diff --git a/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index c3fa886..7fd8ccf 100644
--- a/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstackvtap/app/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -42,7 +42,7 @@
         </command>
 
         <command>
-            <action class="org.onosproject.openstackvtap.cli.OpenstackVtapOutputCommand" />
+            <action class="org.onosproject.openstackvtap.cli.OpenstackVtapNetworkListCommand" />
         </command>
     </command-bundle>
 
diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css
new file mode 100644
index 0000000..2595410
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.css
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/*
+ ONOS GUI -- OpenstackVtap -- CSS file
+ */
+
+/* --- Topo Vtap Panel --- */
+#topo-p-vtap {
+    padding: 16px;
+    top: 190px;
+    left: 20px;
+}
+
+.topo-p td input.number-input {
+    width: 50px;
+}
+
+.topo-p div.footer tbody {
+    float: right;
+}
+
+.topo-p div.footer tbody input.button-input {
+    background-color: #5b99d2;
+    border: 0;
+    border-radius: 3px;
+    color: #fff;
+    height: 25px;
+    width: 60px;
+    padding: 1px 0 0 0;
+}
+
+#vtap-dialog {
+    padding: 16px;
+    top: 190px;
+    left: 20px;
+}
diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html
new file mode 100644
index 0000000..f1a1b72
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.html
@@ -0,0 +1,4 @@
+<!-- partial HTML -->
+<div id="ov-openstackvtap">
+    <p>This is a hidden view .. just a placeholder to house the javascript</p>
+</div>
diff --git a/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js
new file mode 100644
index 0000000..9b20eb0
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/app/view/openstackvtap/openstackvtap.js
@@ -0,0 +1,406 @@
+/*
+ * Copyright 2018-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.
+ */
+
+/*
+ ONOS GUI -- Openstack Vtap View Module
+ */
+(function () {
+    'use strict';
+
+    // injected refs
+    var $log, tov, ovts, fs, wss, tts, api, gs, ps, ds;
+
+    // constants
+    var pCls = 'topo-p',
+        idVta = 'topo-p-vtap',
+        idVDi = 'vtap-dialog',
+        vtapPanelOpts = {
+            edge: 'left',
+            width: 330, // vtap panel width
+        }
+
+    // panels
+    var vtap;
+
+    // selected info
+    var selectedItem;
+
+    // constants
+    var osvIsActReq = 'openstackVtapIsActivatedRequest',
+        osvIsActResp =  'openstackVtapIsActivatedResponse',
+        osvCreateReq = 'openstackVtapCreateRequest',
+        osvCreateResp = 'openstackVtapCreateResponse';
+
+    // function to be replaced by the localization bundle function
+    var topoLion = function (x) {
+        return '#ttrafov#' + x + '#';
+    };
+
+    var overlay = {
+        overlayId: 'vtap-overlay',
+        glyphId: 'm_details',
+        tooltip: 'Openstack Vtap Overlay',
+
+        activate: function () {
+            $log.debug("Openstack Vtap topology overlay ACTIVATED");
+        },
+        deactivate: function () {
+            destroyVtapPanel();
+            $log.debug("Openstack Vtap topology overlay DEACTIVATED");
+        },
+
+        // detail panel button definitions
+        buttons: {
+            createHostVtapBtn: {
+                gid: 'm_details',
+                tt: 'Create Host-to-Host Vtap',
+                cb: displayVtap
+            },
+            showRelatedTraffic: {
+                gid: 'm_relatedIntents',
+                tt: function () { return topoLion('tr_btn_show_related_traffic'); },
+                cb: function (data) { tts.showRelatedIntents(); },
+            },
+        },
+
+        hooks: {
+            multi: function (selectOrder) {
+                var selectedHost = new Object();
+                for (var index in selectOrder) {
+                    if (index == 0) {
+                        selectedHost.src = selectOrder[index];
+                    } else if (index == 1) {
+                        selectedHost.dst = selectOrder[index];
+                    }
+                }
+
+                selectedItem = selectedHost;
+
+                tov.addDetailButton('showRelatedTraffic');
+                if(selectOrder.length == 2) {
+                    tov.addDetailButton('createHostVtapBtn');
+                }
+            }
+        }
+    };
+
+    // Panel API
+    function createTopoPanel(id, opts) {
+        var p = ps.createPanel(id, opts),
+            pid = id,
+            header, body, footer;
+        p.classed(pCls, true);
+
+        function panel() {
+            return p;
+        }
+
+        function hAppend(x) {
+            return header.append(x);
+        }
+
+        function bAppend(x) {
+            return body.append(x);
+        }
+
+        function fAppend(x) {
+            return footer.append(x);
+        }
+
+        function setup() {
+            p.empty();
+
+            p.append('div').classed('header', true);
+            p.append('div').classed('body', true);
+            p.append('div').classed('footer', true);
+
+            header = p.el().select('.header');
+            body = p.el().select('.body');
+            footer = p.el().select('.footer');
+        }
+
+        function destroy() {
+            ps.destroyPanel(pid);
+        }
+
+        return {
+            panel: panel,
+            setup: setup,
+            destroy: destroy,
+            appendHeader: hAppend,
+            appendBody: bAppend,
+            appendFooter: fAppend,
+        };
+    }
+
+    function hideVtapPanel() {
+        vtap.panel().hide();
+    }
+
+    function destroyVtapPanel() {
+        if(vtap != null) {
+            vtap.destroy();
+        }
+        vtap = null;
+    }
+
+    function addInput(tbody, type, id, label, value) {
+        var tr = tbody.append('tr'),
+            lab;
+        if (typeof label === 'string') {
+            lab = label.replace(/_/g, ' ');
+        } else {
+            lab = label;
+        }
+
+        tr.append('td').attr('class', 'label').text(lab + ' :');
+
+        if (type == 'radio') {
+            var td = tr.append('td');
+            for(var index in value) {
+                if(index == 0) {
+                    td.append('input').classed( type + '-input', true)
+                                          .attr('type', type)
+                                          .attr('value', value[index])
+                                          .attr('name', label)
+                                          .attr('id', id)
+                                          .attr('checked', 'true');
+                } else {
+                    td.append('input').classed( type + '-input', true)
+                                          .attr('type', type)
+                                          .attr('name', label)
+                                          .attr('id', id)
+                                          .attr('value', value[index]);
+                }
+                td.append('span').text(value[index]);
+            }
+        } else {
+            tr.append('td').append('input').classed(type + '-input', true).attr('type', type)
+                .attr('id', id).attr('value', value);
+        }
+    }
+
+    function addButton(tr, callback, value) {
+        tr.append('td').append('input').classed('button-input', true).attr('type', 'button')
+                        .attr('value', value).on('click', callback);
+    }
+
+    function makeButton(callback, text, keyName) {
+        var cb = fs.isF(callback),
+            key = fs.isS(keyName);
+
+        function invoke() {
+            cb && cb();
+        }
+
+        return createDiv('vtap-button')
+            .text(text)
+            .on('click', invoke);
+    }
+
+    function createDiv(cls) {
+        var div = d3.select(document.createElement('div'));
+        if (cls) {
+            div.classed(cls, true);
+        }
+        return div;
+    }
+
+    function addInput(tbody, type, id, label, value) {
+        var tr = tbody.append('tr'),
+            lab;
+        if (typeof label === 'string') {
+            lab = label.replace(/_/g, ' ');
+        } else {
+            lab = label;
+        }
+
+        tr.append('td').attr('class', 'label').text(lab + ' :');
+
+        if (type == 'radio') {
+            var td = tr.append('td');
+            for(var index in value) {
+                if(index == 0) {
+                    td.append('input').classed( type + '-input', true)
+                                          .attr('type', type)
+                                          .attr('value', value[index])
+                                          .attr('name', label)
+                                          .attr('id', id)
+                                          .attr('checked', 'true');
+                } else {
+                    td.append('input').classed( type + '-input', true)
+                                          .attr('type', type)
+                                          .attr('name', label)
+                                          .attr('id', id)
+                                          .attr('value', value[index]);
+                }
+                td.append('span').text(value[index]);
+            }
+        } else {
+            tr.append('td').append('input').classed(type + '-input', true).attr('type', type)
+                .attr('id', id).attr('value', value);
+        }
+    }
+
+    function addButton(tr, callback, value) {
+        tr.append('td').append('input').classed('button-input', true).attr('type', 'button')
+                        .attr('value', value).on('click', callback);
+    }
+
+    function makeButton(callback, text, keyName) {
+        var cb = fs.isF(callback),
+            key = fs.isS(keyName);
+
+        function invoke() {
+            cb && cb();
+        }
+
+        return createDiv('vtap-button')
+            .text(text)
+            .on('click', invoke);
+    }
+
+    function createDiv(cls) {
+        var div = d3.select(document.createElement('div'));
+        if (cls) {
+            div.classed(cls, true);
+        }
+        return div;
+    }
+
+    function displayVtap() {
+        $log.debug("sendEvent openstackVtapIsActivatedRequest: ", selectedItem);
+        wss.sendEvent(osvIsActReq, selectedItem);
+    }
+
+    function respIsActCb(selected) {
+        $log.debug("openstackVtapIsActivatedResponse: ", selected);
+        if(vtap == null) {
+            vtap = createTopoPanel(idVta, vtapPanelOpts);
+        }
+        vtap.setup();
+
+        var svg = vtap.appendHeader('div')
+                     .classed('icon', true)
+                     .append('svg'),
+            title = vtap.appendHeader('h2'),
+            table = vtap.appendBody('table'),
+            tbody = table.append('tbody'),
+            glyphId = 'm_details';
+
+        gs.addGlyph(svg, glyphId, 30, 0, [1, 1]);
+
+        title.text('Create OpenstackVtap');
+
+        addInput(tbody, 'text', 'srcIp', 'Source IP', selected.srcName);
+        addInput(tbody, 'text', 'dstIp', 'Destination IP', selected.dstName);
+        addInput(tbody, 'radio', 'ipProto', 'Protocol', selected.ipProtoList);
+        addInput(tbody, 'number', 'srcPort', 'Source Port', "");
+        addInput(tbody, 'number', 'dstPort', 'Destination Port', "");
+        addInput(tbody, 'radio', 'vtapType', 'Type', selected.vtapTypeList);
+
+        vtap.appendFooter('hr');
+        var footTr = vtap.appendFooter('table').append('tbody').append('tr');
+
+        addButton(footTr, createVtap, 'Create');
+        addButton(footTr, hideVtapPanel, 'Cancel');
+
+        vtap.panel().show();
+    }
+
+    function createVtap() {
+        var vtapInfo = {};
+
+        vtapInfo.srcIp = document.getElementById('srcIp').value;
+        vtapInfo.dstIp = document.getElementById('dstIp').value;
+        vtapInfo.ipProto = document.querySelector('input[name="Protocol"]:checked').value;
+        vtapInfo.srcPort = document.getElementById('srcPort').value;
+        vtapInfo.dstPort = document.getElementById('dstPort').value;
+        vtapInfo.vtapType = document.querySelector('input[name="Type"]:checked').value;
+
+        if(vtapInfo.srcPort == ""){
+            vtapInfo.srcPort = 0;
+        }
+        if(vtapInfo.dstPort == ""){
+            vtapInfo.dstPort = 0;
+        }
+
+        $log.debug("sendEvent openstackVtapCreateRequest: ", vtapInfo);
+        wss.sendEvent(osvCreateReq, vtapInfo);
+        hideVtapPanel();
+
+    }
+
+    function respCreateCb(result) {
+        $log.debug("respCreateCb: ", result);
+
+        function dOK() {
+            if(result.result == "Failed"){
+                displayVtap(selectedItem);
+            } else {
+                destroyVtapPanel();
+            }
+        }
+
+        function createContent(value) {
+            var content = ds.createDiv();
+            content.append('p').text(value);
+            return content;
+        }
+
+        ds.openDialog(idVDi, vtapPanelOpts)
+            .setTitle("Create Vtap " + result.result)
+            .addContent(createContent(result.value))
+            .addOk(dOK);
+    }
+
+    // invoke code to register with the overlay service
+    angular.module('ovOpenstackvtap', [])
+        .factory('OpenstackVtapTopovService',
+        ['$log', 'FnService', 'WebSocketService', 'GlyphService', 'PanelService', 'DialogService',
+
+        function (_$log_, _fs_, _wss_, _gs_, _ps_, _ds_) {
+            $log = _$log_;
+            fs = _fs_;
+            wss = _wss_;
+            gs = _gs_;
+            ps = _ps_;
+            ds = _ds_;
+
+            var handlers = {},
+                vtapOverlay = 'vtap-overlay',
+                defaultOverlay = 'traffic';
+
+            handlers[osvIsActResp] = respIsActCb;
+            handlers[osvCreateResp] = respCreateCb;
+
+            wss.bindHandlers(handlers);
+
+            return {
+                displayVtap: displayVtap
+            };
+        }])
+        .run(['$log', 'TopoOverlayService', 'OpenstackVtapTopovService', 'TopoTrafficService',
+
+            function (_$log_, _tov_, _ovts_, _tts_) {
+                $log = _$log_;
+                tov = _tov_;
+                ovts = _ovts_;
+                tts = _tts_;
+                tov.register(overlay);
+            }]
+        );
+}());
diff --git a/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json b/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json
new file mode 100644
index 0000000..32fa001
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/definitions/OpenstackVtapNetwork.json
@@ -0,0 +1,30 @@
+{
+  "type": "object",
+  "required": [
+    "network"
+  ],
+  "properties": {
+    "network": {
+      "type": "object",
+      "required": [
+        "mode",
+        "networkId",
+        "serverIp"
+      ],
+      "properties": {
+        "mode": {
+          "type": "string",
+          "example": "GRE"
+        },
+        "networkId": {
+          "type": "int",
+          "example": 1000
+        },
+        "serverIp": {
+          "type": "string",
+          "example": "10.20.0.1"
+        }
+      }
+    }
+  }
+}
diff --git a/apps/openstackvtap/app/src/main/resources/definitions/dummy.json b/apps/openstackvtap/app/src/main/resources/definitions/dummy.json
deleted file mode 100644
index 7a73a41..0000000
--- a/apps/openstackvtap/app/src/main/resources/definitions/dummy.json
+++ /dev/null
@@ -1,2 +0,0 @@
-{
-}
\ No newline at end of file
diff --git a/apps/openstackvtap/app/src/main/resources/gui/css.html b/apps/openstackvtap/app/src/main/resources/gui/css.html
new file mode 100644
index 0000000..9c1889b
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/gui/css.html
@@ -0,0 +1 @@
+<link rel="stylesheet" href="app/view/openstackvtap/openstackvtap.css">
diff --git a/apps/openstackvtap/app/src/main/resources/gui/js.html b/apps/openstackvtap/app/src/main/resources/gui/js.html
new file mode 100644
index 0000000..46a4bb4
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/resources/gui/js.html
@@ -0,0 +1 @@
+<script src="app/view/openstackvtap/openstackvtap.js"></script>
diff --git a/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml b/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml
index 8b44537..109fbe7 100644
--- a/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml
+++ b/apps/openstackvtap/app/src/main/webapp/WEB-INF/web.xml
@@ -18,7 +18,7 @@
          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>Openstack vTap REST API v1.0</display-name>
+    <display-name>Openstack Vtap REST API v1.0</display-name>
 
     <security-constraint>
         <web-resource-collection>
diff --git a/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java b/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java
index fd9fbf5..412d1d6 100644
--- a/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java
+++ b/apps/openstackvtap/app/src/test/java/org/onosproject/openstackvtap/impl/DefaultOpenstackVtapTest.java
@@ -34,8 +34,8 @@
  */
 public class DefaultOpenstackVtapTest {
 
-    private static final OpenstackVtapId VTAP_ID_1 = OpenstackVtapId.vTapId();
-    private static final OpenstackVtapId VTAP_ID_2 = OpenstackVtapId.vTapId();
+    private static final OpenstackVtapId VTAP_ID_1 = OpenstackVtapId.vtapId();
+    private static final OpenstackVtapId VTAP_ID_2 = OpenstackVtapId.vtapId();
 
     private static final OpenstackVtap.Type VTAP_TYPE_1 = OpenstackVtap.Type.VTAP_TX;
     private static final OpenstackVtap.Type VTAP_TYPE_2 = OpenstackVtap.Type.VTAP_RX;
@@ -74,10 +74,9 @@
         vtap1 = builder1
                     .id(VTAP_ID_1)
                     .type(VTAP_TYPE_1)
-                    .vTapCriterion(CRITERION_1)
+                    .vtapCriterion(CRITERION_1)
                     .txDeviceIds(Sets.newHashSet())
                     .rxDeviceIds(Sets.newHashSet())
-                    .annotations()
                     .build();
 
         OpenstackVtap.Builder builder2 = DefaultOpenstackVtap.builder();
@@ -85,10 +84,9 @@
         sameAsVtap1 = builder2
                         .id(VTAP_ID_1)
                         .type(VTAP_TYPE_1)
-                        .vTapCriterion(CRITERION_1)
+                        .vtapCriterion(CRITERION_1)
                         .txDeviceIds(Sets.newHashSet())
                         .rxDeviceIds(Sets.newHashSet())
-                        .annotations()
                         .build();
 
         OpenstackVtap.Builder builder3 = DefaultOpenstackVtap.builder();
@@ -96,10 +94,9 @@
         vtap2 = builder3
                     .id(VTAP_ID_2)
                     .type(VTAP_TYPE_2)
-                    .vTapCriterion(CRITERION_2)
+                    .vtapCriterion(CRITERION_2)
                     .txDeviceIds(Sets.newHashSet())
                     .rxDeviceIds(Sets.newHashSet())
-                    .annotations()
                     .build();
 
     }
@@ -116,7 +113,7 @@
         DefaultOpenstackVtap vtap = (DefaultOpenstackVtap) vtap1;
 
         assertThat(vtap.id(), is(VTAP_ID_1));
-        assertThat(vtap.vTapCriterion(), is(CRITERION_1));
+        assertThat(vtap.vtapCriterion(), is(CRITERION_1));
         assertThat(vtap.type(), is(VTAP_TYPE_1));
     }