Add group table, flow rules by listening to openstack node events

Change-Id: Ifbb1ae9c812e9bc24260e960c17b5430dcf59a11
diff --git a/apps/openstackvtap/app/BUCK b/apps/openstackvtap/app/BUCK
index 686a3a0..dc3f55c 100644
--- a/apps/openstackvtap/app/BUCK
+++ b/apps/openstackvtap/app/BUCK
@@ -9,6 +9,7 @@
     '//cli:onos-cli',
     '//lib:org.apache.karaf.shell.console',
     '//apps/openstacknetworking/api:onos-apps-openstacknetworking-api',
+    '//apps/openstacknode/api:onos-apps-openstacknode-api',
     '//apps/openstackvtap/api:onos-apps-openstackvtap-api',
 ]
 
diff --git a/apps/openstackvtap/app/pom.xml b/apps/openstackvtap/app/pom.xml
index f8b40b4..0b8e8af 100644
--- a/apps/openstackvtap/app/pom.xml
+++ b/apps/openstackvtap/app/pom.xml
@@ -69,6 +69,12 @@
 
         <dependency>
             <groupId>org.onosproject</groupId>
+            <artifactId>onos-apps-openstacknode-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.onosproject</groupId>
             <artifactId>onlab-osgi</artifactId>
             <version>${project.version}</version>
         </dependency>
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 4b35474..5d116da 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
@@ -17,7 +17,6 @@
 
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
-import org.onlab.packet.IPv4;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.TpPort;
 import org.onosproject.cli.AbstractShellCommand;
@@ -25,6 +24,9 @@
 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;
+
 /**
  * Command line interface for adding openstack vTap rule.
  */
@@ -32,15 +34,16 @@
         description = "OpenstackVtap activate")
 public class OpenstackVtapAddCommand extends AbstractShellCommand {
 
-    private final OpenstackVtapAdminService vTapService = get(OpenstackVtapAdminService.class);
+    private final OpenstackVtapAdminService vTapService =
+                                            get(OpenstackVtapAdminService.class);
 
     @Argument(index = 0, name = "srcIp",
-            description = "source IP address CIDR (e.g., \"10.1.0.0/16\")",
+            description = "source IP address CIDR (e.g., \"10.1.0.2/32\")",
             required = true, multiValued = false)
     String srcIp = "";
 
     @Argument(index = 1, name = "dstIp",
-            description = "destination IP address CIDR (e.g., \"10.1.0.0/16\")",
+            description = "destination IP address CIDR (e.g., \"10.1.0.3/32\")",
             required = true, multiValued = false)
     String dstIp = "";
 
@@ -69,7 +72,7 @@
         DefaultOpenstackVtapCriterion.Builder
                     defaultVtapCriterionBuilder = DefaultOpenstackVtapCriterion.builder();
         if (makeCriterion(defaultVtapCriterionBuilder)) {
-            OpenstackVtap.Type type = getVtapType(vTapTypeStr);
+            OpenstackVtap.Type type = getVtapTypeFromString(vTapTypeStr);
             if (type == null) {
                 print("Invalid vTap type");
                 return;
@@ -84,19 +87,6 @@
         }
     }
 
-    private static OpenstackVtap.Type getVtapType(String vTapTypeStr) {
-        switch (vTapTypeStr.toLowerCase()) {
-            case "all":
-                return OpenstackVtap.Type.VTAP_ALL;
-            case "tx":
-                return OpenstackVtap.Type.VTAP_TX;
-            case "rx":
-                return OpenstackVtap.Type.VTAP_RX;
-            default:
-                return OpenstackVtap.Type.VTAP_NONE;
-       }
-    }
-
     private boolean makeCriterion(DefaultOpenstackVtapCriterion.Builder vTapCriterionBuilder) {
         try {
             vTapCriterionBuilder.srcIpPrefix(IpPrefix.valueOf(srcIp));
@@ -106,25 +96,11 @@
             return false;
         }
 
-        switch (ipProto.toLowerCase()) {
-            case "tcp":
-                vTapCriterionBuilder.ipProtocol(IPv4.PROTOCOL_TCP);
-                break;
-            case "udp":
-                vTapCriterionBuilder.ipProtocol(IPv4.PROTOCOL_UDP);
-                break;
-            case "icmp":
-                vTapCriterionBuilder.ipProtocol(IPv4.PROTOCOL_ICMP);
-                break;
-            default:
-                log.warn("Invalid protocol type {}", ipProto);
-                return false;
-        }
+        vTapCriterionBuilder.ipProtocol(getProtocolTypeFromString(ipProto.toLowerCase()));
 
         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/OpenstackVtapListCommand.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/OpenstackVtapListCommand.java
index 9da2d63..afb9bbf 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
@@ -14,14 +14,17 @@
  * 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.onosproject.cli.AbstractShellCommand;
-import org.apache.karaf.shell.commands.Argument;
 import org.onosproject.openstackvtap.api.OpenstackVtap;
 import org.onosproject.openstackvtap.api.OpenstackVtapService;
 
 import java.util.Set;
 
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString;
+
 /**
  * Command line interface for listing openstack vTap rules.
  */
@@ -42,25 +45,7 @@
 
     @Override
     protected void execute() {
-        OpenstackVtap.Type type;
-        switch (vTapType) {
-            case "none":
-                type = OpenstackVtap.Type.VTAP_NONE;
-                break;
-            case "all":
-                type = OpenstackVtap.Type.VTAP_ALL;
-                break;
-            case "tx":
-                type = OpenstackVtap.Type.VTAP_TX;
-                break;
-            case "rx":
-                type = OpenstackVtap.Type.VTAP_RX;
-                break;
-            default:
-                print("Invalid vTap type");
-                return;
-        }
-
+        OpenstackVtap.Type type = getVtapTypeFromString(vTapType);
         Set<OpenstackVtap> openstackVtaps = vTapService.getVtaps(type);
         for (OpenstackVtap vTap : openstackVtaps) {
             print(FORMAT,
@@ -72,5 +57,4 @@
             print(FORMAT_RX_DEVICES, vTap.rxDeviceIds());
         }
     }
-
 }
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
index 293b62e..257cb9a 100644
--- 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
@@ -27,6 +27,7 @@
 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.
@@ -36,7 +37,8 @@
 public class OpenstackVtapOutputCommand extends AbstractShellCommand {
 
     private final DeviceService deviceService = get(DeviceService.class);
-    private final OpenstackVtapAdminService vTapAdminService = get(OpenstackVtapAdminService.class);
+    private final OpenstackVtapAdminService vTapAdminService =
+                                            get(OpenstackVtapAdminService.class);
 
     @Argument(index = 0, name = "deviceId", description = "device id",
             required = true, multiValued = false)
@@ -52,18 +54,14 @@
 
     @Argument(index = 3, name = "type", description = "vTap type [all|tx|rx]",
             required = false, multiValued = false)
-    String vTapType = "all";
+    String vTapTypeStr = "all";
 
     @Override
     protected void execute() {
         try {
             Device device = deviceService.getDevice(DeviceId.deviceId(id));
             if (device != null) {
-                OpenstackVtap.Type type = getVtapType(vTapType);
-                if (type == null) {
-                    print("Invalid vTap type");
-                    return;
-                }
+                OpenstackVtap.Type type = getVtapTypeFromString(vTapTypeStr);
 
                 vTapAdminService.setVtapOutput(device.id(), type,
                         PortNumber.portNumber(port), VlanId.vlanId((short) vlan));
@@ -78,17 +76,4 @@
             print("Invalid parameter: %s", e.toString());
         }
     }
-
-    private static OpenstackVtap.Type getVtapType(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;
-            default:
-                return OpenstackVtap.Type.VTAP_NONE;
-        }
-    }
 }
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
new file mode 100644
index 0000000..3d0f747
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/ProtocolTypeCompleter.java
@@ -0,0 +1,37 @@
+/*
+ * 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.Lists;
+import org.onosproject.cli.AbstractChoicesCompleter;
+
+import java.util.List;
+
+/**
+ * IP protocol type completer.
+ */
+public class ProtocolTypeCompleter extends AbstractChoicesCompleter {
+
+    @Override
+    protected List<String> choices() {
+        List<String> strings = Lists.newArrayList();
+        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/VmIpCompleter.java b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VmIpCompleter.java
new file mode 100644
index 0000000..3ee28fe
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VmIpCompleter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostService;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+
+/**
+ * VM IP completer.
+ */
+public class VmIpCompleter implements Completer {
+
+    private static final String CIDR = "/32";
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+
+        HostService service = AbstractShellCommand.get(HostService.class);
+        Iterator<Host> it = service.getHosts().iterator();
+        SortedSet<String> strings = delegate.getStrings();
+        while (it.hasNext()) {
+            for (IpAddress ip : it.next().ipAddresses()) {
+                strings.add(ip.toString() + CIDR);
+            }
+        }
+
+        return delegate.complete(buffer, cursor, candidates);
+    }
+}
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
new file mode 100644
index 0000000..c53098d
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapIdCompleter.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.cli;
+
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.completer.StringsCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstackvtap.api.OpenstackVtap;
+import org.onosproject.openstackvtap.api.OpenstackVtapService;
+
+import java.util.List;
+import java.util.SortedSet;
+
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getVtapTypeFromString;
+
+/**
+ * vTap ID completer.
+ */
+public class VtapIdCompleter implements Completer {
+
+    private static final String VTAP_TYPE = "none";
+
+    @Override
+    public int complete(String buffer, int cursor, List<String> candidates) {
+
+        OpenstackVtap.Type type = getVtapTypeFromString(VTAP_TYPE);
+
+        // Delegate string completer
+        StringsCompleter delegate = new StringsCompleter();
+        SortedSet<String> strings = delegate.getStrings();
+
+        OpenstackVtapService service = AbstractShellCommand.get(OpenstackVtapService.class);
+
+        service.getVtaps(type).forEach(t -> {
+            strings.add(t.id().toString());
+        });
+
+        return delegate.complete(buffer, cursor, 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
new file mode 100644
index 0000000..2784833
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/cli/VtapTypeCompleter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.Lists;
+import org.onosproject.cli.AbstractChoicesCompleter;
+
+import java.util.List;
+
+/**
+ * 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");
+        return strings;
+    }
+}
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 d6f63eb..c90571f 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
@@ -75,7 +75,7 @@
 
     private ConsistentMap<OpenstackVtapId, DefaultOpenstackVtap> vTapConsistentMap;
     private MapEventListener<OpenstackVtapId, DefaultOpenstackVtap>
-                                            vTapListener = new VTapEventListener();
+                                            vTapListener = new VtapEventListener();
     private Map<OpenstackVtapId, DefaultOpenstackVtap> vTapMap;
 
     private static final Serializer SERIALIZER = Serializer
@@ -115,7 +115,7 @@
 
         vTapStatusListener = status -> {
             if (status == Status.ACTIVE) {
-                eventExecutor.execute(this::loadVTapIds);
+                eventExecutor.execute(this::loadVtapIds);
             }
         };
         vTapConsistentMap.addStatusChangeListener(vTapStatusListener);
@@ -132,56 +132,6 @@
         log.info("Stopped {} - {}", this.getClass().getSimpleName());
     }
 
-    private void loadVTapIds() {
-        vTapIdsByTxDeviceId.clear();
-        vTapIdsByRxDeviceId.clear();
-        vTapMap.values().forEach(vTap -> refreshDeviceIdsByVtap(null, vTap));
-    }
-
-    private boolean shouldUpdate(DefaultOpenstackVtap existing,
-                                 OpenstackVtap description,
-                                 boolean replaceDevices) {
-        if (existing == null) {
-            return true;
-        }
-
-        if ((description.type() != null && !description.type().equals(existing.type()))
-                || (description.vTapCriterion() != null &&
-                !description.vTapCriterion().equals(existing.vTapCriterion()))) {
-            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
-        return description.annotations().keys().stream()
-                .anyMatch(k -> !Objects.equals(description.annotations().value(k),
-                        existing.annotations().value(k)));
-    }
-
     @Override
     public OpenstackVtap createOrUpdateVtap(OpenstackVtapId vTapId,
                                             OpenstackVtap description,
@@ -446,7 +396,57 @@
                         .collect(Collectors.toSet()));
     }
 
-    private class VTapComparator implements Comparator<OpenstackVtap> {
+    private void loadVtapIds() {
+        vTapIdsByTxDeviceId.clear();
+        vTapIdsByRxDeviceId.clear();
+        vTapMap.values().forEach(vTap -> refreshDeviceIdsByVtap(null, vTap));
+    }
+
+    private boolean shouldUpdate(DefaultOpenstackVtap existing,
+                                 OpenstackVtap description,
+                                 boolean replaceDevices) {
+        if (existing == null) {
+            return true;
+        }
+
+        if ((description.type() != null && !description.type().equals(existing.type()))
+                || (description.vTapCriterion() != null &&
+                !description.vTapCriterion().equals(existing.vTapCriterion()))) {
+            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
+        return description.annotations().keys().stream()
+                .anyMatch(k -> !Objects.equals(description.annotations().value(k),
+                        existing.annotations().value(k)));
+    }
+
+    private class VtapComparator implements Comparator<OpenstackVtap> {
         @Override
         public int compare(OpenstackVtap v1, OpenstackVtap v2) {
             int diff = (v2.type().compareTo(v1.type()));
@@ -531,7 +531,7 @@
         }
     }
 
-    private class VTapEventListener
+    private class VtapEventListener
             implements MapEventListener<OpenstackVtapId, DefaultOpenstackVtap> {
         @Override
         public void event(MapEvent<OpenstackVtapId, DefaultOpenstackVtap> event) {
@@ -540,7 +540,7 @@
             DefaultOpenstackVtap oldValue =
                     event.oldValue() != null ? event.oldValue().value() : null;
 
-            log.debug("VTapEventListener {} -> {}, {}", event.type(), oldValue, newValue);
+            log.debug("VtapEventListener {} -> {}, {}", event.type(), oldValue, newValue);
             switch (event.type()) {
                 case INSERT:
                     refreshDeviceIdsByVtap(oldValue, newValue);
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 1f12dd1..843a602 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
@@ -18,12 +18,14 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.VlanId;
 import org.onosproject.cluster.ClusterService;
@@ -34,6 +36,7 @@
 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;
@@ -57,31 +60,30 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
 import org.onosproject.net.group.DefaultGroupBucket;
 import org.onosproject.net.group.DefaultGroupDescription;
-import org.onosproject.net.group.DefaultGroupKey;
 import org.onosproject.net.group.GroupBucket;
 import org.onosproject.net.group.GroupBuckets;
 import org.onosproject.net.group.GroupDescription;
-import org.onosproject.net.group.GroupKey;
 import org.onosproject.net.group.GroupService;
 import org.onosproject.net.host.HostEvent;
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.onosproject.openstackvtap.api.OpenstackVtap;
+import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
 import org.onosproject.openstackvtap.api.OpenstackVtapAdminService;
 import org.onosproject.openstackvtap.api.OpenstackVtapCriterion;
 import org.onosproject.openstackvtap.api.OpenstackVtapEvent;
+import org.onosproject.openstackvtap.api.OpenstackVtapId;
 import org.onosproject.openstackvtap.api.OpenstackVtapListener;
 import org.onosproject.openstackvtap.api.OpenstackVtapService;
 import org.onosproject.openstackvtap.api.OpenstackVtapStore;
 import org.onosproject.openstackvtap.api.OpenstackVtapStoreDelegate;
-import org.onosproject.openstackvtap.api.OpenstackVtap.Type;
-import org.onosproject.openstackvtap.api.OpenstackVtapId;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
-import com.google.common.collect.Sets;
 
 import java.util.List;
 import java.util.Objects;
@@ -97,7 +99,9 @@
 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.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE;
 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;
@@ -110,6 +114,8 @@
 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.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstackvtap.util.OpenstackVtapUtil.getGroupKey;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -153,6 +159,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackVtapStore store;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
     public static final String APP_ID = "org.onosproject.openstackvtap";
 
     public static final String VTAP_ID_NULL = "OpenstackVtap ID cannot be null";
@@ -168,10 +177,9 @@
     private static final int FLAT_OUTBOUND_NEXT_TABLE = FLAT_TABLE;
     private static final int OUTBOUND_NEXT_TABLE = FORWARDING_TABLE;
 
-    private static final String VTAP_GROUP_KEY = "VTAP_GROUP_KEY";
-
     private final DeviceListener deviceListener = new InternalDeviceListener();
     private final HostListener hostListener = new InternalHostListener();
+    private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
 
     private OpenstackVtapStoreDelegate delegate = new InternalStoreDelegate();
 
@@ -194,12 +202,17 @@
 
         deviceService.addListener(deviceListener);
         hostService.addListener(hostListener);
+        osNodeService.addListener(osNodeListener);
+
+        // TODO: need to sweep through device store and add flow rules and
+        // group tables to mirror VM traffic
 
         log.info("Started {} - {}", appId.name(), this.getClass().getSimpleName());
     }
 
     @Deactivate
     public void deactivate() {
+        osNodeService.removeListener(osNodeListener);
         hostService.removeListener(hostListener);
         deviceService.removeListener(deviceListener);
 
@@ -209,6 +222,8 @@
         eventExecutor.shutdown();
         leadershipService.withdraw(appId.name());
 
+        // TODO: need to purge vtap related flow rules and group tables
+
         log.info("Stopped {} - {}", appId.name(), this.getClass().getSimpleName());
     }
 
@@ -229,30 +244,15 @@
     }
 
     @Override
-    public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type, DeviceId deviceId) {
+    public Set<OpenstackVtap> getVtapsByDeviceId(OpenstackVtap.Type type,
+                                                 DeviceId deviceId) {
         checkNotNull(deviceId, DEVICE_ID_NULL);
         return store.getVtapsByDeviceId(type, deviceId);
     }
 
-    private Set<DeviceId> getEdgeDevice(Type type, OpenstackVtapCriterion vTapCriterionOpenstack) {
-        Set<DeviceId> deviceIds = Sets.newConcurrentHashSet();
-        StreamSupport.stream(hostService.getHosts().spliterator(), true)
-                .forEach(host -> {
-                    if (host.ipAddresses().stream().anyMatch(ipAddress ->
-                            (type.isValid(Type.VTAP_TX) &&
-                                    vTapCriterionOpenstack.srcIpPrefix().contains(ipAddress)) ||
-                                    (type.isValid(Type.VTAP_RX) &&
-                                            vTapCriterionOpenstack.dstIpPrefix().contains(ipAddress)))) {
-                        deviceIds.addAll(host.locations().stream()
-                                .map(HostLocation::deviceId)
-                                .collect(Collectors.toSet()));
-                    }
-                });
-        return deviceIds;
-    }
-
     @Override
-    public OpenstackVtap createVtap(Type type, OpenstackVtapCriterion vTapCriterionOpenstack) {
+    public OpenstackVtap createVtap(Type type,
+                                    OpenstackVtapCriterion vTapCriterionOpenstack) {
         checkNotNull(vTapCriterionOpenstack, VTAP_DESC_NULL);
 
         Set<DeviceId> txDevices = type.isValid(Type.VTAP_TX) ?
@@ -260,13 +260,14 @@
         Set<DeviceId> rxDevices = type.isValid(Type.VTAP_RX) ?
                 getEdgeDevice(type, vTapCriterionOpenstack) : ImmutableSet.of();
 
-        OpenstackVtap description = DefaultOpenstackVtap.builder()
-                                                        .id(OpenstackVtapId.vTapId())
-                                                        .type(type)
-                                                        .vTapCriterion(vTapCriterionOpenstack)
-                                                        .txDeviceIds(txDevices)
-                                                        .rxDeviceIds(rxDevices)
-                                                        .build();
+        OpenstackVtap description =
+                            DefaultOpenstackVtap.builder()
+                                                .id(OpenstackVtapId.vTapId())
+                                                .type(type)
+                                                .vTapCriterion(vTapCriterionOpenstack)
+                                                .txDeviceIds(txDevices)
+                                                .rxDeviceIds(rxDevices)
+                                                .build();
         return store.createOrUpdateVtap(description.id(), description, true);
     }
 
@@ -284,13 +285,14 @@
         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();
+        DefaultOpenstackVtap description =
+                            DefaultOpenstackVtap.builder()
+                                                .id(vTapId)
+                                                .type(vTap.type())
+                                                .vTapCriterion(vTap.vTapCriterion())
+                                                .txDeviceIds(txDevices)
+                                                .rxDeviceIds(rxDevices)
+                                                .build();
         return store.createOrUpdateVtap(vTapId, description, true);
     }
 
@@ -301,11 +303,14 @@
     }
 
     @Override
-    public void setVtapOutput(DeviceId deviceId, OpenstackVtap.Type type, PortNumber portNumber, VlanId vlanId) {
+    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);
@@ -317,58 +322,66 @@
         // TODO: need to provide implementation
     }
 
-    // Triggers driver setup when a host is (re)detected.
-    private class InternalHostListener implements HostListener {
-        @Override
-        public boolean isRelevant(HostEvent event) {
-            // do not allow to proceed without leadership
-            NodeId leader = leadershipService.getLeader(appId.name());
-            return Objects.equals(localNodeId, leader);
-        }
-
-        @Override
-        public void event(HostEvent event) {
-            HostEvent.Type type = event.type();
-            Host host = event.subject();
-            log.trace("InternalHostListener hostId={}, type={}", host.id(), type);
-
-            switch (type) {
-                case HOST_ADDED:
-                    eventExecutor.execute(() -> updateHost(host, null));
-                    break;
-
-                case HOST_REMOVED:
-                    eventExecutor.execute(() -> updateHost(null, host));
-                    break;
-
-                case HOST_UPDATED:
-                case HOST_MOVED:
-                    eventExecutor.execute(() -> updateHost(host, event.prevSubject()));
-                    break;
-                default:
-                    break;
-            }
-        }
+    /**
+     * Obtains the identifier set of edge device where the targeted host is located.
+     * 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
+     * @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()));
+                }
+            });
+        return deviceIds;
     }
 
-    private boolean hostDifference(Host host1, Host host2, IpPrefix ipPrefix) {
-        return ((host1 != null && host1.ipAddresses().stream().anyMatch(ipPrefix::contains)) &&
-                (host2 == null || host2.ipAddresses().stream().noneMatch(ipPrefix::contains)));
+    /**
+     * 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.
+     *
+     * @param newHost   new host instance
+     * @param oldHost   old host instance
+     */
     private void updateHost(Host newHost, Host oldHost) {
         // update devices for vTap tx
         getVtaps(Type.VTAP_TX).parallelStream().forEach(vTap -> {
-            if (hostDifference(oldHost, newHost, vTap.vTapCriterion().srcIpPrefix())) {
-                oldHost.locations().stream()
-                        .map(HostLocation::deviceId)
+
+            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()));
+                                store.removeDeviceFromVtap(vTap.id(), Type.VTAP_TX,
+                                        oldHost.location().deviceId()));
             }
-            if (hostDifference(newHost, oldHost, vTap.vTapCriterion().srcIpPrefix())) {
-                newHost.locations().stream()
-                        .map(HostLocation::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()));
@@ -377,16 +390,16 @@
 
         // update devices for vTap rx
         getVtaps(Type.VTAP_RX).parallelStream().forEach(vTap -> {
-            if (hostDifference(oldHost, newHost, vTap.vTapCriterion().dstIpPrefix())) {
-                oldHost.locations().stream()
-                        .map(HostLocation::deviceId)
+
+            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 (hostDifference(newHost, oldHost, vTap.vTapCriterion().dstIpPrefix())) {
-                newHost.locations().stream()
-                        .map(HostLocation::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()));
@@ -394,35 +407,29 @@
         });
     }
 
-    // Triggers driver setup when a device is (re)detected.
-    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);
-        }
-
-        @Override
-        public void event(DeviceEvent event) {
-            DeviceEvent.Type type = event.type();
-            DeviceId deviceId = event.subject().id();
-            log.trace("InternalDeviceListener deviceId={}, type={}", deviceId, type);
-
-            switch (type) {
-                case DEVICE_ADDED:
-                    eventExecutor.execute(() -> updateDevice(deviceId));
-                    break;
-                default:
-                    break;
-            }
-        }
+    /**
+     * 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 updateDevice(DeviceId deviceId) {
+    /**
+     * 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
-        createVtapPipeline(deviceId);
+        initVtapPipeline(deviceId);
 
         // Install tx filter
         getVtapsByDeviceId(Type.VTAP_TX, deviceId).forEach(vTap -> {
@@ -442,33 +449,72 @@
         });
     }
 
-    private void clearRules() {
-        flowRuleService.removeFlowRulesById(appId);
-        deviceService.getDevices().forEach(device -> {
-            groupService.getGroups(device.id(), appId).forEach(group -> {
-                groupService.removeGroup(device.id(), group.appCookie(), appId);
-            });
+    /**
+     * 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 using the given device identifier.
+     *
+     * @param deviceId  device identifier
+     */
+    private void clearRulesGroupTable(DeviceId deviceId) {
+        Set<FlowRule> purgedRules = Sets.newConcurrentHashSet();
+        for (FlowRule flowRule : flowRuleService.getFlowRulesById(appId)) {
+            if (flowRule.deviceId().equals(deviceId)) {
+                purgedRules.add(flowRule);
+            }
+        }
+
+        flowRuleService.removeFlowRules((FlowRule[]) purgedRules.toArray());
+
+        groupService.getGroups(deviceId, appId).forEach(group -> {
+            groupService.removeGroup(deviceId, group.appCookie(), appId);
         });
-        log.info("OpenstackVtap rules are cleared.");
+        log.info("OpenstackVtap flow rules and groups are purged");
     }
 
     private void installFilterRule(Set<DeviceId> txDeviceIds, Set<DeviceId> rxDeviceIds,
-                                   OpenstackVtapCriterion vTapCriterionOpenstack, boolean install) {
+                                   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));
+                    (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));
+                    (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,
-                                vTapCriterionOpenstack, PRIORITY_VTAP_RULE, install);
+                        connectTables(deviceId, inTable, NONE_TABLE, outGroup,
+                                vTapCriterion, PRIORITY_VTAP_RULE, install);
                     });
             return null;
         };
@@ -478,72 +524,6 @@
         installFlow.apply(rxDeviceIds, outbound);
     }
 
-    // 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);
-
-            switch (type) {
-                case VTAP_ADDED:
-                    eventExecutor.execute(() -> {
-                        // Add new devices
-                        installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
-                                vTap.vTapCriterion(), true);
-                    });
-                    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);
-
-                        // 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;
-            }
-            post(event);
-        }
-    }
-
-    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.trace("Provisioned vni or forwarding table");
-                //log.trace("{}", ops.toString());
-            }
-
-            @Override
-            public void onError(FlowRuleOperations ops) {
-                log.error("Failed to privision vni or forwarding table");
-                //log.error("{}", ops.toString());
-            }
-        }));
-    }
-
     private void connectTables(DeviceId deviceId, int fromTable, int toTable, int toGroup,
                                OpenstackVtapCriterion vTapCriterionOpenstack, int rulePriority,
                                boolean install) {
@@ -557,6 +537,8 @@
         switch (vTapCriterionOpenstack.ipProtocol()) {
             case PROTOCOL_TCP:
                 selectorBuilder.matchIPProtocol(vTapCriterionOpenstack.ipProtocol());
+
+                // Add port match only if the port number is greater than zero
                 if (vTapCriterionOpenstack.srcTpPort().toInt() > 0) {
                     selectorBuilder.matchTcpSrc(vTapCriterionOpenstack.srcTpPort());
                 }
@@ -566,6 +548,8 @@
                 break;
             case PROTOCOL_UDP:
                 selectorBuilder.matchIPProtocol(vTapCriterionOpenstack.ipProtocol());
+
+                // Add port match only if the port number is greater than zero
                 if (vTapCriterionOpenstack.srcTpPort().toInt() > 0) {
                     selectorBuilder.matchUdpSrc(vTapCriterionOpenstack.srcTpPort());
                 }
@@ -610,7 +594,7 @@
 
         // Set output port & vlan
         int priority = PRIORITY_VTAP_DROP;
-        if (vlanId != null) {
+        if (vlanId != null && vlanId.toShort() != UNTAGGED) {
             treatment.pushVlan().setVlanId(vlanId);
         }
         if (outPort != null) {
@@ -630,17 +614,15 @@
         applyFlowRule(flowRule, true);
     }
 
-    private GroupKey getGroupKey(int groupId) {
-        return new DefaultGroupKey((VTAP_GROUP_KEY + Integer.toString(groupId)).getBytes());
-    }
-
-    private ExtensionTreatment buildNiciraExtenstion(DeviceId id, int tableId) {
+    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);
+        DriverHandler driverHandler =
+                    new DefaultDriverHandler(new DefaultDriverData(driver, id));
+        ExtensionTreatmentResolver resolver =
+                    driverHandler.behaviour(ExtensionTreatmentResolver.class);
 
-        ExtensionTreatment extensionInstruction = resolver.getExtensionInstruction(
-                ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_RESUBMIT_TABLE.type());
+        ExtensionTreatment extensionInstruction =
+                    resolver.getExtensionInstruction(NICIRA_RESUBMIT_TABLE.type());
 
         try {
             extensionInstruction.setPropertyValue("table", ((short) tableId));
@@ -656,7 +638,7 @@
         List<GroupBucket> buckets = Lists.newArrayList();
         tableIds.forEach(tableId -> {
             TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder()
-                    .extension(buildNiciraExtenstion(deviceId, tableId), deviceId);
+                    .extension(buildNiciraExtension(deviceId, tableId), deviceId);
             GroupBucket bucket = DefaultGroupBucket
                     .createAllGroupBucket(treatment.build());
             buckets.add(bucket);
@@ -678,26 +660,156 @@
         groupService.addGroup(groupDescription);
     }
 
-    private void createVtapPipeline(DeviceId deviceId) {
-        // Clear all flow rules & group tables
-        clearRules();
+    private void applyFlowRule(FlowRule flowRule, boolean install) {
+        FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
 
-        // 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);
+        flowOpsBuilder = install ? flowOpsBuilder.add(flowRule) : flowOpsBuilder.remove(flowRule);
 
-        // Make tx group table
-        createGroupTable(deviceId, VTAP_INBOUND_GROUP_TABLE,
-                ImmutableList.of(INBOUND_NEXT_TABLE, VTAP_INBOUND_MIRROR_TABLE),
-                ImmutableList.of());
+        flowRuleService.apply(flowOpsBuilder.build(new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                log.trace("Installed flow rules for tapping");
+            }
 
-        // 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());
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.error("Failed to install flow rules for tapping");
+            }
+        }));
+    }
+
+    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);
+
+            switch (type) {
+                case DEVICE_ADDED:
+                    eventExecutor.execute(() -> initFlowAndGroupByDeviceId(deviceId));
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    private class InternalHostListener implements HostListener {
+        @Override
+        public boolean isRelevant(HostEvent event) {
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNodeId, leader);
+        }
+
+        @Override
+        public void event(HostEvent event) {
+            HostEvent.Type type = event.type();
+            Host host = event.subject();
+            log.trace("InternalHostListener hostId={}, type={}", host.id(), type);
+
+            switch (type) {
+                case HOST_ADDED:
+                    eventExecutor.execute(() -> updateHost(host, null));
+                    break;
+
+                case HOST_REMOVED:
+                    eventExecutor.execute(() -> updateHost(null, host));
+                    break;
+
+                case HOST_UPDATED:
+                case HOST_MOVED:
+                    eventExecutor.execute(() -> updateHost(host, event.prevSubject()));
+                    break;
+                default:
+                    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(() -> clearRulesGroupTable(deviceId));
+                default:
+                    break;
+            }
+        }
+    }
+
+    // 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);
+
+            switch (type) {
+                case VTAP_ADDED:
+                    eventExecutor.execute(() -> {
+                        // Add new devices
+                        installFilterRule(vTap.txDeviceIds(), vTap.rxDeviceIds(),
+                                vTap.vTapCriterion(), true);
+                    });
+                    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);
+
+                        // 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;
+            }
+            post(event);
+        }
     }
 }
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
new file mode 100644
index 0000000..1e67693
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/OpenstackVtapUtil.java
@@ -0,0 +1,79 @@
+/*
+ * 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.util;
+
+import org.onlab.packet.IPv4;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.openstackvtap.api.OpenstackVtap;
+
+/**
+ * An utility that used in openstack vTap app.
+ */
+public final class OpenstackVtapUtil {
+
+    private static final String VTAP_GROUP_KEY = "VTAP_GROUP_KEY";
+
+    /**
+     * Prevents object instantiation from external.
+     */
+    private OpenstackVtapUtil() {
+    }
+
+    /**
+     * 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
+     */
+    public static byte getProtocolTypeFromString(String str) {
+        switch (str) {
+            case "tcp":
+                return IPv4.PROTOCOL_TCP;
+            case "udp":
+                return IPv4.PROTOCOL_UDP;
+            case "icmp":
+                return IPv4.PROTOCOL_ICMP;
+            default:
+                return 0;
+        }
+    }
+
+    public static GroupKey getGroupKey(int groupId) {
+        return new DefaultGroupKey((VTAP_GROUP_KEY + Integer.toString(groupId)).getBytes());
+    }
+}
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
new file mode 100644
index 0000000..905d1c4
--- /dev/null
+++ b/apps/openstackvtap/app/src/main/java/org/onosproject/openstackvtap/util/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.
+ */
+
+/**
+ * Openstack vTap utility package.
+ */
+package org.onosproject.openstackvtap.util;
\ No newline at end of file
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 49c2e50..c3fa886 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
@@ -17,18 +17,38 @@
     <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
         <command>
             <action class="org.onosproject.openstackvtap.cli.OpenstackVtapAddCommand" />
+            <completers>
+                <ref component-id="vmIpCompleter"/>
+                <ref component-id="vmIpCompleter"/>
+                <ref component-id="protocolTypeCompleter"/>
+                <null/>
+                <null/>
+                <ref component-id="vtapTypeCompleter"/>
+            </completers>
         </command>
 
         <command>
             <action class="org.onosproject.openstackvtap.cli.OpenstackVtapListCommand" />
+            <completers>
+                <ref component-id="vtapTypeCompleter"/>
+            </completers>
         </command>
 
         <command>
             <action class="org.onosproject.openstackvtap.cli.OpenstackVtapDeleteCommand" />
+            <completers>
+                <ref component-id="vtapIdCompleter"/>
+            </completers>
         </command>
 
         <command>
             <action class="org.onosproject.openstackvtap.cli.OpenstackVtapOutputCommand" />
         </command>
     </command-bundle>
+
+    <bean id="vtapTypeCompleter" class="org.onosproject.openstackvtap.cli.VtapTypeCompleter"/>
+    <bean id="protocolTypeCompleter" class="org.onosproject.openstackvtap.cli.ProtocolTypeCompleter"/>
+    <bean id="vmIpCompleter" class="org.onosproject.openstackvtap.cli.VmIpCompleter"/>
+    <bean id="vtapIdCompleter" class="org.onosproject.openstackvtap.cli.VtapIdCompleter"/>
+
 </blueprint>