ONOS-5182 Refactored SONA to cache network states

Change-Id: Ib316fa5fa5d36e9da370a1578ac55de4a8dd9b04
diff --git a/apps/openstacknetworking/BUCK b/apps/openstacknetworking/BUCK
index e44142f..519f134 100644
--- a/apps/openstacknetworking/BUCK
+++ b/apps/openstacknetworking/BUCK
@@ -6,10 +6,8 @@
   '//utils/rest:onlab-rest',
   '//cli:onos-cli',
   '//apps/openstacknode:onos-apps-openstacknode',
-  '//apps/openstackinterface/api:onos-apps-openstackinterface-api',
-  '//apps/openstackinterface/app:onos-apps-openstackinterface-app',
   '//apps/scalablegateway:onos-apps-scalablegateway',
-  '//apps/dhcp/api:onos-apps-dhcp-api',
+  '//lib:openstack4j-core',
 ]
 
 osgi_jar_with_tests (
@@ -28,9 +26,7 @@
   url = 'http://onosproject.org',
   description = 'OpenStack Networking application.',
   required_apps = [
-    'org.onosproject.dhcp',
     'org.onosproject.openstacknode',
-    'org.onosproject.openstackinterface',
     'org.onosproject.scalablegateway'
   ]
 )
diff --git a/apps/openstacknetworking/pom.xml b/apps/openstacknetworking/pom.xml
index e9569cc..afbe2ea 100644
--- a/apps/openstacknetworking/pom.xml
+++ b/apps/openstacknetworking/pom.xml
@@ -35,9 +35,7 @@
         <onos.app.url>http://onosproject.org</onos.app.url>
         <onos.app.readme>OpenStack networking application</onos.app.readme>
         <onos.app.requires>
-            org.onosproject.dhcp,
             org.onosproject.openstacknode,
-            org.onosproject.openstacinterface,
             org.onosproject.scalablegateway
         </onos.app.requires>
         <web.context>/onos/openstacknetworking</web.context>
@@ -66,6 +64,14 @@
             <version>${project.version}</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.onosproject</groupId>
             <artifactId>onos-rest</artifactId>
             <version>${project.version}</version>
@@ -98,26 +104,6 @@
         </dependency>
         <dependency>
             <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-dhcp-api</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-dhcp</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-openstackinterface-api</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-app-openstackinterface-app</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
             <artifactId>onos-app-openstacknode</artifactId>
             <version>${project.version}</version>
         </dependency>
@@ -164,6 +150,21 @@
             <groupId>org.onosproject</groupId>
             <artifactId>onlab-misc</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.pacesys</groupId>
+            <artifactId>openstack4j-core</artifactId>
+            <version>2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>org.pacesys.openstack4j.connectors</groupId>
+            <artifactId>openstack4j-http-connector</artifactId>
+            <version>2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>org.pacesys.openstack4j.connectors</groupId>
+            <artifactId>openstack4j-httpclient</artifactId>
+            <version>2.11</version>
+        </dependency>
     </dependencies>
 
     <build>
@@ -206,6 +207,17 @@
                             ${project.groupId}.${project.artifactId}
                         </Bundle-SymbolicName>
                         <Web-ContextPath>${web.context}</Web-ContextPath>
+                        <Import-Package>
+                            !org.apache.http.*,
+                            !com.fasterxml.jackson.dataformat.*,
+                            !javax.annotation,
+                            *,org.glassfish.jersey.servlet
+                        </Import-Package>
+                        <Embed-Dependency>
+                            openstack4j-core,
+                            openstack4j-http-connector,
+                            openstack4j-httpclient
+                        </Embed-Dependency>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
index 4ba121a..00e91b0 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/Constants.java
@@ -15,9 +15,6 @@
  */
 package org.onosproject.openstacknetworking.api;
 
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 
 /**
@@ -28,42 +25,18 @@
     private Constants() {
     }
 
-    public static final String SWITCHING_APP_ID = "org.onosproject.openstackswitching";
-    public static final String ROUTING_APP_ID = "org.onosproject.openstackrouting";
-
-    public static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
-    public static final String DEVICE_OWNER_ROUTER_GATEWAY = "network:router_gateway";
-    public static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
-
-    public static final String PORT_NAME_PREFIX_VM = "tap";
-    public static final String PORT_NAME_PREFIX_TUNNEL = "vxlan";
+    public static final String OPENSTACK_NETWORKING_APP_ID = "org.onosproject.openstacknetworking";
 
     public static final String DEFAULT_GATEWAY_MAC_STR = "fe:00:00:00:00:02";
     public static final MacAddress DEFAULT_GATEWAY_MAC = MacAddress.valueOf(DEFAULT_GATEWAY_MAC_STR);
-    // TODO make this configurable
     public static final MacAddress DEFAULT_EXTERNAL_ROUTER_MAC = MacAddress.valueOf("fe:00:00:00:00:01");
 
-    public static final Ip4Address DNS_SERVER_IP = Ip4Address.valueOf("8.8.8.8");
-    public static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
-    public static final int DHCP_INFINITE_LEASE = -1;
-
-    public static final String NETWORK_ID = "networkId";
-    public static final String SUBNET_ID = "subnetId";
-    public static final String PORT_ID = "portId";
-    public static final String VXLAN_ID = "vxlanId";
-    public static final String TENANT_ID = "tenantId";
-    public static final String GATEWAY_IP = "gatewayIp";
-    public static final String CREATE_TIME = "createTime";
-
-    public static final int SWITCHING_RULE_PRIORITY = 30000;
-    public static final int TUNNELTAG_RULE_PRIORITY = 30000;
-    public static final int ACL_RULE_PRIORITY = 30000;
-    public static final int EW_ROUTING_RULE_PRIORITY = 28000;
-
-    public static final int GATEWAY_ICMP_PRIORITY = 43000;
-    public static final int ROUTING_RULE_PRIORITY = 25000;
-    public static final int FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY = 42000;
-    public static final int FLOATING_RULE_PRIORITY = 41000;
-    public static final int PNAT_RULE_PRIORITY = 26000;
-    public static final int PNAT_TIMEOUT = 120;
+    public static final int PRIORITY_TUNNEL_TAG_RULE = 30000;
+    public static final int PRIORITY_FLOATING_INTERNAL = 42000;
+    public static final int PRIORITY_FLOATING_EXTERNAL = 41000;
+    public static final int PRIORITY_ICMP_RULE = 43000;
+    public static final int PRIORITY_INTERNAL_ROUTING_RULE = 28000;
+    public static final int PRIORITY_EXTERNAL_ROUTING_RULE = 25000;
+    public static final int PRIORITY_SNAT_RULE = 26000;
+    public static final int PRIORITY_SWITCHING_RULE = 30000;
 }
\ No newline at end of file
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java
new file mode 100644
index 0000000..97a112b
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePort.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Representation of virtual instance port.
+ */
+public interface InstancePort {
+
+    /**
+     * Returns the OpenStack network ID of the instance port.
+     *
+     * @return openstack network id
+     */
+    String networkId();
+
+    /**
+     * Returns the OpenStack port ID of a given host.
+     *
+     * @return openstack port id
+     */
+    String portId();
+
+    /**
+     * Returns the MAC address of the instance port.
+     *
+     * @return mac address
+     */
+    MacAddress macAddress();
+
+    /**
+     * Returns the IP address of the instance port.
+     *
+     * @return ip address
+     */
+    IpAddress ipAddress();
+
+    /**
+     * Returns the device ID of the instance port.
+     *
+     * @return device id
+     */
+    DeviceId deviceId();
+
+    /**
+     * Returns the port number of the instance port.
+     *
+     * @return port number
+     */
+    PortNumber portNumber();
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java
new file mode 100644
index 0000000..945772e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes virtual instance port event.
+ */
+public class InstancePortEvent extends AbstractEvent<InstancePortEvent.Type, InstancePort> {
+
+    public enum Type {
+
+        /**
+         * Signifies that a new instance port is detected.
+         */
+        OPENSTACK_INSTANCE_PORT_DETECTED,
+
+        /**
+         * Signifies that the instance port is updated.
+         */
+        OPENSTACK_INSTANCE_PORT_UPDATED,
+
+        /**
+         * Signifies that the instance port is disabled.
+         */
+        OPENSTACK_INSTANCE_PORT_VANISHED
+    }
+
+    /**
+     * Creates an event of a given type for the specified instance port.
+     *
+     * @param type     instance port event type
+     * @param instPort instance port
+     */
+    public InstancePortEvent(Type type, InstancePort instPort) {
+        super(type, instPort);
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortListener.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortListener.java
new file mode 100644
index 0000000..708a9dd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for instance port event.
+ */
+public interface InstancePortListener extends EventListener<InstancePortEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
new file mode 100644
index 0000000..91c8183
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.event.ListenerService;
+
+import java.util.Set;
+
+/**
+ * Service for interacting with the inventory of instance ports.
+ */
+public interface InstancePortService
+        extends ListenerService<InstancePortEvent, InstancePortListener> {
+
+    /**
+     * Returns instance port with the given MAC address.
+     *
+     * @param macAddress mac address
+     * @return instance port; null if not found
+     */
+    InstancePort instancePort(MacAddress macAddress);
+
+    /**
+     * Returns instance port with the given IP address in the given OpenStack network.
+     *
+     * @param ipAddress ip address
+     * @param osNetId   openstack network id
+     * @return instance port; null if not found
+     */
+    InstancePort instancePort(IpAddress ipAddress, String osNetId);
+
+    /**
+     * Returns instance port with the given openstack port ID.
+     *
+     * @param osPortId openstack port id
+     * @return instance port; null if not found
+     */
+    InstancePort instancePort(String osPortId);
+
+    /**
+     * Returns all instance ports.
+     *
+     * @return set of instance ports; empty list if no port exists
+     */
+    Set<InstancePort> instancePorts();
+
+    /**
+     * Returns instance ports in the given OpenStack network.
+     *
+     * @param osNetId openstack network
+     * @return set of instance ports; empty list if no port exists
+     */
+    Set<InstancePort> instancePorts(String osNetId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java
deleted file mode 100644
index 9988a4e..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackFloatingIpService.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.api;
-
-import org.onosproject.net.Host;
-
-import org.onosproject.openstackinterface.OpenstackFloatingIP;
-
-/**
- * Handles floating IP update requests from OpenStack.
- */
-public interface OpenstackFloatingIpService {
-
-    enum Action {
-        ASSOCIATE,
-        DISSASSOCIATE
-    }
-
-    /**
-     * Handles floating IP create request from OpenStack.
-     *
-     * @param floatingIp floating IP information
-     */
-    void createFloatingIp(OpenstackFloatingIP floatingIp);
-
-    /**
-     * Handles floating IP update request from OpenStack.
-     *
-     * @param floatingIp floating IP information
-     */
-    void updateFloatingIp(OpenstackFloatingIP floatingIp);
-
-    /**
-     * Handles floating IP remove request from OpenStack.
-     *
-     * @param floatingIpId floating ip identifier
-     */
-    void deleteFloatingIp(String floatingIpId);
-
-    /**
-     * Handles to purge data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void purgeVmFlow(Host host);
-
-    /**
-     * Handles to reinstall data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java
new file mode 100644
index 0000000..5ebb559
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkAdminService.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+/**
+ * Service for administering the inventory of OpenStack network, subnet and port.
+ */
+public interface OpenstackNetworkAdminService {
+
+    /**
+     * Creates a network with the given information.
+     *
+     * @param network the new network
+     */
+    void createNetwork(Network network);
+
+    /**
+     * Updates the network with the given information.
+     *
+     * @param network the updated network
+     */
+    void updateNetwork(Network network);
+
+    /**
+     * Removes the network with the given network id.
+     *
+     * @param networkId network id
+     */
+    void removeNetwork(String networkId);
+
+    /**
+     * Creates a subnet with the given information.
+     *
+     * @param subnet the new subnet
+     */
+    void createSubnet(Subnet subnet);
+
+    /**
+     * Updates a subnet with the given information.
+     *
+     * @param subnet the updated subnet
+     */
+    void updateSubnet(Subnet subnet);
+
+    /**
+     * Removes the subnet with the given subnet id.
+     *
+     * @param subnetId subnet id
+     */
+    void removeSubnet(String subnetId);
+
+    /**
+     * Creates a port with the given information.
+     *
+     * @param port the new port
+     */
+    void createPort(Port port);
+
+    /**
+     * Updates the port with the given information.
+     *
+     * @param port the updated port
+     */
+    void updatePort(Port port);
+
+    /**
+     * Removes the port with the given port id.
+     *
+     * @param portId port id
+     */
+    void removePort(String portId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
new file mode 100644
index 0000000..c022c29
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkEvent.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.joda.time.LocalDateTime;
+import org.onosproject.event.AbstractEvent;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Describes OpenStack network service event.
+ */
+public class OpenstackNetworkEvent extends AbstractEvent<OpenstackNetworkEvent.Type, Network> {
+
+    private final Port port;
+    private final Subnet subnet;
+
+    public enum Type {
+        /**
+         * Signifies that a new OpenStack network is created.
+         */
+        OPENSTACK_NETWORK_CREATED,
+
+        /**
+         * Signifies that the OpenStack network is updated.
+         */
+        OPENSTACK_NETWORK_UPDATED,
+
+        /**
+         * Signifies that the OpenStack network is removed.
+         */
+        OPENSTACK_NETWORK_REMOVED,
+
+        /**
+         * Signifies that a new OpenStack subnet is created.
+         */
+        OPENSTACK_SUBNET_CREATED,
+
+        /**
+         * Signifies that the OpenStack subnet is updated.
+         */
+        OPENSTACK_SUBNET_UPDATED,
+
+        /**
+         * Signifies that the OpenStack subnet is removed.
+         */
+        OPENSTACK_SUBNET_REMOVED,
+
+        /**
+         * Signifies that a new OpenStack port is created.
+         */
+        OPENSTACK_PORT_CREATED,
+
+        /**
+         * Signifies that the OpenStack port is updated.
+         */
+        OPENSTACK_PORT_UPDATED,
+
+        /**
+         * Signifies that the OpenStack port is removed.
+         */
+        OPENSTACK_PORT_REMOVED
+    }
+
+    /**
+     * Creates an event of a given type for the specified network and the current time.
+     * @param type    openstack network event type
+     * @param network openstack network
+     */
+    public OpenstackNetworkEvent(Type type, Network network) {
+        super(type, network);
+        this.port = null;
+        this.subnet = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified network, port and the
+     * current time.
+     *
+     * @param type    openstack network event type
+     * @param network openstack network
+     * @param port    openstack port
+     */
+    public OpenstackNetworkEvent(Type type, Network network, Port port) {
+        super(type, network);
+        this.port = port;
+        this.subnet = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified network, subnet and the
+     * current time.
+     *
+     * @param type    openstack network event type
+     * @param network openstack network
+     * @param subnet  openstack subnet
+     */
+    public OpenstackNetworkEvent(Type type, Network network, Subnet subnet) {
+        super(type, network);
+        this.port = null;
+        this.subnet = subnet;
+    }
+
+    /**
+     * Returns the port of the network event.
+     *
+     * @return openstack port; null if the event is not port specific
+     */
+    public Port port() {
+        return port;
+    }
+
+    /**
+     * Returns the subnet of the network event.
+     *
+     * @return openstack subnet; null if the event is not subnet specific
+     */
+    public Subnet subnet() {
+        return subnet;
+    }
+
+    @Override
+    public String toString() {
+        if (port == null && subnet == null) {
+            return super.toString();
+        }
+        return toStringHelper(this)
+                .add("time", new LocalDateTime(time()))
+                .add("type", type())
+                .add("network", subject())
+                .add("port", port)
+                .add("subnet", subnet)
+                .toString();
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkListener.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkListener.java
new file mode 100644
index 0000000..1e824da
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for OpenStack network event.
+ */
+public interface OpenstackNetworkListener extends EventListener<OpenstackNetworkEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
new file mode 100644
index 0000000..78ac42d
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkService.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.ListenerService;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+import java.util.Set;
+
+/**
+ * Service for interacting with the inventory of OpenStack network and port.
+ */
+public interface OpenstackNetworkService
+        extends ListenerService<OpenstackNetworkEvent, OpenstackNetworkListener> {
+    /**
+     * Returns the network with the supplied network ID.
+     *
+     * @param networkId network id
+     * @return openstack network
+     */
+    Network network(String networkId);
+
+    /**
+     * Returns all networks registered in the service.
+     *
+     * @return set of networks
+     */
+    Set<Network> networks();
+
+    /**
+     * Returns the subnet with the supplied subnet ID.
+     *
+     * @param subnetId subnet id
+     * @return subnet
+     */
+    Subnet subnet(String subnetId);
+
+    /**
+     * Returns all subnets registered in the service.
+     *
+     * @return set of subnet
+     */
+    Set<Subnet> subnets();
+
+    /**
+     * Returns all subnets associated with the supplied network.
+     *
+     * @param networkId network id
+     * @return set of subnet
+     */
+    Set<Subnet> subnets(String networkId);
+
+    /**
+     * Returns the OpenStack port with the supplied port ID.
+     *
+     * @param portId openstack port id
+     * @return openstack port
+     */
+    Port port(String portId);
+
+    /**
+     * Returns the OpenStack port with the supplied ONOS port.
+     *
+     * @param port onos port
+     * @return openstack port
+     */
+    Port port(org.onosproject.net.Port port);
+
+    /**
+     * Returns all OpenStack ports registered in the service.
+     *
+     * @return set of ports
+     */
+    Set<Port> ports();
+
+    /**
+     * Returns all OpenStack ports associated with the supplied network.
+     * @param networkId network id
+     * @return set of ports
+     */
+    Set<Port> ports(String networkId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
new file mode 100644
index 0000000..86f338fe
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStore.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.store.Store;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of OpenStack network states; not intended for direct use.
+ */
+public interface OpenstackNetworkStore
+        extends Store<OpenstackNetworkEvent, OpenstackNetworkStoreDelegate> {
+
+    /**
+     * Creates the new network.
+     *
+     * @param network openstack network
+     */
+    void createNetwork(Network network);
+
+    /**
+     * Updates the network.
+     *
+     * @param network openstack network
+     */
+    void updateNetwork(Network network);
+
+    /**
+     * Removes the network with the given network id.
+     *
+     * @param networkId network id
+     * @return removed openstack network; null if failed
+     */
+    Network removeNetwork(String networkId);
+
+    /**
+     * Returns the network with the given network id.
+     *
+     * @param networkId network id
+     * @return network; null if not found
+     */
+    Network network(String networkId);
+
+    /**
+     * Returns all networks.
+     *
+     * @return set of networks
+     */
+    Set<Network> networks();
+
+    /**
+     * Creates a subnet with the given information.
+     *
+     * @param subnet the new subnet
+     */
+    void createSubnet(Subnet subnet);
+
+    /**
+     * Updates a subnet with the given information.
+     *
+     * @param subnet the updated subnet
+     */
+    void updateSubnet(Subnet subnet);
+
+    /**
+     * Removes the subnet with the given subnet id.
+     *
+     * @param subnetId subnet id
+     * @return removed subnet; null if failed
+     */
+    Subnet removeSubnet(String subnetId);
+
+    /**
+     * Returns the subnet with the supplied subnet ID.
+     *
+     * @param subnetId subnet id
+     * @return subnet
+     */
+    Subnet subnet(String subnetId);
+
+    /**
+     * Returns all subnets registered in the service.
+     *
+     * @return set of subnet
+     */
+    Set<Subnet> subnets();
+
+    /**
+     * Creates the new port.
+     *
+     * @param port the new port
+     */
+    void createPort(Port port);
+
+    /**
+     * Updates the port.
+     *
+     * @param port port
+     */
+    void updatePort(Port port);
+
+    /**
+     * Removes the port.
+     *
+     * @param portId port id
+     * @return removed port; null if failed
+     */
+    Port removePort(String portId);
+
+    /**
+     * Returns the port with the given port id.
+     *
+     * @param portId port id
+     * @return port
+     */
+    Port port(String portId);
+
+    /**
+     * Returns all ports.
+     *
+     * @return set of ports
+     */
+    Set<Port> ports();
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStoreDelegate.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStoreDelegate.java
new file mode 100644
index 0000000..ba8ce56
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackNetworkStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * OpenStack network store delegate abstraction.
+ */
+public interface OpenstackNetworkStoreDelegate extends StoreDelegate<OpenstackNetworkEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java
new file mode 100644
index 0000000..5870eb0
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterAdminService.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+/**
+ * Service for administering the inventory of OpenStack router and floating IP.
+ */
+public interface OpenstackRouterAdminService {
+
+    /**
+     * Creates a router with the given information.
+     *
+     * @param osRouter the new router
+     */
+    void createRouter(Router osRouter);
+
+    /**
+     * Updates the router with the given information.
+     *
+     * @param osRouter updated router
+     */
+    void updateRouter(Router osRouter);
+
+    /**
+     * Removes the router with the given router ID.
+     *
+     * @param osRouterId router id
+     */
+    void removeRouter(String osRouterId);
+
+    /**
+     * Adds a new interface to the router.
+     *
+     * @param osRouterIface openstack router interface
+     */
+    void addRouterInterface(RouterInterface osRouterIface);
+
+    /**
+     * Updates the router interface.
+     *
+     * @param osRouterIface openstack router interface
+     */
+    void updateRouterInterface(RouterInterface osRouterIface);
+
+    /**
+     * Removes the router interface.
+     *
+     * @param osRouterIfaceId openstack router interface id
+     */
+    void removeRouterInterface(String osRouterIfaceId);
+
+    /**
+     * Creates a floating IP with the given information.
+     *
+     * @param floatingIP the new floating ip
+     */
+    void createFloatingIp(NetFloatingIP floatingIP);
+
+    /**
+     * Updates the floating IP with the given information.
+     *
+     * @param floatingIP the updated floating ip
+     */
+    void updateFloatingIp(NetFloatingIP floatingIP);
+
+    /**
+     * Removes the floating IP with the given floating IP ID.
+     *
+     * @param floatingIpId floating ip id
+     */
+    void removeFloatingIp(String floatingIpId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterEvent.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterEvent.java
new file mode 100644
index 0000000..4cf4d99
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterEvent.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.joda.time.LocalDateTime;
+import org.onosproject.event.AbstractEvent;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Describes OpenStack router service events.
+ */
+public class OpenstackRouterEvent extends AbstractEvent<OpenstackRouterEvent.Type, Router> {
+
+    private final ExternalGateway exGateway;
+    private final RouterInterface routerIface;
+    private final NetFloatingIP floatingIP;
+    private final String portId;
+
+    public enum Type {
+
+        /**
+         * Signifies that a new OpenStack router is created.
+         */
+        OPENSTACK_ROUTER_CREATED,
+
+        /**
+         * Signifies that the OpenStack router is updated.
+         */
+        OPENSTACK_ROUTER_UPDATED,
+
+        /**
+         * Signifies that the OpenStack router is removed.
+         */
+        OPENSTACK_ROUTER_REMOVED,
+
+        /**
+         * Signifies that the external gateway is added to the router.
+         */
+        OPENSTACK_ROUTER_GATEWAY_ADDED,
+
+        /**
+         * Signifies that the external gateway is removed from the router.
+         */
+        OPENSTACK_ROUTER_GATEWAY_REMOVED,
+
+        /**
+         * Signifies that the OpenStack router interface is added.
+         */
+        OPENSTACK_ROUTER_INTERFACE_ADDED,
+
+        /**
+         * Signifies that the OpenStack router interface is updated.
+         */
+        OPENSTACK_ROUTER_INTERFACE_UPDATED,
+
+        /**
+         * Signifies that the OpenStack router interface is removed.
+         */
+        OPENSTACK_ROUTER_INTERFACE_REMOVED,
+
+        /**
+         * Signifies that a new floating IP is created.
+         */
+        OPENSTACK_FLOATING_IP_CREATED,
+
+        /**
+         * Signifies that the floating IP is updated.
+         */
+        OPENSTACK_FLOATING_IP_UPDATED,
+
+        /**
+         * Signifies that the floating IP is removed.
+         */
+        OPENSTACK_FLOATING_IP_REMOVED,
+
+        /**
+         * Signifies that the floating IP is associated to a fixed IP.
+         */
+        OPENSTACK_FLOATING_IP_ASSOCIATED,
+
+        /**
+         * Signifies that the floating IP disassociated from the fixed IP.
+         */
+        OPENSTACK_FLOATING_IP_DISASSOCIATED
+    }
+
+    /**
+     * Creates an event of a given type for the specified router and the current time.
+     *
+     * @param type   openstack router event type
+     * @param osRouter openstack router
+     */
+    public OpenstackRouterEvent(Type type, Router osRouter) {
+        super(type, osRouter);
+        this.exGateway = null;
+        this.routerIface = null;
+        this.floatingIP = null;
+        this.portId = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified router, external gateway and
+     * the current time.
+     *
+     * @param type      openstack router event type
+     * @param osRouter  openstack router
+     * @param exGateway openstack router external gateway
+     */
+    public OpenstackRouterEvent(Type type, Router osRouter, ExternalGateway exGateway) {
+        super(type, osRouter);
+        this.exGateway = exGateway;
+        this.routerIface = null;
+        this.floatingIP = null;
+        this.portId = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified router, floating IP and
+     * the current time.
+     *
+     * @param type          openstack router event type
+     * @param osRouter      openstack router
+     * @param osRouterIface openstack router interface
+     */
+    public OpenstackRouterEvent(Type type, Router osRouter, RouterInterface osRouterIface) {
+        super(type, osRouter);
+        this.exGateway = null;
+        this.routerIface = osRouterIface;
+        this.floatingIP = null;
+        this.portId = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified router, floating IP and
+     * the current time.
+     *
+     * @param type       openstack router event type
+     * @param router     openstack router
+     * @param floatingIP openstack floating ip
+     */
+    public OpenstackRouterEvent(Type type, Router router, NetFloatingIP floatingIP) {
+        super(type, router);
+        this.exGateway = null;
+        this.routerIface = null;
+        this.floatingIP = floatingIP;
+        this.portId = null;
+    }
+
+    /**
+     * Creates an event of a given type for the specified router, floating IP,
+     * associated OpenStack port ID and the current time.
+     *
+     * @param type       openstack router event type
+     * @param router     openstack router
+     * @param floatingIP openstack floating ip
+     * @param portId     associated openstack port id
+     */
+    public OpenstackRouterEvent(Type type, Router router, NetFloatingIP floatingIP,
+                                String portId) {
+        super(type, router);
+        this.exGateway = null;
+        this.routerIface = null;
+        this.floatingIP = floatingIP;
+        this.portId = portId;
+    }
+
+    /**
+     * Returns the router external gateway object of the router event.
+     *
+     * @return openstack router external gateway; null if the event is not
+     * relevant to the router external gateway
+     */
+    public ExternalGateway externalGateway() {
+        return exGateway;
+    }
+
+    /**
+     * Returns the router interface object of the router event.
+     *
+     * @return openstack router interface; null if the event is not relevant to
+     * the router interface
+     */
+    public RouterInterface routerIface() {
+        return routerIface;
+    }
+
+    /**
+     * Returns the floating IP of the router event.
+     *
+     * @return openstack floating ip; null if the event is not relevant to
+     * the floating ip
+     */
+    public NetFloatingIP floatingIp() {
+        return floatingIP;
+    }
+
+    /**
+     * Returns the associated port ID of the floating IP.
+     *
+     * @return openstack port id; null if the event is not relevant to the
+     * floating ip
+     */
+    public String portId() {
+        return portId;
+    }
+
+    @Override
+    public String toString() {
+        if (floatingIP == null) {
+            return super.toString();
+        }
+        return toStringHelper(this)
+                .add("time", new LocalDateTime(time()))
+                .add("type", type())
+                .add("router", subject())
+                .add("externalGateway", exGateway)
+                .add("routerIface", routerIface)
+                .add("floatingIp", floatingIP)
+                .add("portId", portId)
+                .toString();
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterListener.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterListener.java
new file mode 100644
index 0000000..2442d79
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterListener.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for OpenStack router event.
+ */
+public interface OpenstackRouterListener extends EventListener<OpenstackRouterEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java
new file mode 100644
index 0000000..75c1378
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterService.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.event.ListenerService;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+import java.util.Set;
+
+/**
+ * Handles router update requests from OpenStack.
+ */
+public interface OpenstackRouterService
+        extends ListenerService<OpenstackRouterEvent, OpenstackRouterListener> {
+
+    /**
+     * Returns the router with the supplied router ID.
+     *
+     * @param osRouterId openstack router id
+     * @return openstack router
+     */
+    Router router(String osRouterId);
+
+    /**
+     * Returns all routers.
+     *
+     * @return set of openstack routers
+     */
+    Set<Router> routers();
+
+    /**
+     * Returns the router interface with the given ID.
+     *
+     * @param osRouterIfaceId openstack router interface port id
+     * @return openstack router interface
+     */
+    RouterInterface routerInterface(String osRouterIfaceId);
+
+    /**
+     * Returns all router interfaces.
+     *
+     * @return set of openstack router interfaces
+     */
+    Set<RouterInterface> routerInterfaces();
+
+    /**
+     * Returns all router interfaces with the router ID.
+     *
+     * @param osRouterId openstack router id
+     * @return set of router interfaces
+     */
+    Set<RouterInterface> routerInterfaces(String osRouterId);
+
+    /**
+     * Returns the floating IP with the supplied floating IP ID.
+     * @param floatingIpId floating ip id
+     * @return openstack floating ip
+     */
+    NetFloatingIP floatingIp(String floatingIpId);
+
+    /**
+     * Returns all floating IPs.
+     *
+     * @return set of openstack floating ips
+     */
+    Set<NetFloatingIP> floatingIps();
+
+    /**
+     * Returns all floating IPs associated with the router ID.
+     *
+     * @param routerId router id
+     * @return set of openstack floating ips
+     */
+    Set<NetFloatingIP> floatingIps(String routerId);
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java
new file mode 100644
index 0000000..b0744a0
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStore.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.store.Store;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of OpenStack router states; not intended for direct use.
+ */
+public interface OpenstackRouterStore
+        extends Store<OpenstackRouterEvent, OpenstackRouterStoreDelegate> {
+
+    /**
+     * Creates a new router.
+     *
+     * @param osRouter openstack router
+     */
+    void createRouter(Router osRouter);
+
+    /**
+     * Updates the router.
+     *
+     * @param osRouter updated openstack router
+     */
+    void updateRouter(Router osRouter);
+
+    /**
+     * Removes the router with the supplied ID.
+     *
+     * @param osRouterId openstack router id
+     * @return removed router; null if failed
+     */
+    Router removeRouter(String osRouterId);
+
+    /**
+     * Returns the router with the supplied router ID.
+     *
+     * @param osRouterId openstack router id
+     * @return openstack router; null if not found
+     */
+    Router router(String osRouterId);
+
+    /**
+     * Returns all routers.
+     *
+     * @return set of openstack router
+     */
+    Set<Router> routers();
+
+    /**
+     * Adds a new interface to the router.
+     *
+     * @param osRouterIface openstack router interface
+     */
+    void addRouterInterface(RouterInterface osRouterIface);
+
+    /**
+     * Updates the router interface.
+     *
+     * @param osRouterIface openstack router interface
+     */
+    void updateRouterInterface(RouterInterface osRouterIface);
+
+    /**
+     * Removes the router interface.
+     *
+     * @param osRouterIfaceId openstack router interface id
+     * @return removed router interface; null if failed
+     */
+    RouterInterface removeRouterInterface(String osRouterIfaceId);
+
+    /**
+     * Returns the router interface with the given ID.
+     *
+     * @param osRouterIfaceId openstack router interface port id
+     * @return openstack router interface
+     */
+    RouterInterface routerInterface(String osRouterIfaceId);
+
+    /**
+     * Returns all router interfaces.
+     *
+     * @return set of openstack router interfaces
+     */
+    Set<RouterInterface> routerInterfaces();
+
+    /**
+     * Creates a new floating IP address.
+     *
+     * @param floatingIp openstack floating ip
+     */
+    void createFloatingIp(NetFloatingIP floatingIp);
+
+    /**
+     * Updates the floating IP address.
+     *
+     * @param floatingIp updated openstack floating ip
+     */
+    void updateFloatingIp(NetFloatingIP floatingIp);
+
+    /**
+     * Removes the floating IP with the supplied ID.
+     *
+     * @param floatingIpId floating ip id
+     * @return removed floating ip; null if failed
+     */
+    NetFloatingIP removeFloatingIp(String floatingIpId);
+
+    /**
+     * Returns the floating IP with the supplied ID.
+     *
+     * @param floatingIpId floating ip id
+     * @return openstack floating ip; null if not found
+     */
+    NetFloatingIP floatingIp(String floatingIpId);
+
+    /**
+     * Returns all floating IP addresses.
+     *
+     * @return openstack floating ip; empty set if no floating ip exists
+     */
+    Set<NetFloatingIP> floatingIps();
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStoreDelegate.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStoreDelegate.java
new file mode 100644
index 0000000..12ce679
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRouterStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.api;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * OpenStack router store delegate abstraction.
+ */
+public interface OpenstackRouterStoreDelegate extends StoreDelegate<OpenstackRouterEvent> {
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java
deleted file mode 100644
index 29e492e..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackRoutingService.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.api;
-
-import org.onosproject.net.Host;
-
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackRouterInterface;
-
-/**
- * Handles router update requests from OpenStack.
- */
-public interface OpenstackRoutingService {
-
-    /**
-     * Handles the router create request from OpenStack.
-     *
-     * @param osRouter router information
-     */
-    void createRouter(OpenstackRouter osRouter);
-
-    /**
-     * Handles the router update request from OpenStack.
-     * Update router is called when the name, administrative state, or the external
-     * gateway setting is updated. The external gateway update is the only case
-     * that openstack routing service cares.
-     *
-     * @param osRouter router information
-     */
-    void updateRouter(OpenstackRouter osRouter);
-
-    /**
-     * Handles the router remove request from OpenStack.
-     *
-     * @param osRouterId identifier of the router
-     */
-    void removeRouter(String osRouterId);
-
-    /**
-     * Handles router interface add request from OpenStack.
-     *
-     * @param osInterface router interface information
-     */
-    void addRouterInterface(OpenstackRouterInterface osInterface);
-
-    /**
-     * Handles router interface remove request from OpenStack.
-     *
-     * @param osInterface router interface information
-     */
-    void removeRouterInterface(OpenstackRouterInterface osInterface);
-
-    /**
-     * Handles to purge data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void purgeVmFlow(Host host);
-
-    /**
-     * Handles to reinstall data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
deleted file mode 100644
index 1709c95..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSecurityGroupService.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.api;
-
-import org.onosproject.net.Host;
-
-import org.onosproject.openstackinterface.OpenstackPort;
-
-/**
- * Represents OpenstackSecurityGroupService Interface.
- */
-public interface OpenstackSecurityGroupService {
-
-    /**
-     * Updates the flow rules for Security Group for the VM (OpenstackPort).
-     *
-     * @param osPort OpenstackPort information for the VM
-     */
-    void updateSecurityGroup(OpenstackPort osPort);
-
-    /**
-     * Handles to purge data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void purgeVmFlow(Host host);
-
-    /**
-     * Handles to reinstall data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java
deleted file mode 100644
index dc1113e..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/OpenstackSwitchingService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.api;
-
-import org.onosproject.net.Host;
-
-/**
- * Handles switch update requests from OpenStack and  CLI.
- */
-public interface OpenstackSwitchingService {
-    /**
-     * Handles to purge data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void purgeVmFlow(Host host);
-
-    /**
-     * Handles to reinstall data plane flow of existing VM.
-     *
-     * @param host VM Host information
-     */
-    void reinstallVmFlow(Host host);
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
index 0cd17c1..1696e21 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/api/package-info.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,6 +15,8 @@
  */
 
 /**
- * Application for OpenstackRouting.
+ * Implements OpenStack L3 service plugin, which routes packets between subnets,
+ * forwards packets from internal networks to external ones, and accesses instances
+ * from external networks through floating IPs.
  */
-package org.onosproject.openstacknetworking.api;
\ No newline at end of file
+package org.onosproject.openstacknetworking.api;
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
new file mode 100644
index 0000000..511e89e
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackFloatingIpListCommand.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
+
+import java.util.Comparator;
+import java.util.List;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack floating IP addresses.
+ */
+@Command(scope = "onos", name = "openstack-floatingips",
+        description = "Lists all OpenStack floating IP addresses")
+public class OpenstackFloatingIpListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s";
+
+    @Override
+    protected void execute() {
+        OpenstackRouterService service = AbstractShellCommand.get(OpenstackRouterService.class);
+        List<NetFloatingIP> floatingIps = Lists.newArrayList(service.floatingIps());
+        floatingIps.sort(Comparator.comparing(NetFloatingIP::getFloatingIpAddress));
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(floatingIps)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list networks in JSON format");
+            }
+            return;
+        }
+
+        print(FORMAT, "ID", "Floating IP", "Fixed IP");
+        for (NetFloatingIP floatingIp: floatingIps) {
+            print(FORMAT, floatingIp.getId(),
+                    floatingIp.getFloatingIpAddress(),
+                    Strings.isNullOrEmpty(floatingIp.getFixedIpAddress()) ?
+            "" : floatingIp.getFixedIpAddress());
+        }
+    }
+
+    private JsonNode json(List<NetFloatingIP> floatingIps) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (NetFloatingIP floatingIp: floatingIps) {
+            result.add(writeFloatingIp(floatingIp));
+        }
+        return result;
+    }
+
+    private ObjectNode writeFloatingIp(NetFloatingIP floatingIp) {
+        try {
+            String strFloatingIp = ObjectMapperSingleton.getContext(NeutronFloatingIP.class)
+                    .writerFor(NeutronFloatingIP.class)
+                    .writeValueAsString(floatingIp);
+            log.trace(strFloatingIp);
+            return (ObjectNode) mapper().readTree(strFloatingIp.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
deleted file mode 100644
index b2f5b8d..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstancePurgeFlowsCommand.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.host.HostService;
-
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Purge Flows of OpenstackInstance Data Plane.
- */
-
-@Command(scope = "onos", name = "openstack-purge-flows",
-        description = "Purge data plane flows of existing VM.")
-public class OpenstackInstancePurgeFlowsCommand extends AbstractShellCommand {
-
-    @Option(name = "-a", aliases = "--all",
-            description = "HostIDs are all existing VM",
-            required = false, multiValued = false)
-    private Boolean allhost = false;
-
-    @Argument(index = 0, name = "hostId", description = "HostID(s)",
-            required = false, multiValued = true)
-    private String[] hostids = null;
-
-    @Override
-    protected void execute() {
-         HostService hostService = AbstractShellCommand.get(HostService.class);
-
-         OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
-         OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
-         OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
-         OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
-
-         if (allhost) {
-            switchingService.purgeVmFlow(null);
-            sgService.purgeVmFlow(null);
-            routingService.purgeVmFlow(null);
-            floatingIpService.purgeVmFlow(null);
-
-            hostService.getHosts().forEach(host -> {
-                printHost(host);
-            });
-         } else if (hostids != null) {
-            for (String hostid : hostids) {
-                Host host = hostService.getHost(HostId.hostId(hostid));
-                if (host == null) {
-                    continue;
-                }
-                switchingService.purgeVmFlow(host);
-                sgService.purgeVmFlow(host);
-                routingService.purgeVmFlow(host);
-                floatingIpService.purgeVmFlow(host);
-                printHost(host);
-            }
-         }
-    }
-
-    private void printHost(Host host) {
-        print("Purge data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
-              host.id(), host.annotations().value(VXLAN_ID),
-              host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
deleted file mode 100644
index eda334d..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackInstanceReInstallFlowCommand.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.cli;
-
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.host.HostService;
-
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Re-Install Flows of OpenstackInstance Data Plane.
- */
-
-@Command(scope = "onos", name = "openstack-reinstall-flows",
-        description = "Re-install data plane flows of existing VM.")
-public class OpenstackInstanceReInstallFlowCommand extends AbstractShellCommand {
-
-    @Option(name = "-a", aliases = "--all",
-    description = "HostIDs are all existing VM", required = false, multiValued = false)
-    private Boolean allhost = false;
-
-    @Argument(index = 0, name = "hostId", description = "HostID(s)",
-            required = false, multiValued = true)
-    private String[] hostids = null;
-
-    @Override
-    protected void execute() {
-         HostService hostService = AbstractShellCommand.get(HostService.class);
-
-         OpenstackSwitchingService switchingService = getService(OpenstackSwitchingService.class);
-         OpenstackSecurityGroupService sgService = getService(OpenstackSecurityGroupService.class);
-         OpenstackRoutingService routingService = getService(OpenstackRoutingService.class);
-         OpenstackFloatingIpService floatingIpService = getService(OpenstackFloatingIpService.class);
-
-         if (allhost) {
-            hostService.getHosts().forEach(host -> {
-                switchingService.reinstallVmFlow(host);
-                sgService.reinstallVmFlow(host);
-                routingService.reinstallVmFlow(host);
-                floatingIpService.reinstallVmFlow(host);
-                printHost(host);
-
-            });
-         } else if (hostids != null) {
-            for (String hostid : hostids) {
-                Host host = hostService.getHost(HostId.hostId(hostid));
-                if (host == null) {
-                    continue;
-                }
-                switchingService.reinstallVmFlow(host);
-                sgService.reinstallVmFlow(host);
-                routingService.reinstallVmFlow(host);
-                floatingIpService.reinstallVmFlow(host);
-                printHost(host);
-            }
-         }
-    }
-
-    private void printHost(Host host) {
-        print("Re-install data plane flows of VM(hostid=%s, vni=%s, ip=%s, mac=%s).",
-              host.id(), host.annotations().value(VXLAN_ID),
-              host.ipAddresses().stream().findFirst().get().getIp4Address(), host.mac());
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
new file mode 100644
index 0000000..8b489c9
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack networks.
+ */
+@Command(scope = "onos", name = "openstack-networks",
+        description = "Lists all OpenStack networks")
+public class OpenstackNetworkListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+    @Override
+    protected void execute() {
+        OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+        List<Network> networks = Lists.newArrayList(service.networks());
+        networks.sort(Comparator.comparing(Network::getName));
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(networks)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list networks in JSON format");
+            }
+            return;
+        }
+
+        print(FORMAT, "ID", "Name", "VNI", "Subnets");
+        for (Network net: networks) {
+            List<String> subnets = service.subnets().stream()
+                    .filter(subnet -> subnet.getNetworkId().equals(net.getId()))
+                    .map(Subnet::getCidr)
+                    .collect(Collectors.toList());
+            print(FORMAT, net.getId(),
+                    net.getName(),
+                    net.getProviderSegID(),
+                    subnets.isEmpty() ? "" : subnets);
+        }
+    }
+
+    private JsonNode json(List<Network> networks) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (Network net: networks) {
+            result.add(writeNetwork(net));
+        }
+        return result;
+    }
+
+    private ObjectNode writeNetwork(Network net) {
+        try {
+            String strNet = ObjectMapperSingleton.getContext(NeutronNetwork.class)
+                    .writerFor(NeutronNetwork.class)
+                    .writeValueAsString(net);
+            return (ObjectNode) mapper().readTree(strNet.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
new file mode 100644
index 0000000..58d44cd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackPortListCommand.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack ports.
+ */
+@Command(scope = "onos", name = "openstack-ports",
+        description = "Lists all OpenStack ports")
+public class OpenstackPortListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+    @Argument(name = "networkId", description = "Network ID")
+    private String networkId = null;
+
+    @Override
+    protected void execute() {
+        OpenstackNetworkService service = AbstractShellCommand.get(OpenstackNetworkService.class);
+
+        List<Port> ports = Lists.newArrayList(service.ports());
+        ports.sort(Comparator.comparing(Port::getNetworkId));
+
+        if (!Strings.isNullOrEmpty(networkId)) {
+            ports.removeIf(port -> !port.getNetworkId().equals(networkId));
+        }
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(ports)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list ports in JSON format");
+            }
+            return;
+        }
+
+        print(FORMAT, "ID", "Network", "MAC", "Fixed IPs");
+        for (Port port: ports) {
+            List<String> fixedIps = port.getFixedIps().stream()
+                    .map(IP::getIpAddress)
+                    .collect(Collectors.toList());
+            Network osNet = service.network(port.getNetworkId());
+            print(FORMAT, port.getId(),
+                    osNet.getName(),
+                    port.getMacAddress(),
+                    fixedIps.isEmpty() ? "" : fixedIps);
+        }
+    }
+
+    private JsonNode json(List<Port> ports) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (Port port: ports) {
+            result.add(writePort(port));
+        }
+        return result;
+    }
+
+    private ObjectNode writePort(Port port) {
+        try {
+            String strPort = ObjectMapperSingleton.getContext(NeutronPort.class)
+                    .writerFor(NeutronPort.class)
+                    .writeValueAsString(port);
+            return (ObjectNode) mapper().readTree(strPort.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
new file mode 100644
index 0000000..4f7c8bd
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackRouterListCommand.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.cli;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Lists;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+
+/**
+ * Lists OpenStack routers.
+ */
+@Command(scope = "onos", name = "openstack-routers",
+        description = "Lists all OpenStack routers")
+public class OpenstackRouterListCommand extends AbstractShellCommand {
+
+    private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+
+    private final OpenstackRouterService routerService =
+            AbstractShellCommand.get(OpenstackRouterService.class);
+    private final OpenstackNetworkService netService =
+            AbstractShellCommand.get(OpenstackNetworkService.class);
+
+    @Override
+    protected void execute() {
+        List<Router> routers = Lists.newArrayList(routerService.routers());
+        routers.sort(Comparator.comparing(Router::getName));
+
+        if (outputJson()) {
+            try {
+                print("%s", mapper().writeValueAsString(json(routers)));
+            } catch (JsonProcessingException e) {
+                print("Failed to list routers in JSON format");
+            }
+            return;
+        }
+        print(FORMAT, "ID", "Name", "External", "Internal");
+        for (Router router: routers) {
+            String exNetId = router.getExternalGatewayInfo() != null ?
+                router.getExternalGatewayInfo().getNetworkId() : null;
+
+            List<String> externals = Lists.newArrayList();
+            if (exNetId != null) {
+                // FIXME fix openstack4j to provide external gateway ip info
+                externals = netService.ports(exNetId).stream()
+                        .filter(port -> Objects.equals(port.getDeviceId(),
+                                router.getId()))
+                        .flatMap(port -> port.getFixedIps().stream())
+                        .map(IP::getIpAddress)
+                        .collect(Collectors.toList());
+            }
+
+            List<String> internals = Lists.newArrayList();
+            routerService.routerInterfaces(router.getId()).forEach(iface -> {
+                    internals.add(getRouterIfaceIp(iface));
+            });
+            print(FORMAT, router.getId(), router.getName(), externals, internals);
+        }
+    }
+
+    private String getRouterIfaceIp(RouterInterface iface) {
+        Port osPort = netService.port(iface.getPortId());
+        IP ipAddr = osPort.getFixedIps().stream()
+                .filter(ip -> ip.getSubnetId().equals(iface.getSubnetId()))
+                .findAny().orElse(null);
+        return ipAddr == null ? "" : ipAddr.getIpAddress();
+    }
+
+    private JsonNode json(List<Router> routers) {
+        ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
+        for (Router router: routers) {
+            result.add(writeRouter(router));
+        }
+        return result;
+    }
+
+    private ObjectNode writeRouter(Router osRouter) {
+        try {
+            String strNet = ObjectMapperSingleton.getContext(NeutronRouter.class)
+                    .writerFor(NeutronRouter.class)
+                    .writeValueAsString(osRouter);
+            return (ObjectNode) mapper().readTree(strNet.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java
deleted file mode 100644
index f37a323..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/AbstractVmHandler.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.impl;
-
-import org.onlab.osgi.DefaultServiceDirectory;
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.packet.Ip4Address;
-import org.onlab.util.Tools;
-import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.Host;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackSubnet;
-import org.slf4j.Logger;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Provides abstract virtual machine handler.
- */
-public abstract class AbstractVmHandler {
-    protected final Logger log = getLogger(getClass());
-
-    protected final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
-            groupedThreads(this.getClass().getSimpleName(), "event-handler"));
-
-    protected CoreService coreService;
-    protected MastershipService mastershipService;
-    protected HostService hostService;
-    protected OpenstackInterfaceService openstackService;
-
-    protected HostListener hostListener = new InternalHostListener();
-
-    protected void activate() {
-        ServiceDirectory services = new DefaultServiceDirectory();
-        coreService = services.get(CoreService.class);
-        mastershipService = services.get(MastershipService.class);
-        openstackService = services.get(OpenstackInterfaceService.class);
-        hostService = services.get(HostService.class);
-        hostService.addListener(hostListener);
-
-        log.info("Started");
-    }
-
-    protected void deactivate() {
-        hostService.removeListener(hostListener);
-        eventExecutor.shutdown();
-
-        log.info("Stopped");
-    }
-
-    /**
-     * Performs any action when a host is detected.
-     *
-     * @param host detected host
-     */
-    protected abstract void hostDetected(Host host);
-
-    /**
-     * Performs any action when a host is removed.
-     *
-     * @param host removed host
-     */
-    protected abstract void hostRemoved(Host host);
-
-    protected boolean isValidHost(Host host) {
-        return !host.ipAddresses().isEmpty() &&
-                host.annotations().value(VXLAN_ID) != null &&
-                host.annotations().value(NETWORK_ID) != null &&
-                host.annotations().value(TENANT_ID) != null &&
-                host.annotations().value(PORT_ID) != null;
-    }
-
-    protected Set<Host> getVmsInDifferentCnode(Host host) {
-        return Tools.stream(hostService.getHosts())
-                .filter(h -> !h.location().deviceId().equals(host.location().deviceId()))
-                .filter(this::isValidHost)
-                .filter(h -> Objects.equals(getVni(h), getVni(host)))
-                .collect(Collectors.toSet());
-    }
-
-    protected Optional<Host> getVmByPortId(String portId) {
-        return Tools.stream(hostService.getHosts())
-                .filter(this::isValidHost)
-                .filter(host -> host.annotations().value(PORT_ID).equals(portId))
-                .findFirst();
-    }
-
-    protected Set<Host> getHosts(OpenstackSubnet osSubnet) {
-        return Tools.stream(hostService.getHosts())
-                .filter(host -> host.annotations().value(SUBNET_ID).equals(osSubnet.id()))
-                .collect(Collectors.toSet());
-    }
-
-    protected Optional<OpenstackRouter> getRouter(Host host) {
-        return openstackService.routers().stream()
-                .filter(router -> routableSubNets(router.id()).stream()
-                        .filter(subnet -> subnet.id().equals(host.annotations().value(SUBNET_ID)))
-                        .findAny().isPresent())
-                .findAny();
-    }
-
-    protected Set<OpenstackSubnet> routableSubNets(String osRouterId) {
-        return openstackService.ports().stream()
-                .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) &&
-                        p.deviceId().equals(osRouterId))
-                .map(p -> openstackService.subnet(p.fixedIps().keySet().stream().findFirst().get()))
-                .collect(Collectors.toSet());
-    }
-
-    protected Ip4Address getIp(Host host) {
-        return host.ipAddresses().stream().findFirst().get().getIp4Address();
-    }
-
-    protected String getVni(Host host) {
-        return host.annotations().value(VXLAN_ID);
-    }
-
-    protected String getTenantId(Host host) {
-        return host.annotations().value(TENANT_ID);
-    }
-
-    private class InternalHostListener implements HostListener {
-
-        @Override
-        public void event(HostEvent event) {
-            Host host = event.subject();
-            if (!mastershipService.isLocalMaster(host.location().deviceId())) {
-                // do not allow to proceed without mastership
-                return;
-            }
-
-            if (!isValidHost(host)) {
-                log.debug("Invalid host event, ignore it {}", host);
-                return;
-            }
-
-            switch (event.type()) {
-                case HOST_UPDATED:
-                case HOST_ADDED:
-                    eventExecutor.execute(() -> hostDetected(host));
-                    break;
-                case HOST_REMOVED:
-                    eventExecutor.execute(() -> hostRemoved(host));
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
new file mode 100644
index 0000000..ac29491
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackNetworkStore.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.collect.ImmutableSet;
+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.Service;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.openstack4j.model.network.IPVersionType;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.NetworkType;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.State;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronAllowedAddressPair;
+import org.openstack4j.openstack.networking.domain.NeutronExtraDhcpOptCreate;
+import org.openstack4j.openstack.networking.domain.NeutronHostRoute;
+import org.openstack4j.openstack.networking.domain.NeutronIP;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
+import org.openstack4j.openstack.networking.domain.NeutronPool;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+import org.openstack4j.openstack.networking.domain.NeutronSubnet;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.OpenstackNetworkEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages the inventory of OpenStack network, subnet, and port using a {@code ConsistentMap}.
+ */
+@Service
+@Component(immediate = true)
+public class DistributedOpenstackNetworkStore
+        extends AbstractStore<OpenstackNetworkEvent, OpenstackNetworkStoreDelegate>
+        implements OpenstackNetworkStore {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_DUPLICATE = " already exists";
+
+    private static final KryoNamespace SERIALIZER_NEUTRON_L2 = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(Network.class)
+            .register(NeutronNetwork.class)
+            .register(State.class)
+            .register(NetworkType.class)
+            .register(Port.class)
+            .register(NeutronPort.class)
+            .register(NeutronIP.class)
+            .register(NeutronAllowedAddressPair.class)
+            .register(NeutronExtraDhcpOptCreate.class)
+            .register(Subnet.class)
+            .register(NeutronSubnet.class)
+            .register(NeutronPool.class)
+            .register(NeutronHostRoute.class)
+            .register(IPVersionType.class)
+            .build();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private final MapEventListener<String, Network> networkMapListener = new OpenstackNetworkMapListener();
+    private final MapEventListener<String, Subnet> subnetMapListener = new OpenstackSubnetMapListener();
+    private final MapEventListener<String, Port> portMapListener = new OpenstackPortMapListener();
+
+    private ConsistentMap<String, Network> osNetworkStore;
+    private ConsistentMap<String, Subnet> osSubnetStore;
+    private ConsistentMap<String, Port> osPortStore;
+
+    @Activate
+    protected void activate() {
+        ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+
+        osNetworkStore = storageService.<String, Network>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
+                .withName("openstack-networkstore")
+                .withApplicationId(appId)
+                .build();
+        osNetworkStore.addListener(networkMapListener);
+
+        osSubnetStore = storageService.<String, Subnet>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
+                .withName("openstack-subnetstore")
+                .withApplicationId(appId)
+                .build();
+        osSubnetStore.addListener(subnetMapListener);
+
+        osPortStore = storageService.<String, Port>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L2))
+                .withName("openstack-portstore")
+                .withApplicationId(appId)
+                .build();
+        osPortStore.addListener(portMapListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osNetworkStore.removeListener(networkMapListener);
+        osSubnetStore.removeListener(subnetMapListener);
+        osPortStore.removeListener(portMapListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createNetwork(Network osNet) {
+        osNetworkStore.compute(osNet.getId(), (id, existing) -> {
+            final String error = osNet.getName() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return osNet;
+        });
+    }
+
+    @Override
+    public void updateNetwork(Network osNet) {
+        osNetworkStore.compute(osNet.getId(), (id, existing) -> {
+            final String error = osNet.getName() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return osNet;
+        });
+    }
+
+    @Override
+    public Network removeNetwork(String netId) {
+        Versioned<Network> osNet = osNetworkStore.remove(netId);
+        return osNet == null ? null : osNet.value();
+    }
+
+    @Override
+    public Network network(String netId) {
+        Versioned<Network> versioned = osNetworkStore.get(netId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<Network> networks() {
+        Set<Network> osNets = osNetworkStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osNets);
+    }
+
+    @Override
+    public void createSubnet(Subnet osSubnet) {
+        osSubnetStore.compute(osSubnet.getId(), (id, existing) -> {
+            final String error = osSubnet.getId() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return osSubnet;
+        });
+    }
+
+    @Override
+    public void updateSubnet(Subnet osSubnet) {
+        osSubnetStore.compute(osSubnet.getId(), (id, existing) -> {
+            final String error = osSubnet.getId() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return osSubnet;
+        });
+    }
+
+    @Override
+    public Subnet removeSubnet(String subnetId) {
+        Versioned<Subnet> osSubnet = osSubnetStore.remove(subnetId);
+        return osSubnet == null ? null : osSubnet.value();
+    }
+
+    @Override
+    public Subnet subnet(String subnetId) {
+        Versioned<Subnet> osSubnet = osSubnetStore.get(subnetId);
+        return osSubnet == null ? null : osSubnet.value();
+    }
+
+    @Override
+    public Set<Subnet> subnets() {
+        Set<Subnet> osSubnets = osSubnetStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osSubnets);
+    }
+
+    @Override
+    public void createPort(Port osPort) {
+        osPortStore.compute(osPort.getId(), (id, existing) -> {
+            final String error = osPort.getId() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return osPort;
+        });
+    }
+
+    @Override
+    public void updatePort(Port osPort) {
+        osPortStore.compute(osPort.getId(), (id, existing) -> {
+            final String error = osPort.getId() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return osPort;
+        });
+    }
+
+    @Override
+    public Port removePort(String portId) {
+        Versioned<Port> osPort = osPortStore.remove(portId);
+        return osPort == null ? null : osPort.value();
+    }
+
+    @Override
+    public Port port(String portId) {
+        Versioned<Port> osPort = osPortStore.get(portId);
+        return osPort == null ? null : osPort.value();
+    }
+
+    @Override
+    public Set<Port> ports() {
+        Set<Port> osPorts = osPortStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osPorts);
+    }
+
+    private class OpenstackNetworkMapListener implements MapEventListener<String, Network> {
+
+        @Override
+        public void event(MapEvent<String, Network> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("OpenStack network updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_NETWORK_UPDATED,
+                                event.newValue().value()));
+                    });
+                    break;
+                case INSERT:
+                    log.debug("OpenStack network created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_NETWORK_CREATED,
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("OpenStack network removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_NETWORK_REMOVED,
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+    }
+
+    private class OpenstackSubnetMapListener implements MapEventListener<String, Subnet> {
+
+        @Override
+        public void event(MapEvent<String, Subnet> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("OpenStack subnet updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_SUBNET_UPDATED,
+                                network(event.newValue().value().getNetworkId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case INSERT:
+                    log.debug("OpenStack subnet created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_SUBNET_CREATED,
+                                network(event.newValue().value().getNetworkId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("OpenStack subnet removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_SUBNET_REMOVED,
+                                network(event.oldValue().value().getNetworkId()),
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+    }
+
+    private class OpenstackPortMapListener implements MapEventListener<String, Port> {
+
+        @Override
+        public void event(MapEvent<String, Port> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("OpenStack port updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_PORT_UPDATED,
+                                network(event.newValue().value().getNetworkId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case INSERT:
+                    log.debug("OpenStack port created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_PORT_CREATED,
+                                network(event.newValue().value().getNetworkId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("OpenStack port removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNetworkEvent(
+                                OPENSTACK_PORT_REMOVED,
+                                network(event.oldValue().value().getNetworkId()),
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
new file mode 100644
index 0000000..6b10512
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/DistributedOpenstackRouterStore.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStore;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStoreDelegate;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.State;
+import org.openstack4j.openstack.networking.domain.NeutronExternalGateway;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
+import org.openstack4j.openstack.networking.domain.NeutronHostRoute;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
+import org.osgi.service.component.annotations.Activate;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
+import static org.onosproject.openstacknetworking.api.OpenstackRouterEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages the inventory of OpenStack router and floating IP using a {@code ConsistentMap}.
+ */
+@Service
+@Component(immediate = true)
+public class DistributedOpenstackRouterStore
+        extends AbstractStore<OpenstackRouterEvent, OpenstackRouterStoreDelegate>
+        implements OpenstackRouterStore {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_DUPLICATE = " already exists";
+
+    private static final KryoNamespace SERIALIZER_NEUTRON_L3 = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(Router.class)
+            .register(NeutronRouter.class)
+            .register(State.class)
+            .register(NeutronHostRoute.class)
+            .register(ExternalGateway.class)
+            .register(NeutronExternalGateway.class)
+            .register(RouterInterface.class)
+            .register(NeutronRouterInterface.class)
+            .register(NetFloatingIP.class)
+            .register(NeutronFloatingIP.class)
+            .build();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+    private final MapEventListener<String, Router> routerMapListener = new OpenstackRouterMapListener();
+    private final MapEventListener<String, RouterInterface> routerInterfaceMapListener =
+            new OpenstackRouterInterfaceMapListener();
+    private final MapEventListener<String, NetFloatingIP> floatingIpMapListener =
+            new OpenstackFloatingIpMapListener();
+
+    private ConsistentMap<String, Router> osRouterStore;
+    private ConsistentMap<String, RouterInterface> osRouterInterfaceStore;
+    private ConsistentMap<String, NetFloatingIP> osFloatingIpStore;
+
+    @Activate
+    protected void activate() {
+        ApplicationId appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+
+        osRouterStore = storageService.<String, Router>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3))
+                .withName("openstack-routerstore")
+                .withApplicationId(appId)
+                .build();
+        osRouterStore.addListener(routerMapListener);
+
+        osRouterInterfaceStore = storageService.<String, RouterInterface>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3))
+                .withName("openstack-routerifacestore")
+                .withApplicationId(appId)
+                .build();
+        osRouterInterfaceStore.addListener(routerInterfaceMapListener);
+
+        osFloatingIpStore = storageService.<String, NetFloatingIP>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON_L3))
+                .withName("openstack-floatingipstore")
+                .withApplicationId(appId)
+                .build();
+        osFloatingIpStore.addListener(floatingIpMapListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osRouterStore.removeListener(routerMapListener);
+        osRouterInterfaceStore.removeListener(routerInterfaceMapListener);
+        osFloatingIpStore.removeListener(floatingIpMapListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createRouter(Router osRouter) {
+        osRouterStore.compute(osRouter.getId(), (id, existing) -> {
+            final String error = osRouter.getName() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return osRouter;
+        });
+    }
+
+    @Override
+    public void updateRouter(Router osRouter) {
+        osRouterStore.compute(osRouter.getId(), (id, existing) -> {
+            final String error = osRouter.getName() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return osRouter;
+        });
+    }
+
+    @Override
+    public Router removeRouter(String routerId) {
+        Versioned<Router> osRouter = osRouterStore.remove(routerId);
+        return osRouter == null ? null : osRouter.value();
+    }
+
+    @Override
+    public Router router(String routerId) {
+        Versioned<Router> versioned = osRouterStore.get(routerId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<Router> routers() {
+        Set<Router> osRouters = osRouterStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osRouters);
+    }
+
+    @Override
+    public void addRouterInterface(RouterInterface osRouterIface) {
+        osRouterInterfaceStore.compute(osRouterIface.getPortId(), (id, existing) -> {
+            final String error = osRouterIface.getPortId() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return osRouterIface;
+        });
+    }
+
+    @Override
+    public void updateRouterInterface(RouterInterface osRouterIface) {
+        osRouterInterfaceStore.compute(osRouterIface.getPortId(), (id, existing) -> {
+            final String error = osRouterIface.getPortId() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return osRouterIface;
+        });
+    }
+
+    @Override
+    public RouterInterface removeRouterInterface(String routerIfaceId) {
+        Versioned<RouterInterface> osRouterIface = osRouterInterfaceStore.remove(routerIfaceId);
+        return osRouterIface == null ? null : osRouterIface.value();
+    }
+
+    @Override
+    public RouterInterface routerInterface(String routerIfaceId) {
+        Versioned<RouterInterface> osRouterIface = osRouterInterfaceStore.get(routerIfaceId);
+        return osRouterIface == null ? null : osRouterIface.value();
+    }
+
+    @Override
+    public Set<RouterInterface> routerInterfaces() {
+        Set<RouterInterface> osRouterIfaces = osRouterInterfaceStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osRouterIfaces);
+    }
+
+    @Override
+    public void createFloatingIp(NetFloatingIP osFloatingIp) {
+        osFloatingIpStore.compute(osFloatingIp.getId(), (id, existing) -> {
+            final String error = osFloatingIp.getId() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return osFloatingIp;
+        });
+    }
+
+    @Override
+    public void updateFloatingIp(NetFloatingIP osFloatingIp) {
+        osFloatingIpStore.compute(osFloatingIp.getId(), (id, existing) -> {
+            final String error = osFloatingIp.getId() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return osFloatingIp;
+        });
+    }
+
+    @Override
+    public NetFloatingIP removeFloatingIp(String floatingIpId) {
+        Versioned<NetFloatingIP> osFloatingIp = osFloatingIpStore.remove(floatingIpId);
+        return osFloatingIp == null ? null : osFloatingIp.value();
+    }
+
+    @Override
+    public NetFloatingIP floatingIp(String floatingIpId) {
+        Versioned<NetFloatingIP> osFloatingIp = osFloatingIpStore.get(floatingIpId);
+        return osFloatingIp == null ? null : osFloatingIp.value();
+    }
+
+    @Override
+    public Set<NetFloatingIP> floatingIps() {
+        Set<NetFloatingIP> osFloatingIps = osFloatingIpStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osFloatingIps);
+    }
+
+    private class OpenstackRouterMapListener implements MapEventListener<String, Router> {
+
+        @Override
+        public void event(MapEvent<String, Router> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("OpenStack router updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_ROUTER_UPDATED,
+                                event.newValue().value()));
+                        processGatewayUpdate(event);
+                    });
+                    break;
+                case INSERT:
+                    log.debug("OpenStack router created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_ROUTER_CREATED,
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("OpenStack router removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_ROUTER_REMOVED,
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+
+        private void processGatewayUpdate(MapEvent<String, Router> event) {
+            ExternalGateway oldGateway = event.oldValue().value().getExternalGatewayInfo();
+            ExternalGateway newGateway = event.newValue().value().getExternalGatewayInfo();
+
+            if (oldGateway == null && newGateway != null) {
+                notifyDelegate(new OpenstackRouterEvent(
+                        OPENSTACK_ROUTER_GATEWAY_ADDED,
+                        event.newValue().value(), newGateway));
+            }
+            if (oldGateway != null && newGateway == null) {
+                notifyDelegate(new OpenstackRouterEvent(
+                        OPENSTACK_ROUTER_GATEWAY_ADDED,
+                        event.newValue().value(), oldGateway));
+            }
+        }
+    }
+
+    private class OpenstackRouterInterfaceMapListener implements MapEventListener<String, RouterInterface> {
+
+        @Override
+        public void event(MapEvent<String, RouterInterface> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("OpenStack router interface updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_ROUTER_INTERFACE_UPDATED,
+                                router(event.newValue().value().getId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case INSERT:
+                    log.debug("OpenStack router interface created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_ROUTER_INTERFACE_ADDED,
+                                router(event.newValue().value().getId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("OpenStack router interface removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_ROUTER_INTERFACE_REMOVED,
+                                router(event.oldValue().value().getId()),
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+    }
+
+    private class OpenstackFloatingIpMapListener implements MapEventListener<String, NetFloatingIP> {
+
+        @Override
+        public void event(MapEvent<String, NetFloatingIP> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("OpenStack floating IP updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        Router osRouter = Strings.isNullOrEmpty(
+                                event.newValue().value().getRouterId()) ?
+                                null :
+                                router(event.newValue().value().getRouterId());
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_FLOATING_IP_UPDATED,
+                                osRouter,
+                                event.newValue().value()));
+                        processFloatingIpUpdate(event, osRouter);
+                    });
+                    break;
+                case INSERT:
+                    log.debug("OpenStack floating IP created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        Router osRouter = Strings.isNullOrEmpty(
+                                event.newValue().value().getRouterId()) ?
+                                null :
+                                router(event.newValue().value().getRouterId());
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_FLOATING_IP_CREATED,
+                                osRouter,
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("OpenStack floating IP removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        Router osRouter = Strings.isNullOrEmpty(
+                                event.oldValue().value().getRouterId()) ?
+                                null :
+                                router(event.oldValue().value().getRouterId());
+                        notifyDelegate(new OpenstackRouterEvent(
+                                OPENSTACK_FLOATING_IP_REMOVED,
+                                osRouter,
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+
+        private void processFloatingIpUpdate(MapEvent<String, NetFloatingIP> event,
+                                             Router osRouter) {
+            String oldPortId = event.oldValue().value().getPortId();
+            String newPortId = event.newValue().value().getPortId();
+
+            if (Strings.isNullOrEmpty(oldPortId) && !Strings.isNullOrEmpty(newPortId)) {
+                notifyDelegate(new OpenstackRouterEvent(
+                        OPENSTACK_FLOATING_IP_ASSOCIATED,
+                        osRouter,
+                        event.newValue().value(), newPortId));
+            }
+            if (!Strings.isNullOrEmpty(oldPortId) && Strings.isNullOrEmpty(newPortId)) {
+                notifyDelegate(new OpenstackRouterEvent(
+                        OPENSTACK_FLOATING_IP_DISASSOCIATED,
+                        osRouter,
+                        event.newValue().value(), oldPortId));
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePort.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePort.java
new file mode 100644
index 0000000..de6a3ce
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePort.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.PortNumber;
+import org.onosproject.openstacknetworking.api.InstancePort;
+
+import java.util.Objects;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of instance port based on host subsystem.
+ * Basically, HostBasedInstancePort is just a wrapper of a host, which helps
+ * mapping between OpenStack port and the OVS port and retrieving information
+ * such as IP address, location, and so on.
+ */
+public final class HostBasedInstancePort implements InstancePort {
+
+    static final String ANNOTATION_NETWORK_ID = "networkId";
+    static final String ANNOTATION_PORT_ID = "portId";
+    static final String ANNOTATION_CREATE_TIME = "createTime";
+
+    private final Host host;
+
+    /**
+     * Default constructor.
+     *
+     * @param instance host object of this instance
+     */
+    private HostBasedInstancePort(Host instance) {
+        this.host = instance;
+    }
+
+    /**
+     * Returns new instance.
+     *
+     * @param host host object of this instance
+     * @return instance
+     */
+    public static HostBasedInstancePort of(Host host) {
+        checkNotNull(host);
+        checkArgument(!Strings.isNullOrEmpty(host.annotations().value(ANNOTATION_NETWORK_ID)));
+        checkArgument(!Strings.isNullOrEmpty(host.annotations().value(ANNOTATION_PORT_ID)));
+        checkArgument(!Strings.isNullOrEmpty(host.annotations().value(ANNOTATION_CREATE_TIME)));
+
+        return new HostBasedInstancePort(host);
+    }
+
+    @Override
+    public String networkId() {
+        return host.annotations().value(ANNOTATION_NETWORK_ID);
+    }
+
+    @Override
+    public String portId() {
+        return host.annotations().value(ANNOTATION_PORT_ID);
+    }
+
+    @Override
+    public MacAddress macAddress() {
+        return host.mac();
+    }
+
+    @Override
+    public IpAddress ipAddress() {
+        Optional<IpAddress> ipAddr = host.ipAddresses().stream().findFirst();
+        return ipAddr.orElse(null);
+    }
+
+    @Override
+    public DeviceId deviceId() {
+        return host.location().deviceId();
+    }
+
+    @Override
+    public PortNumber portNumber() {
+        return host.location().port();
+    }
+
+    @Override
+    public String toString() {
+        return host.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof HostBasedInstancePort) {
+            HostBasedInstancePort that = (HostBasedInstancePort) obj;
+            if (Objects.equals(this.portId(), that.portId())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(portId());
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java
new file mode 100644
index 0000000..85fd5bf
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.collect.ImmutableSet;
+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.MacAddress;
+import org.onlab.util.Tools;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortEvent;
+import org.onosproject.openstacknetworking.api.InstancePortListener;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.slf4j.Logger;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_DETECTED;
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_UPDATED;
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_VANISHED;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing host based instance ports.
+ * It also provides instance port events for the hosts mapped to OpenStack VM interface.
+ */
+@Service
+@Component(immediate = true)
+public class HostBasedInstancePortManager
+        extends ListenerRegistry<InstancePortEvent, InstancePortListener>
+        implements InstancePortService {
+
+    protected final Logger log = getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    private final HostListener hostListener = new InternalHostListener();
+
+    @Activate
+    protected void activate() {
+        hostService.addListener(hostListener);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        hostService.removeListener(hostListener);
+        log.info("Stopped");
+    }
+
+    @Override
+    public InstancePort instancePort(MacAddress macAddress) {
+        Host host = hostService.getHost(HostId.hostId(macAddress));
+        if (host == null || !isValidHost(host)) {
+            return null;
+        }
+        return HostBasedInstancePort.of(host);
+    }
+
+    @Override
+    public InstancePort instancePort(IpAddress ipAddress, String osNetId) {
+        return Tools.stream(hostService.getHosts()).filter(this::isValidHost)
+                .map(HostBasedInstancePort::of)
+                .filter(instPort -> instPort.networkId().equals(osNetId))
+                .filter(instPort -> instPort.ipAddress().equals(ipAddress))
+                .findAny().orElse(null);
+    }
+
+    @Override
+    public InstancePort instancePort(String osPortId) {
+        return Tools.stream(hostService.getHosts()).filter(this::isValidHost)
+                .map(HostBasedInstancePort::of)
+                .filter(instPort -> instPort.portId().equals(osPortId))
+                .findAny().orElse(null);
+    }
+
+    @Override
+    public Set<InstancePort> instancePorts() {
+        Set<InstancePort> instPors = Tools.stream(hostService.getHosts())
+                .filter(this::isValidHost)
+                .map(HostBasedInstancePort::of)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(instPors);
+    }
+
+    @Override
+    public Set<InstancePort> instancePorts(String osNetId) {
+        Set<InstancePort> instPors = Tools.stream(hostService.getHosts())
+                .filter(this::isValidHost)
+                .map(HostBasedInstancePort::of)
+                .filter(instPort -> instPort.networkId().equals(osNetId))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(instPors);
+    }
+
+    private boolean isValidHost(Host host) {
+        return !host.ipAddresses().isEmpty() &&
+                host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
+                host.annotations().value(ANNOTATION_PORT_ID) != null;
+    }
+
+    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;
+            }
+            return true;
+        }
+
+        @Override
+        public void event(HostEvent event) {
+            InstancePort instPort = HostBasedInstancePort.of(event.subject());
+            InstancePortEvent instPortEvent;
+            switch (event.type()) {
+                case HOST_UPDATED:
+                    instPortEvent = new InstancePortEvent(
+                            OPENSTACK_INSTANCE_PORT_UPDATED,
+                            instPort);
+                    log.debug("Instance port is updated: {}", instPort);
+                    process(instPortEvent);
+                    break;
+                case HOST_ADDED:
+                    instPortEvent = new InstancePortEvent(
+                            OPENSTACK_INSTANCE_PORT_DETECTED,
+                            instPort);
+                    log.debug("Instance port is detected: {}", instPort);
+                    process(instPortEvent);
+                    break;
+                case HOST_REMOVED:
+                    instPortEvent = new InstancePortEvent(
+                            OPENSTACK_INSTANCE_PORT_VANISHED,
+                            instPort);
+                    log.debug("Instance port is disabled: {}", instPort);
+                    process(instPortEvent);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java
deleted file mode 100644
index 602b0f7..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFloatingIpManager.java
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.impl;
-
-import com.google.common.base.Strings;
-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.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.host.HostService;
-import org.onosproject.openstackinterface.OpenstackFloatingIP;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.Serializer;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.Versioned;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-
-
-@Service
-@Component(immediate = true)
-public class OpenstackFloatingIpManager extends AbstractVmHandler implements OpenstackFloatingIpService {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected StorageService storageService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService nodeService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ScalableGatewayService gatewayService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    private static final String NOT_ASSOCIATED = "null";
-    private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER =
-            KryoNamespace.newBuilder().register(KryoNamespaces.API);
-
-    private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
-            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
-    private final InternalNodeListener nodeListener = new InternalNodeListener();
-    private ConsistentMap<IpAddress, Host> floatingIpMap;
-
-    private ApplicationId appId;
-
-    @Activate
-    protected void activate() {
-        super.activate();
-        appId = coreService.registerApplication(ROUTING_APP_ID);
-        nodeService.addListener(nodeListener);
-        floatingIpMap = storageService.<IpAddress, Host>consistentMapBuilder()
-                .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
-                .withName("openstackrouting-floatingip")
-                .withApplicationId(appId)
-                .build();
-
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        super.deactivate();
-        nodeService.removeListener(nodeListener);
-        log.info("Stopped");
-    }
-
-    @Override
-    protected void hostDetected(Host host) {
-        IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
-        Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
-                .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
-                .findFirst();
-        if (floatingIp.isPresent()) {
-            eventExecutor.execute(() -> associateFloatingIp(floatingIp.get()));
-        }
-    }
-
-    @Override
-    protected void hostRemoved(Host host) {
-        IpAddress hostIp = host.ipAddresses().stream().findFirst().get();
-        Optional<OpenstackFloatingIP> floatingIp = openstackService.floatingIps().stream()
-                .filter(fip -> fip.fixedIpAddress() != null && fip.fixedIpAddress().equals(hostIp))
-                .findFirst();
-        if (floatingIp.isPresent()) {
-            eventExecutor.execute(() -> disassociateFloatingIp(floatingIp.get()));
-        }
-    }
-
-    @Override
-    public void reinstallVmFlow(Host host) {
-        if (host == null) {
-            hostService.getHosts().forEach(h -> {
-                hostDetected(h);
-                log.info("Re-Install data plane flow of virtual machine {}", h);
-            });
-        } else {
-            hostDetected(host);
-            log.info("Re-Install data plane flow of virtual machine {}", host);
-        }
-    }
-
-    @Override
-    public void purgeVmFlow(Host host) {
-        if (host == null) {
-            hostService.getHosts().forEach(h -> {
-                hostRemoved(h);
-                log.info("Purge data plane flow of virtual machine {}", h);
-            });
-        } else {
-            hostRemoved(host);
-            log.info("Purge data plane flow of virtual machine {}", host);
-        }
-    }
-
-    @Override
-    public void createFloatingIp(OpenstackFloatingIP floatingIp) {
-    }
-
-    @Override
-    public void updateFloatingIp(OpenstackFloatingIP floatingIp) {
-        if (Strings.isNullOrEmpty(floatingIp.portId()) ||
-                floatingIp.portId().equals(NOT_ASSOCIATED)) {
-            eventExecutor.execute(() -> disassociateFloatingIp(floatingIp));
-        } else {
-            eventExecutor.execute(() -> associateFloatingIp(floatingIp));
-        }
-    }
-
-    @Override
-    public void deleteFloatingIp(String floatingIpId) {
-    }
-
-    private void associateFloatingIp(OpenstackFloatingIP floatingIp) {
-        Optional<Host> associatedVm = Tools.stream(hostService.getHosts())
-                .filter(host -> Objects.equals(
-                        host.annotations().value(PORT_ID),
-                        floatingIp.portId()))
-                .findAny();
-        if (!associatedVm.isPresent()) {
-            log.warn("Failed to associate floating IP({}) to port:{}",
-                     floatingIp.floatingIpAddress(),
-                     floatingIp.portId());
-            return;
-        }
-
-        floatingIpMap.put(floatingIp.floatingIpAddress(), associatedVm.get());
-        populateFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.get());
-
-        log.info("Associated floating IP {} to fixed IP {}",
-                 floatingIp.floatingIpAddress(), floatingIp.fixedIpAddress());
-    }
-
-    private void disassociateFloatingIp(OpenstackFloatingIP floatingIp) {
-        Versioned<Host> associatedVm = floatingIpMap.remove(floatingIp.floatingIpAddress());
-        if (associatedVm == null) {
-            log.warn("Failed to disassociate floating IP({})",
-                     floatingIp.floatingIpAddress());
-            // No VM is actually associated with the floating IP, do nothing
-            return;
-        }
-
-        removeFloatingIpRules(floatingIp.floatingIpAddress(), associatedVm.value());
-        log.info("Disassociated floating IP {} from fixed IP {}",
-                 floatingIp.floatingIpAddress(),
-                 associatedVm.value().ipAddresses());
-    }
-
-    private void populateFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
-        populateFloatingIpIncomingRules(floatingIp, associatedVm);
-        populateFloatingIpOutgoingRules(floatingIp, associatedVm);
-    }
-
-    private void removeFloatingIpRules(IpAddress floatingIp, Host associatedVm) {
-        Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
-        if (!fixedIp.isPresent()) {
-            log.warn("Failed to remove floating IP({}) from {}",
-                     floatingIp, associatedVm);
-            return;
-        }
-
-        TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
-        TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
-
-        sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
-                .matchIPSrc(fixedIp.get().toIpPrefix());
-
-        sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(floatingIp.toIpPrefix());
-
-        gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
-            TrafficSelector.Builder sForTrafficFromVmBuilder = DefaultTrafficSelector.builder()
-                    .matchEthType(Ethernet.TYPE_IPV4)
-                    .matchIPDst(floatingIp.toIpPrefix())
-                    .matchInPort(nodeService.tunnelPort(deviceId).get());
-
-            RulePopulatorUtil.setRule(
-                    flowObjectiveService,
-                    appId,
-                    deviceId,
-                    sOutgoingBuilder.build(),
-                    DefaultTrafficTreatment.builder().build(),
-                    ForwardingObjective.Flag.VERSATILE,
-                    FLOATING_RULE_PRIORITY, false);
-
-            RulePopulatorUtil.setRule(
-                    flowObjectiveService,
-                    appId,
-                    deviceId,
-                    sIncomingBuilder.build(),
-                    DefaultTrafficTreatment.builder().build(),
-                    ForwardingObjective.Flag.VERSATILE,
-                    FLOATING_RULE_PRIORITY, false);
-
-            RulePopulatorUtil.setRule(
-                    flowObjectiveService,
-                    appId,
-                    deviceId,
-                    sForTrafficFromVmBuilder.build(),
-                    DefaultTrafficTreatment.builder().build(),
-                    ForwardingObjective.Flag.VERSATILE,
-                    FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY, false);
-        });
-    }
-
-    private void populateFloatingIpIncomingRules(IpAddress floatingIp, Host associatedVm) {
-        DeviceId cnodeId = associatedVm.location().deviceId();
-        Optional<IpAddress> dataIp = nodeService.dataIp(cnodeId);
-        Optional<IpAddress> fixedIp = associatedVm.ipAddresses().stream().findFirst();
-
-        if (!fixedIp.isPresent() || !dataIp.isPresent()) {
-            log.warn("Failed to associate floating IP({})", floatingIp);
-            return;
-        }
-
-        TrafficSelector selectorForTrafficFromExternal = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(floatingIp.toIpPrefix())
-                .build();
-
-        gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
-            TrafficTreatment treatmentForTrafficFromExternal = DefaultTrafficTreatment.builder()
-                    .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
-                    .setEthDst(associatedVm.mac())
-                    .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
-                    .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
-                    .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
-                               gnodeId)
-                    .setOutput(nodeService.tunnelPort(gnodeId).get())
-                    .build();
-
-            ForwardingObjective forwardingObjectiveForTrafficFromExternal = DefaultForwardingObjective.builder()
-                    .withSelector(selectorForTrafficFromExternal)
-                    .withTreatment(treatmentForTrafficFromExternal)
-                    .withFlag(ForwardingObjective.Flag.VERSATILE)
-                    .withPriority(FLOATING_RULE_PRIORITY)
-                    .fromApp(appId)
-                    .add();
-
-            flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromExternal);
-
-
-            TrafficSelector selectorForTrafficFromVm = DefaultTrafficSelector.builder()
-                    .matchEthType(Ethernet.TYPE_IPV4)
-                    .matchIPDst(floatingIp.toIpPrefix())
-                    .matchInPort(nodeService.tunnelPort(gnodeId).get())
-                    .build();
-
-            TrafficTreatment treatmentForTrafficFromVm = DefaultTrafficTreatment.builder()
-                    .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
-                    .setEthDst(associatedVm.mac())
-                    .setIpDst(associatedVm.ipAddresses().stream().findFirst().get())
-                    .setTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
-                    .extension(buildExtension(deviceService, gnodeId, dataIp.get().getIp4Address()),
-                            gnodeId)
-                    .setOutput(PortNumber.IN_PORT)
-                    .build();
-
-            ForwardingObjective forwardingObjectiveForTrafficFromVm = DefaultForwardingObjective.builder()
-                    .withSelector(selectorForTrafficFromVm)
-                    .withTreatment(treatmentForTrafficFromVm)
-                    .withFlag(ForwardingObjective.Flag.VERSATILE)
-                    .withPriority(FLOATING_RULE_FOR_TRAFFIC_FROM_VM_PRIORITY)
-                    .fromApp(appId)
-                    .add();
-
-            flowObjectiveService.forward(gnodeId, forwardingObjectiveForTrafficFromVm);
-
-        });
-    }
-
-    private void populateFloatingIpOutgoingRules(IpAddress floatingIp, Host associatedVm) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(Long.valueOf(associatedVm.annotations().value(VXLAN_ID)))
-                .matchIPSrc(associatedVm.ipAddresses().stream().findFirst().get().toIpPrefix())
-                .build();
-
-        gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setIpSrc(floatingIp)
-                    .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
-                    .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
-                    .setOutput(gatewayService.getUplinkPort(gnodeId))
-                    .build();
-
-            ForwardingObjective fo = DefaultForwardingObjective.builder()
-                    .withSelector(selector)
-                    .withTreatment(treatment)
-                    .withFlag(ForwardingObjective.Flag.VERSATILE)
-                    .withPriority(FLOATING_RULE_PRIORITY)
-                    .fromApp(appId)
-                    .add();
-
-            flowObjectiveService.forward(gnodeId, fo);
-        });
-    }
-
-    // TODO consider the case that port with associated floating IP is attached to a VM
-
-    private class InternalNodeListener implements OpenstackNodeListener {
-
-        @Override
-        public void event(OpenstackNodeEvent event) {
-            OpenstackNode node = event.node();
-
-            switch (event.type()) {
-                case COMPLETE:
-                    reinstallVmFlow(null);
-                    break;
-                case INIT:
-                case DEVICE_CREATED:
-                case INCOMPLETE:
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java
deleted file mode 100644
index d58062a..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackIcmpHandler.java
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.impl;
-
-import com.google.common.collect.Maps;
-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.onlab.packet.Ethernet;
-import org.onlab.packet.ICMP;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.net.packet.InboundPacket;
-import org.onosproject.net.packet.OutboundPacket;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketPriority;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketService;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.GatewayNode;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-import org.slf4j.Logger;
-
-import java.nio.ByteBuffer;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
-import static org.slf4j.LoggerFactory.getLogger;
-
-
-/**
- * Handle ICMP packet sent from OpenStack Gateway nodes.
- * For a request to any private network gateway IPs, it generates fake reply.
- * For a request to the external network, it does source NAT with a public IP and
- * forward the request to the external only if the request instance has external
- * connection setups.
- */
-@Component(immediate = true)
-public class OpenstackIcmpHandler {
-    protected final Logger log = getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected PacketService packetService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ScalableGatewayService gatewayService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService nodeService;
-
-    private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
-            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
-    private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
-    private final InternalNodeListener nodeListener = new InternalNodeListener();
-    private final Map<String, Host> icmpInfoMap = Maps.newHashMap();
-
-    ApplicationId appId;
-
-    @Activate
-    protected void activate() {
-        appId = coreService.registerApplication(ROUTING_APP_ID);
-        packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
-        nodeService.addListener(nodeListener);
-        requestPacket(appId);
-
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        packetService.removeProcessor(packetProcessor);
-        log.info("Stopped");
-    }
-
-    private void requestPacket(ApplicationId appId) {
-        TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPProtocol(IPv4.PROTOCOL_ICMP)
-                .build();
-
-        gatewayService.getGatewayDeviceIds().forEach(gateway -> {
-            packetService.requestPackets(icmpSelector,
-                                         PacketPriority.CONTROL,
-                                         appId,
-                                         Optional.of(gateway));
-            log.debug("Requested ICMP packet on {}", gateway);
-        });
-    }
-
-    private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
-        IPv4 ipPacket = (IPv4) ethernet.getPayload();
-        log.trace("Processing ICMP packet from ip {}, mac {}",
-                  Ip4Address.valueOf(ipPacket.getSourceAddress()),
-                  ethernet.getSourceMAC());
-
-        ICMP icmp = (ICMP) ipPacket.getPayload();
-        short icmpId = getIcmpId(icmp);
-
-        DeviceId srcDevice = context.inPacket().receivedFrom().deviceId();
-        switch (icmp.getIcmpType()) {
-            case ICMP.TYPE_ECHO_REQUEST:
-                Optional<Host> reqHost = hostService.getHostsByMac(ethernet.getSourceMAC())
-                        .stream().findFirst();
-                if (!reqHost.isPresent()) {
-                    log.warn("No host found for MAC {}", ethernet.getSourceMAC());
-                    return;
-                }
-
-                // TODO Considers icmp between internal subnets belong to the same router.
-                // TODO do we have to support ICMP reply for non-existing gateway?
-                Ip4Address gatewayIp = Ip4Address.valueOf(
-                        reqHost.get().annotations().value(Constants.GATEWAY_IP));
-                if (Objects.equals(ipPacket.getDestinationAddress(), gatewayIp.toInt())) {
-                    processRequestToGateway(ipPacket, reqHost.get());
-                } else {
-                    Optional<Ip4Address> srcNatIp = getSrcNatIp(reqHost.get());
-                    if (!srcNatIp.isPresent()) {
-                        log.trace("VM {} has no external connection", reqHost.get());
-                        return;
-                    }
-
-                    sendRequestToExternal(ipPacket, srcDevice, srcNatIp.get());
-                    String icmpInfoKey = String.valueOf(icmpId)
-                            .concat(String.valueOf(srcNatIp.get().toInt()))
-                            .concat(String.valueOf(ipPacket.getDestinationAddress()));
-                    icmpInfoMap.putIfAbsent(icmpInfoKey, reqHost.get());
-                }
-                break;
-            case ICMP.TYPE_ECHO_REPLY:
-                String icmpInfoKey = String.valueOf(icmpId)
-                        .concat(String.valueOf(ipPacket.getDestinationAddress()))
-                        .concat(String.valueOf(ipPacket.getSourceAddress()));
-
-                processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey));
-                icmpInfoMap.remove(icmpInfoKey);
-                break;
-            default:
-                break;
-        }
-    }
-
-    // TODO do we have to handle the request to the fake gateway?
-    private void processRequestToGateway(IPv4 ipPacket, Host reqHost) {
-        ICMP icmpReq = (ICMP) ipPacket.getPayload();
-        icmpReq.setChecksum((short) 0);
-        icmpReq.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
-
-        int destinationAddress = ipPacket.getSourceAddress();
-
-        ipPacket.setSourceAddress(ipPacket.getDestinationAddress())
-                .setDestinationAddress(destinationAddress)
-                .resetChecksum();
-
-        ipPacket.setPayload(icmpReq);
-        Ethernet icmpReply = new Ethernet();
-        icmpReply.setEtherType(Ethernet.TYPE_IPV4)
-                .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
-                .setDestinationMACAddress(reqHost.mac())
-                .setPayload(ipPacket);
-
-        sendReply(icmpReply, reqHost);
-    }
-
-    private void sendRequestToExternal(IPv4 ipPacket, DeviceId srcDevice, Ip4Address srcNatIp) {
-        ICMP icmpReq = (ICMP) ipPacket.getPayload();
-        icmpReq.resetChecksum();
-        ipPacket.setSourceAddress(srcNatIp.toInt()).resetChecksum();
-        ipPacket.setPayload(icmpReq);
-
-        Ethernet icmpRequestEth = new Ethernet();
-        icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
-                .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
-                .setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC)
-                .setPayload(ipPacket);
-
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(gatewayService.getUplinkPort(srcDevice))
-                .build();
-
-        OutboundPacket packet = new DefaultOutboundPacket(
-                srcDevice,
-                treatment,
-                ByteBuffer.wrap(icmpRequestEth.serialize()));
-
-        packetService.emit(packet);
-    }
-
-    private void processReplyFromExternal(IPv4 ipPacket, Host dstHost) {
-        ICMP icmpReply = (ICMP) ipPacket.getPayload();
-        icmpReply.resetChecksum();
-
-        Ip4Address ipAddress = dstHost.ipAddresses().stream().findFirst().get().getIp4Address();
-        ipPacket.setDestinationAddress(ipAddress.toInt())
-                .resetChecksum();
-        ipPacket.setPayload(icmpReply);
-
-        Ethernet icmpResponseEth = new Ethernet();
-        icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
-                .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
-                .setDestinationMACAddress(dstHost.mac())
-                .setPayload(ipPacket);
-
-        sendReply(icmpResponseEth, dstHost);
-    }
-
-    private void sendReply(Ethernet icmpReply, Host dstHost) {
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(dstHost.location().port())
-                .build();
-
-        OutboundPacket packet = new DefaultOutboundPacket(
-                dstHost.location().deviceId(),
-                treatment,
-                ByteBuffer.wrap(icmpReply.serialize()));
-
-        packetService.emit(packet);
-    }
-
-    private Optional<Ip4Address> getSrcNatIp(Host host) {
-        // TODO cache external gateway IP for each network because
-        // asking Neutron for every ICMP request is a bad idea
-        Optional<OpenstackPort> osPort = openstackService.ports().stream()
-                .filter(port -> port.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE) &&
-                        Objects.equals(host.annotations().value(NETWORK_ID),
-                                       port.networkId()))
-                .findAny();
-        if (!osPort.isPresent()) {
-            return Optional.empty();
-        }
-
-        OpenstackRouter osRouter = openstackService.router(osPort.get().deviceId());
-        if (osRouter == null) {
-            return Optional.empty();
-        }
-
-        return osRouter.gatewayExternalInfo().externalFixedIps()
-                .values().stream().findAny();
-    }
-
-    private short getIcmpId(ICMP icmp) {
-        return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
-    }
-
-    private class InternalPacketProcessor implements PacketProcessor {
-
-        @Override
-        public void process(PacketContext context) {
-            if (context.isHandled()) {
-                return;
-            } else if (!gatewayService.getGatewayDeviceIds().contains(
-                    context.inPacket().receivedFrom().deviceId())) {
-                // return if the packet is not from gateway nodes
-                return;
-            }
-
-            InboundPacket pkt = context.inPacket();
-            Ethernet ethernet = pkt.parsed();
-            if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
-                return;
-            }
-
-            IPv4 iPacket = (IPv4) ethernet.getPayload();
-            if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
-                    eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
-            }
-        }
-    }
-
-    private class InternalNodeListener implements OpenstackNodeListener {
-
-        @Override
-        public void event(OpenstackNodeEvent event) {
-            OpenstackNode node = event.node();
-
-            switch (event.type()) {
-                case COMPLETE:
-                    if (node.type() == GATEWAY) {
-                        log.info("GATEWAY node {} detected", node.hostname());
-                        eventExecutor.execute(() -> {
-                            GatewayNode gnode = GatewayNode.builder()
-                                    .gatewayDeviceId(node.intBridge())
-                                    .dataIpAddress(node.dataIp().getIp4Address())
-                                    .uplinkIntf(node.externalPortName().get())
-                                    .build();
-                            gatewayService.addGatewayNode(gnode);
-                            requestPacket(appId);
-                        });
-                    }
-                    break;
-                case INIT:
-                case DEVICE_CREATED:
-                case INCOMPLETE:
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
new file mode 100644
index 0000000..2517510
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing OpenStack network,
+ * subnet, and port.
+ */
+@Service
+@Component(immediate = true)
+public class OpenstackNetworkManager
+        extends ListenerRegistry<OpenstackNetworkEvent, OpenstackNetworkListener>
+        implements OpenstackNetworkAdminService, OpenstackNetworkService {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String MSG_NETWORK  = "OpenStack network %s %s";
+    private static final String MSG_SUBNET  = "OpenStack subnet %s %s";
+    private static final String MSG_PORT = "OpenStack port %s %s";
+    private static final String MSG_CREATED = "created";
+    private static final String MSG_UPDATED = "updated";
+    private static final String MSG_REMOVED = "removed";
+
+    private static final String ERR_NULL_NETWORK  = "OpenStack network cannot be null";
+    private static final String ERR_NULL_NETWORK_ID  = "OpenStack network ID cannot be null";
+    private static final String ERR_NULL_NETWORK_NAME  = "OpenStack network name cannot be null";
+    private static final String ERR_NULL_SUBNET = "OpenStack subnet cannot be null";
+    private static final String ERR_NULL_SUBNET_ID = "OpenStack subnet ID cannot be null";
+    private static final String ERR_NULL_SUBNET_NET_ID = "OpenStack subnet network ID cannot be null";
+    private static final String ERR_NULL_SUBNET_CIDR = "OpenStack subnet CIDR cannot be null";
+    private static final String ERR_NULL_PORT = "OpenStack port cannot be null";
+    private static final String ERR_NULL_PORT_ID = "OpenStack port ID cannot be null";
+    private static final String ERR_NULL_PORT_NET_ID = "OpenStack port network ID cannot be null";
+
+    private static final String ERR_IN_USE = " still in use";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkStore osNetworkStore;
+
+    private final OpenstackNetworkStoreDelegate delegate = new InternalNetworkStoreDelegate();
+
+    @Activate
+    protected void activate() {
+        coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+        osNetworkStore.setDelegate(delegate);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osNetworkStore.unsetDelegate(delegate);
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createNetwork(Network osNet) {
+        checkNotNull(osNet, ERR_NULL_NETWORK);
+        checkArgument(!Strings.isNullOrEmpty(osNet.getId()), ERR_NULL_NETWORK_ID);
+        checkArgument(!Strings.isNullOrEmpty(osNet.getName()), ERR_NULL_NETWORK_NAME);
+
+        osNetworkStore.createNetwork(osNet);
+        log.info(String.format(MSG_NETWORK, osNet.getName(), MSG_CREATED));
+    }
+
+    @Override
+    public void updateNetwork(Network osNet) {
+        checkNotNull(osNet, ERR_NULL_NETWORK);
+        checkArgument(!Strings.isNullOrEmpty(osNet.getId()), ERR_NULL_NETWORK_ID);
+        checkArgument(!Strings.isNullOrEmpty(osNet.getName()), ERR_NULL_NETWORK_NAME);
+
+        osNetworkStore.updateNetwork(osNet);
+        log.info(String.format(MSG_NETWORK, osNet.getId(), MSG_UPDATED));
+    }
+
+    @Override
+    public void removeNetwork(String netId) {
+        checkArgument(!Strings.isNullOrEmpty(netId), ERR_NULL_NETWORK_ID);
+        synchronized (this) {
+            if (isNetworkInUse(netId)) {
+                final String error = String.format(MSG_NETWORK, netId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            Network osNet = osNetworkStore.removeNetwork(netId);
+            if (osNet != null) {
+                log.info(String.format(MSG_NETWORK, osNet.getName(), MSG_REMOVED));
+            }
+        }
+    }
+
+    @Override
+    public void createSubnet(Subnet osSubnet) {
+        checkNotNull(osSubnet, ERR_NULL_SUBNET);
+        checkArgument(!Strings.isNullOrEmpty(osSubnet.getId()), ERR_NULL_SUBNET_ID);
+        checkArgument(!Strings.isNullOrEmpty(osSubnet.getNetworkId()), ERR_NULL_SUBNET_NET_ID);
+        checkArgument(!Strings.isNullOrEmpty(osSubnet.getCidr()), ERR_NULL_SUBNET_CIDR);
+
+        osNetworkStore.createSubnet(osSubnet);
+        log.info(String.format(MSG_SUBNET, osSubnet.getCidr(), MSG_CREATED));
+    }
+
+    @Override
+    public void updateSubnet(Subnet osSubnet) {
+        checkNotNull(osSubnet, ERR_NULL_SUBNET);
+        checkArgument(!Strings.isNullOrEmpty(osSubnet.getId()), ERR_NULL_SUBNET_ID);
+        checkArgument(!Strings.isNullOrEmpty(osSubnet.getNetworkId()), ERR_NULL_SUBNET_NET_ID);
+        checkArgument(!Strings.isNullOrEmpty(osSubnet.getCidr()), ERR_NULL_SUBNET_CIDR);
+
+        osNetworkStore.updateSubnet(osSubnet);
+        log.info(String.format(MSG_SUBNET, osSubnet.getCidr(), MSG_UPDATED));
+    }
+
+    @Override
+    public void removeSubnet(String subnetId) {
+        checkArgument(!Strings.isNullOrEmpty(subnetId), ERR_NULL_SUBNET_ID);
+        synchronized (this) {
+            if (isSubnetInUse(subnetId)) {
+                final String error = String.format(MSG_SUBNET, subnetId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            Subnet osSubnet = osNetworkStore.removeSubnet(subnetId);
+            if (osSubnet != null) {
+                log.info(String.format(MSG_SUBNET, osSubnet.getCidr(), MSG_REMOVED));
+            }
+        }
+    }
+
+    @Override
+    public void createPort(Port osPort) {
+        checkNotNull(osPort, ERR_NULL_PORT);
+        checkArgument(!Strings.isNullOrEmpty(osPort.getId()), ERR_NULL_PORT_ID);
+        checkArgument(!Strings.isNullOrEmpty(osPort.getNetworkId()), ERR_NULL_PORT_NET_ID);
+
+        osNetworkStore.createPort(osPort);
+        log.info(String.format(MSG_PORT, osPort.getId(), MSG_CREATED));
+    }
+
+    @Override
+    public void updatePort(Port osPort) {
+        checkNotNull(osPort, ERR_NULL_PORT);
+        checkArgument(!Strings.isNullOrEmpty(osPort.getId()), ERR_NULL_PORT_ID);
+        checkArgument(!Strings.isNullOrEmpty(osPort.getNetworkId()), ERR_NULL_PORT_NET_ID);
+
+        osNetworkStore.updatePort(osPort);
+        log.info(String.format(MSG_SUBNET, osPort.getId(), MSG_UPDATED));
+    }
+
+    @Override
+    public void removePort(String portId) {
+        checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_PORT_ID);
+        synchronized (this) {
+            if (isPortInUse(portId)) {
+                final String error = String.format(MSG_PORT, portId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            Port osPort = osNetworkStore.removePort(portId);
+            if (osPort != null) {
+                log.info(String.format(MSG_SUBNET, osPort.getId(), MSG_REMOVED));
+            }
+        }
+    }
+
+    @Override
+    public Network network(String netId) {
+        checkArgument(!Strings.isNullOrEmpty(netId), ERR_NULL_NETWORK_ID);
+        return osNetworkStore.network(netId);
+    }
+
+    @Override
+    public Set<Network> networks() {
+        return osNetworkStore.networks();
+    }
+
+    @Override
+    public Subnet subnet(String subnetId) {
+        checkArgument(!Strings.isNullOrEmpty(subnetId), ERR_NULL_SUBNET_ID);
+        return osNetworkStore.subnet(subnetId);
+    }
+
+    @Override
+    public Set<Subnet> subnets() {
+        return osNetworkStore.subnets();
+    }
+
+    @Override
+    public Set<Subnet> subnets(String netId) {
+        Set<Subnet> osSubnets = osNetworkStore.subnets().stream()
+                .filter(subnet -> Objects.equals(subnet.getNetworkId(), netId))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osSubnets);
+    }
+
+    @Override
+    public Port port(String portId) {
+        checkArgument(!Strings.isNullOrEmpty(portId), ERR_NULL_PORT_ID);
+        return osNetworkStore.port(portId);
+    }
+
+    @Override
+    public Port port(org.onosproject.net.Port port) {
+        String portName = port.annotations().value(PORT_NAME);
+        if (Strings.isNullOrEmpty(portName)) {
+            return null;
+        }
+        Optional<Port> osPort = osNetworkStore.ports()
+                .stream()
+                .filter(p -> p.getId().contains(portName.substring(3)))
+                .findFirst();
+        return osPort.isPresent() ? osPort.get() : null;
+    }
+
+    @Override
+    public Set<Port> ports() {
+        return osNetworkStore.ports();
+    }
+
+    @Override
+    public Set<Port> ports(String netId) {
+        Set<Port> osPorts = osNetworkStore.ports().stream()
+                .filter(port -> Objects.equals(port.getNetworkId(), netId))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osPorts);
+    }
+
+    private boolean isNetworkInUse(String netId) {
+        return !subnets(netId).isEmpty() && !ports(netId).isEmpty();
+    }
+
+    private boolean isSubnetInUse(String subnetId) {
+        // TODO add something if needed
+        return false;
+    }
+
+    private boolean isPortInUse(String portId) {
+        // TODO add something if needed
+        return false;
+    }
+
+    private class InternalNetworkStoreDelegate implements OpenstackNetworkStoreDelegate {
+
+        @Override
+        public void notify(OpenstackNetworkEvent event) {
+            if (event != null) {
+                log.trace("send oepnstack switching event {}", event);
+                process(event);
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java
deleted file mode 100644
index 5a5eafc..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackPnatHandler.java
+++ /dev/null
@@ -1,433 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.impl;
-
-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.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.TCP;
-import org.onlab.packet.TpPort;
-import org.onlab.packet.UDP;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.net.packet.InboundPacket;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketService;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.Serializer;
-import org.onosproject.store.service.StorageService;
-import org.slf4j.Logger;
-
-import java.nio.ByteBuffer;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.concurrent.ExecutorService;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Handle NAT packet processing for managing flow rules in openstack nodes.
- */
-@Component(immediate = true)
-public class OpenstackPnatHandler {
-    private final Logger log = getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected PacketService packetService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected StorageService storageService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService nodeService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ScalableGatewayService gatewayService;
-
-    private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
-            .register(KryoNamespaces.API);
-
-    private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000;
-    private static final int TP_PORT_MINIMUM_NUM = 1024;
-    private static final int TP_PORT_MAXIMUM_NUM = 65535;
-
-    private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
-            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
-    private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
-
-    private ConsistentMap<Integer, String> tpPortNumMap;
-    private ApplicationId appId;
-
-    @Activate
-    protected void activate() {
-        appId = coreService.registerApplication(ROUTING_APP_ID);
-        tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
-                .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
-                .withName("openstackrouting-tpportnum")
-                .withApplicationId(appId)
-                .build();
-
-        packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        packetService.removeProcessor(packetProcessor);
-        log.info("Stopped");
-    }
-
-    private void processPnatPacket(PacketContext context, Ethernet ethernet) {
-        IPv4 iPacket = (IPv4) ethernet.getPayload();
-        InboundPacket inboundPacket = context.inPacket();
-
-        int srcPort = getPortNum(ethernet.getSourceMAC(), iPacket.getDestinationAddress());
-        OpenstackPort osPort = getOpenstackPort(ethernet.getSourceMAC());
-        if (osPort == null) {
-            return;
-        }
-        Ip4Address externalGatewayIp = getExternalGatewayIp(osPort);
-        if (externalGatewayIp == null) {
-            return;
-        }
-
-        populatePnatFlowRules(context.inPacket(),
-                osPort,
-                TpPort.tpPort(srcPort),
-                externalGatewayIp);
-
-        packetOut((Ethernet) ethernet.clone(),
-                  inboundPacket.receivedFrom().deviceId(),
-                  srcPort,
-                  externalGatewayIp);
-    }
-
-    private void packetOut(Ethernet ethernet, DeviceId deviceId, int portNum, Ip4Address externalIp) {
-        IPv4 iPacket = (IPv4) ethernet.getPayload();
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-
-        switch (iPacket.getProtocol()) {
-            case IPv4.PROTOCOL_TCP:
-                TCP tcpPacket = (TCP) iPacket.getPayload();
-                tcpPacket.setSourcePort(portNum);
-                tcpPacket.resetChecksum();
-                tcpPacket.setParent(iPacket);
-                iPacket.setPayload(tcpPacket);
-                break;
-            case IPv4.PROTOCOL_UDP:
-                UDP udpPacket = (UDP) iPacket.getPayload();
-                udpPacket.setSourcePort(portNum);
-                udpPacket.resetChecksum();
-                udpPacket.setParent(iPacket);
-                iPacket.setPayload(udpPacket);
-                break;
-            default:
-                log.trace("Temporally, this method can process UDP and TCP protocol.");
-                return;
-        }
-
-        iPacket.setSourceAddress(externalIp.toString());
-        iPacket.resetChecksum();
-        iPacket.setParent(ethernet);
-        ethernet.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
-        ethernet.setPayload(iPacket);
-
-        treatment.setOutput(gatewayService.getUplinkPort(deviceId));
-        ethernet.resetChecksum();
-        packetService.emit(new DefaultOutboundPacket(
-                deviceId,
-                treatment.build(),
-                ByteBuffer.wrap(ethernet.serialize())));
-    }
-
-    private int getPortNum(MacAddress sourceMac, int destinationAddress) {
-        int portNum = findUnusedPortNum();
-        if (portNum == 0) {
-            clearPortNumMap();
-            portNum = findUnusedPortNum();
-        }
-        tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
-        return portNum;
-    }
-
-    private int findUnusedPortNum() {
-        for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
-            if (!tpPortNumMap.containsKey(i)) {
-                return i;
-            }
-        }
-        return 0;
-    }
-
-    private void clearPortNumMap() {
-        tpPortNumMap.entrySet().forEach(e -> {
-            if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) {
-                tpPortNumMap.remove(e.getKey());
-            }
-        });
-    }
-
-    // TODO there can be multiple routers connected to a particular openstack port
-    // TODO cache router information
-    private Ip4Address getExternalGatewayIp(OpenstackPort osPort) {
-        Optional<OpenstackPort> routerPort = openstackService.ports().stream()
-                .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
-                .filter(p -> checkSameSubnet(p, osPort))
-                .findAny();
-        if (!routerPort.isPresent()) {
-            log.warn("No router is connected to network {}", osPort.networkId());
-            return null;
-        }
-
-        OpenstackRouter osRouter = openstackService.router(routerPort.get().deviceId());
-        if (osRouter == null) {
-            log.warn("Failed to get OpenStack router {}",
-                     routerPort.get().deviceId());
-            return null;
-        }
-
-        return osRouter.gatewayExternalInfo().externalFixedIps().values()
-                .stream().findAny().orElse(null);
-    }
-
-    private OpenstackPort getOpenstackPort(MacAddress srcMac) {
-        Optional<Host> host = hostService.getHostsByMac(srcMac).stream()
-                .filter(h -> h.annotations().value(PORT_ID) != null)
-                .findAny();
-        if (!host.isPresent()) {
-            log.warn("Failed to find a host with MAC:{}", srcMac);
-            return null;
-        }
-        return openstackService.port(host.get().annotations().value(PORT_ID));
-    }
-
-    private boolean checkSameSubnet(OpenstackPort osPortA, OpenstackPort osPortB) {
-        return osPortA.fixedIps().keySet().stream()
-                .anyMatch(subnetId -> osPortB.fixedIps().keySet().contains(subnetId));
-    }
-
-    private void populatePnatFlowRules(InboundPacket inboundPacket,
-                                      OpenstackPort osPort,
-                                      TpPort patPort,
-                                      Ip4Address externalIp) {
-        long vni = getVni(osPort.networkId());
-        populatePnatIncomingFlowRules(vni, externalIp, patPort, inboundPacket);
-        populatePnatOutgoingFlowRules(vni, externalIp, patPort, inboundPacket);
-    }
-
-    private long getVni(String netId) {
-        // TODO remove this and use host vxlan annotation if applicable
-        return Long.parseLong(openstackService.network(netId).segmentId());
-    }
-
-    private void populatePnatOutgoingFlowRules(long vni, Ip4Address externalIp, TpPort patPort,
-                                               InboundPacket inboundPacket) {
-        IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPProtocol(iPacket.getProtocol())
-                .matchTunnelId(vni)
-                .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
-                .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        switch (iPacket.getProtocol()) {
-            case IPv4.PROTOCOL_TCP:
-                TCP tcpPacket = (TCP) iPacket.getPayload();
-                sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
-                        .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
-                tBuilder.setTcpSrc(patPort)
-                        .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
-                break;
-            case IPv4.PROTOCOL_UDP:
-                UDP udpPacket = (UDP) iPacket.getPayload();
-                sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
-                        .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
-                tBuilder.setUdpSrc(patPort)
-                        .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
-
-                break;
-            default:
-                log.debug("Unsupported IPv4 protocol {}");
-                break;
-        }
-
-        tBuilder.setIpSrc(externalIp);
-        gatewayService.getGatewayNodes().forEach(gateway -> {
-            TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
-            tmpBuilder.setOutput(gatewayService.getUplinkPort(gateway.getGatewayDeviceId()));
-            ForwardingObjective fo = DefaultForwardingObjective.builder()
-                    .withSelector(sBuilder.build())
-                    .withTreatment(tmpBuilder.build())
-                    .withFlag(ForwardingObjective.Flag.VERSATILE)
-                    .withPriority(PNAT_RULE_PRIORITY)
-                    .makeTemporary(PNAT_TIMEOUT)
-                    .fromApp(appId)
-                    .add();
-
-            flowObjectiveService.forward(gateway.getGatewayDeviceId(), fo);
-        });
-    }
-
-    private void populatePnatIncomingFlowRules(long vni, Ip4Address externalIp, TpPort patPort,
-                                               InboundPacket inboundPacket) {
-        IPv4 iPacket = (IPv4) inboundPacket.parsed().getPayload();
-        IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPProtocol(iPacket.getProtocol())
-                .matchIPDst(IpPrefix.valueOf(externalIp, 32))
-                .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        tBuilder.setTunnelId(vni)
-                .setEthDst(inboundPacket.parsed().getSourceMAC())
-                .setIpDst(internalIp);
-
-        switch (iPacket.getProtocol()) {
-            case IPv4.PROTOCOL_TCP:
-                TCP tcpPacket = (TCP) iPacket.getPayload();
-                sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
-                        .matchTcpDst(patPort);
-                tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
-                break;
-            case IPv4.PROTOCOL_UDP:
-                UDP udpPacket = (UDP) iPacket.getPayload();
-                sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
-                        .matchUdpDst(patPort);
-                tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
-                break;
-            default:
-                break;
-        }
-
-        Optional<Host> srcVm = Tools.stream(hostService.getHostsByIp(internalIp))
-                .filter(host -> Objects.equals(
-                        host.annotations().value(VXLAN_ID),
-                        String.valueOf(vni)))
-                .findFirst();
-        if (!srcVm.isPresent()) {
-            log.warn("Failed to find source VM with IP {}", internalIp);
-            return;
-        }
-
-        gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
-            DeviceId srcDeviceId = srcVm.get().location().deviceId();
-            TrafficTreatment.Builder tmpBuilder = DefaultTrafficTreatment.builder(tBuilder.build());
-            tmpBuilder.extension(RulePopulatorUtil.buildExtension(
-                    deviceService,
-                    deviceId,
-                    nodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId)
-                    .setOutput(nodeService.tunnelPort(deviceId).get());
-
-            ForwardingObjective fo = DefaultForwardingObjective.builder()
-                    .withSelector(sBuilder.build())
-                    .withTreatment(tmpBuilder.build())
-                    .withFlag(ForwardingObjective.Flag.VERSATILE)
-                    .withPriority(PNAT_RULE_PRIORITY)
-                    .makeTemporary(PNAT_TIMEOUT)
-                    .fromApp(appId)
-                    .add();
-
-            flowObjectiveService.forward(deviceId, fo);
-        });
-    }
-
-    private class InternalPacketProcessor implements PacketProcessor {
-
-        @Override
-        public void process(PacketContext context) {
-            if (context.isHandled()) {
-                return;
-            } else if (!gatewayService.getGatewayDeviceIds().contains(
-                    context.inPacket().receivedFrom().deviceId())) {
-                // return if the packet is not from gateway nodes
-                return;
-            }
-
-            InboundPacket pkt = context.inPacket();
-            Ethernet ethernet = pkt.parsed();
-            if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
-                return;
-            }
-
-            IPv4 iPacket = (IPv4) ethernet.getPayload();
-            switch (iPacket.getProtocol()) {
-                case IPv4.PROTOCOL_ICMP:
-                    break;
-                case IPv4.PROTOCOL_UDP:
-                    UDP udpPacket = (UDP) iPacket.getPayload();
-                    if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
-                            udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
-                        // don't process DHCP
-                        break;
-                    }
-                default:
-                    eventExecutor.execute(() -> processPnatPacket(context, ethernet));
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java
new file mode 100644
index 0000000..0d74940
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRouterManager.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStore;
+import org.onosproject.openstacknetworking.api.OpenstackRouterStoreDelegate;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing OpenStack router and
+ * floating IP address.
+ */
+@Service
+@Component(immediate = true)
+public class OpenstackRouterManager
+        extends ListenerRegistry<OpenstackRouterEvent, OpenstackRouterListener>
+        implements OpenstackRouterAdminService, OpenstackRouterService {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String MSG_ROUTER = "OpenStack router %s %s";
+    private static final String MSG_ROUTER_IFACE = "OpenStack router interface %s %s";
+    private static final String MSG_FLOATING_IP = "OpenStack floating IP %s %s";
+    private static final String MSG_CREATED = "created";
+    private static final String MSG_UPDATED = "updated";
+    private static final String MSG_REMOVED = "removed";
+
+    private static final String ERR_NULL_ROUTER = "OpenStack router cannot be null";
+    private static final String ERR_NULL_ROUTER_ID = "OpenStack router ID cannot be null";
+    private static final String ERR_NULL_ROUTER_NAME = "OpenStack router name cannot be null";
+    private static final String ERR_NULL_IFACE = "OpenStack router interface cannot be null";
+    private static final String ERR_NULL_IFACE_ROUTER_ID = "OpenStack router interface router ID cannot be null";
+    private static final String ERR_NULL_IFACE_PORT_ID = "OpenStack router interface port ID cannot be null";
+    private static final String ERR_NULL_FLOATING = "OpenStack floating IP cannot be null";
+    private static final String ERR_NULL_FLOATING_ID = "OpenStack floating IP cannot be null";
+
+    private static final String ERR_IN_USE = " still in use";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackRouterStore osRouterStore;
+
+    private final OpenstackRouterStoreDelegate delegate = new InternalRouterStoreDelegate();
+
+    @Activate
+    protected void activate() {
+        coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+        osRouterStore.setDelegate(delegate);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osRouterStore.unsetDelegate(delegate);
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createRouter(Router osRouter) {
+        checkNotNull(osRouter, ERR_NULL_ROUTER);
+        checkArgument(!Strings.isNullOrEmpty(osRouter.getId()), ERR_NULL_ROUTER_ID);
+        checkArgument(!Strings.isNullOrEmpty(osRouter.getName()), ERR_NULL_ROUTER_NAME);
+
+        osRouterStore.createRouter(osRouter);
+        log.info(String.format(MSG_ROUTER, osRouter.getName(), MSG_CREATED));
+    }
+
+    @Override
+    public void updateRouter(Router osRouter) {
+        checkNotNull(osRouter, ERR_NULL_ROUTER);
+        checkArgument(!Strings.isNullOrEmpty(osRouter.getId()), ERR_NULL_ROUTER_ID);
+        checkArgument(!Strings.isNullOrEmpty(osRouter.getName()), ERR_NULL_ROUTER_NAME);
+
+        osRouterStore.updateRouter(osRouter);
+        log.info(String.format(MSG_ROUTER, osRouter.getId(), MSG_UPDATED));
+    }
+
+    @Override
+    public void removeRouter(String routerId) {
+        checkArgument(!Strings.isNullOrEmpty(routerId), ERR_NULL_ROUTER_ID);
+        synchronized (this) {
+            if (isRouterInUse(routerId)) {
+                final String error = String.format(MSG_ROUTER, routerId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            Router osRouter = osRouterStore.removeRouter(routerId);
+            if (osRouter != null) {
+                log.info(String.format(MSG_ROUTER, osRouter.getName(), MSG_REMOVED));
+            }
+        }
+    }
+
+    @Override
+    public void addRouterInterface(RouterInterface osIface) {
+        checkNotNull(osIface, ERR_NULL_IFACE);
+        checkArgument(!Strings.isNullOrEmpty(osIface.getId()), ERR_NULL_IFACE_ROUTER_ID);
+        checkArgument(!Strings.isNullOrEmpty(osIface.getPortId()), ERR_NULL_IFACE_PORT_ID);
+
+        osRouterStore.addRouterInterface(osIface);
+        log.info(String.format(MSG_ROUTER_IFACE, osIface.getPortId(), MSG_CREATED));
+    }
+
+    @Override
+    public void updateRouterInterface(RouterInterface osIface) {
+        checkNotNull(osIface, ERR_NULL_IFACE);
+        checkArgument(!Strings.isNullOrEmpty(osIface.getId()), ERR_NULL_IFACE_ROUTER_ID);
+        checkArgument(!Strings.isNullOrEmpty(osIface.getPortId()), ERR_NULL_IFACE_PORT_ID);
+
+        osRouterStore.updateRouterInterface(osIface);
+        log.info(String.format(MSG_ROUTER_IFACE, osIface.getPortId(), MSG_UPDATED));
+    }
+
+    @Override
+    public void removeRouterInterface(String osIfaceId) {
+        checkArgument(!Strings.isNullOrEmpty(osIfaceId), ERR_NULL_IFACE_PORT_ID);
+        synchronized (this) {
+            if (isRouterIfaceInUse(osIfaceId)) {
+                final String error = String.format(MSG_ROUTER, osIfaceId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            RouterInterface osIface = osRouterStore.removeRouterInterface(osIfaceId);
+            if (osIface != null) {
+                log.info(String.format(MSG_ROUTER_IFACE, osIface.getPortId(), MSG_REMOVED));
+            }
+        }
+    }
+
+    @Override
+    public void createFloatingIp(NetFloatingIP osFloatingIp) {
+        checkNotNull(osFloatingIp, ERR_NULL_FLOATING);
+        checkArgument(!Strings.isNullOrEmpty(osFloatingIp.getId()), ERR_NULL_FLOATING_ID);
+
+        osRouterStore.createFloatingIp(osFloatingIp);
+        log.info(String.format(MSG_FLOATING_IP, osFloatingIp.getId(), MSG_CREATED));
+    }
+
+    @Override
+    public void updateFloatingIp(NetFloatingIP osFloatingIp) {
+        checkNotNull(osFloatingIp, ERR_NULL_FLOATING);
+        checkArgument(!Strings.isNullOrEmpty(osFloatingIp.getId()), ERR_NULL_FLOATING_ID);
+
+        osRouterStore.updateFloatingIp(osFloatingIp);
+        log.info(String.format(MSG_FLOATING_IP, osFloatingIp.getId(), MSG_UPDATED));
+    }
+
+    @Override
+    public void removeFloatingIp(String floatingIpId) {
+        checkArgument(!Strings.isNullOrEmpty(floatingIpId), ERR_NULL_FLOATING_ID);
+        synchronized (this) {
+            if (isFloatingIpInUse(floatingIpId)) {
+                final String error = String.format(MSG_FLOATING_IP, floatingIpId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            NetFloatingIP osFloatingIp = osRouterStore.removeFloatingIp(floatingIpId);
+            if (osFloatingIp != null) {
+                log.info(String.format(MSG_FLOATING_IP, osFloatingIp.getId(), MSG_REMOVED));
+            }
+        }
+    }
+
+    @Override
+    public Router router(String routerId) {
+        checkArgument(!Strings.isNullOrEmpty(routerId), ERR_NULL_ROUTER_ID);
+        return osRouterStore.router(routerId);
+    }
+
+    @Override
+    public Set<Router> routers() {
+        return osRouterStore.routers();
+    }
+
+    @Override
+    public RouterInterface routerInterface(String osIfaceId) {
+        checkArgument(!Strings.isNullOrEmpty(osIfaceId), ERR_NULL_IFACE_PORT_ID);
+        return osRouterStore.routerInterface(osIfaceId);
+    }
+
+    @Override
+    public Set<RouterInterface> routerInterfaces() {
+        return osRouterStore.routerInterfaces();
+    }
+
+    @Override
+    public Set<RouterInterface> routerInterfaces(String routerId) {
+        Set<RouterInterface> osIfaces = osRouterStore.routerInterfaces().stream()
+                .filter(iface -> Objects.equals(iface.getId(), routerId))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osIfaces);
+    }
+
+    @Override
+    public NetFloatingIP floatingIp(String floatingIpId) {
+        checkArgument(!Strings.isNullOrEmpty(floatingIpId), ERR_NULL_FLOATING_ID);
+        return osRouterStore.floatingIp(floatingIpId);
+    }
+
+    @Override
+    public Set<NetFloatingIP> floatingIps() {
+        return osRouterStore.floatingIps();
+    }
+
+    @Override
+    public Set<NetFloatingIP> floatingIps(String routerId) {
+        Set<NetFloatingIP> osFloatingIps = osRouterStore.floatingIps().stream()
+                .filter(fip -> Objects.equals(fip.getRouterId(), routerId))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osFloatingIps);
+    }
+
+    private boolean isRouterInUse(String routerId) {
+        // TODO add checking
+        return false;
+    }
+
+    private boolean isRouterIfaceInUse(String osIfaceId) {
+        // TODO add checking
+        return false;
+    }
+
+    private boolean isFloatingIpInUse(String floatingIpId) {
+        // TODO add checking
+        return false;
+    }
+
+    private class InternalRouterStoreDelegate implements OpenstackRouterStoreDelegate {
+
+        @Override
+        public void notify(OpenstackRouterEvent event) {
+            if (event != null) {
+                log.trace("send oepnstack routing event {}", event);
+                process(event);
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 0bf93dc..03810c3 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -32,39 +32,41 @@
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.scalablegateway.api.ScalableGatewayService;
 import org.onosproject.openstacknetworking.api.Constants;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
+import java.util.Objects;
 import java.util.concurrent.ExecutorService;
 
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.DEVICE_OWNER_FLOATING_IP;
-import static org.onosproject.openstacknetworking.api.Constants.DEVICE_OWNER_ROUTER_GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Handle ARP, ICMP and NAT packets from gateway nodes.
+ * Handle ARP requests from gateway nodes.
  */
 @Component(immediate = true)
 public class OpenstackRoutingArpHandler {
+
     private final Logger log = getLogger(getClass());
 
+    private static final String DEVICE_OWNER_ROUTER_GW = "network:router_gateway";
+    private static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ScalableGatewayService gatewayService;
 
-    private final ExecutorService executorService =
-            newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "packet-event", log));
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
 
     private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
 
@@ -77,21 +79,25 @@
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
+        eventExecutor.shutdown();
         log.info("Stopped");
     }
 
     private void processArpPacket(PacketContext context, Ethernet ethernet) {
         ARP arp = (ARP) ethernet.getPayload();
-        log.trace("arpEvent called from {} to {}",
-                Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
-                Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
-
         if (arp.getOpCode() != ARP.OP_REQUEST) {
             return;
         }
 
+        if (log.isTraceEnabled()) {
+            log.trace("ARP request received from {} for {}",
+                    Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
+                    Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
+        }
+
         IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
-        if (getTargetMacForTargetIp(targetIp.getIp4Address()) == MacAddress.NONE) {
+        if (!isServiceIp(targetIp.getIp4Address())) {
+            log.trace("Unknown target ARP request for {}, ignore it", targetIp);
             return;
         }
 
@@ -107,6 +113,8 @@
                 context.inPacket().receivedFrom().deviceId(),
                 treatment,
                 ByteBuffer.wrap(ethReply.serialize())));
+
+        context.block();
     }
 
     private class InternalPacketProcessor implements PacketProcessor {
@@ -125,19 +133,20 @@
             Ethernet ethernet = pkt.parsed();
             if (ethernet != null &&
                     ethernet.getEtherType() == Ethernet.TYPE_ARP) {
-                executorService.execute(() -> processArpPacket(context, ethernet));
+                eventExecutor.execute(() -> processArpPacket(context, ethernet));
             }
         }
     }
 
-    // TODO make a cache for the MAC, not a good idea to REST call every time it gets ARP request
-    private MacAddress getTargetMacForTargetIp(Ip4Address targetIp) {
-        OpenstackPort port = openstackService.ports().stream()
-                .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_GATEWAY) ||
-                        p.deviceOwner().equals(DEVICE_OWNER_FLOATING_IP))
-                .filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address()))
-                .findAny().orElse(null);
-
-        return port == null ? MacAddress.NONE : port.macAddress();
+    private boolean isServiceIp(IpAddress targetIp) {
+        // FIXME use floating IP and external gateway information of router instead
+        // once openstack4j fixed
+        return osNetworkService.ports().stream()
+                .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
+                        DEVICE_OWNER_ROUTER_GW) ||
+                        Objects.equals(osPort.getDeviceOwner(),
+                                DEVICE_OWNER_FLOATING_IP))
+                .flatMap(osPort -> osPort.getFixedIps().stream())
+                .anyMatch(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(targetIp));
     }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
new file mode 100644
index 0000000..8594aea
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+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.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+
+/**
+ * Handles OpenStack floating IP events.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingFloatingIpHandler {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackRouterService osRouterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ScalableGatewayService gatewayService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+    private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpLisener();
+    private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
+
+    private ApplicationId appId;
+    private NodeId localNodeId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        localNodeId = clusterService.getLocalNode().id();
+        leadershipService.runForLeadership(appId.name());
+        osRouterService.addListener(floatingIpLisener);
+        osNodeService.addListener(osNodeListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osNodeService.removeListener(osNodeListener);
+        osRouterService.removeListener(floatingIpLisener);
+        leadershipService.withdraw(appId.name());
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    private void floatingIpUpdated(NetFloatingIP floatingIp, String portId) {
+        Port osPort = osNetworkService.port(portId);
+        if (osPort == null) {
+            final String error = String.format(ERR_FLOW + "no port(%s) exists",
+                    floatingIp.getFloatingIpAddress(),
+                    floatingIp.getPortId());
+            throw new IllegalStateException(error);
+        }
+
+        if (Strings.isNullOrEmpty(floatingIp.getPortId())) {
+            setFloatingIpRules(floatingIp, osPort, false);
+            log.info("Disassociated floating IP:{} from fixed IP:{}",
+                    floatingIp.getFloatingIpAddress(),
+                    osPort.getFixedIps());
+        } else {
+            setFloatingIpRules(floatingIp, osPort, true);
+            log.info("Associated floating IP:{} to fixed IP:{}",
+                    floatingIp.getFloatingIpAddress(),
+                    floatingIp.getFixedIpAddress());
+        }
+    }
+
+    private void setFloatingIpRules(NetFloatingIP floatingIp, Port osPort,
+                                    boolean install) {
+        Network osNet = osNetworkService.network(osPort.getNetworkId());
+        if (osNet == null) {
+            final String error = String.format(ERR_FLOW + "no network(%s) exists",
+                    floatingIp.getFloatingIpAddress(),
+                    osPort.getNetworkId());
+            throw new IllegalStateException(error);
+        }
+
+        MacAddress srcMac = MacAddress.valueOf(osPort.getMacAddress());
+        InstancePort instPort = instancePortService.instancePort(srcMac);
+        if (instPort == null) {
+            final String error = String.format(ERR_FLOW + "no host(MAC:%s) found",
+                    floatingIp.getFloatingIpAddress(), srcMac);
+            throw new IllegalStateException(error);
+        }
+
+        setDownstreamRules(floatingIp, osNet, instPort, install);
+        setUpstreamRules(floatingIp, osNet, instPort, install);
+    }
+
+    private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
+                                    InstancePort instPort, boolean install) {
+        Optional<IpAddress> dataIp = osNodeService.dataIp(instPort.deviceId());
+        if (!dataIp.isPresent()) {
+            log.warn(ERR_FLOW + "compute node {} is not ready",
+                    floatingIp, instPort.deviceId());
+            return;
+        }
+
+        IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
+        TrafficSelector externalSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(floating.toIpPrefix())
+                .build();
+
+        gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
+            TrafficTreatment externalTreatment = DefaultTrafficTreatment.builder()
+                    .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
+                    .setEthDst(instPort.macAddress())
+                    .setIpDst(instPort.ipAddress().getIp4Address())
+                    .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
+                    .extension(buildExtension(
+                            deviceService,
+                            gnodeId,
+                            dataIp.get().getIp4Address()),
+                            gnodeId)
+                    .setOutput(osNodeService.tunnelPort(gnodeId).get())
+                    .build();
+
+            RulePopulatorUtil.setRule(
+                    flowObjectiveService,
+                    appId,
+                    gnodeId,
+                    externalSelector,
+                    externalTreatment,
+                    ForwardingObjective.Flag.VERSATILE,
+                    PRIORITY_FLOATING_EXTERNAL,
+                    install);
+
+            // access from one VM to the other via floating IP
+            TrafficSelector internalSelector = DefaultTrafficSelector.builder()
+                    .matchEthType(Ethernet.TYPE_IPV4)
+                    .matchIPDst(floating.toIpPrefix())
+                    .matchInPort(osNodeService.tunnelPort(gnodeId).get())
+                    .build();
+
+            TrafficTreatment internalTreatment = DefaultTrafficTreatment.builder()
+                    .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
+                    .setEthDst(instPort.macAddress())
+                    .setIpDst(instPort.ipAddress().getIp4Address())
+                    .setTunnelId(Long.valueOf(osNet.getProviderSegID()))
+                    .extension(buildExtension(
+                            deviceService,
+                            gnodeId,
+                            dataIp.get().getIp4Address()),
+                            gnodeId)
+                    .setOutput(PortNumber.IN_PORT)
+                    .build();
+
+            RulePopulatorUtil.setRule(
+                    flowObjectiveService,
+                    appId,
+                    gnodeId,
+                    internalSelector,
+                    internalTreatment,
+                    ForwardingObjective.Flag.VERSATILE,
+                    PRIORITY_FLOATING_INTERNAL,
+                    install);
+        });
+    }
+
+    private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
+                                  InstancePort instPort, boolean install) {
+        IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(Long.valueOf(osNet.getProviderSegID()))
+                .matchIPSrc(instPort.ipAddress().toIpPrefix())
+                .build();
+
+        gatewayService.getGatewayDeviceIds().forEach(gnodeId -> {
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setIpSrc(floating.getIp4Address())
+                    .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
+                    .setEthDst(Constants.DEFAULT_EXTERNAL_ROUTER_MAC)
+                    .setOutput(gatewayService.getUplinkPort(gnodeId))
+                    .build();
+
+            RulePopulatorUtil.setRule(
+                    flowObjectiveService,
+                    appId,
+                    gnodeId,
+                    selector,
+                    treatment,
+                    ForwardingObjective.Flag.VERSATILE,
+                    PRIORITY_FLOATING_EXTERNAL,
+                    install);
+        });
+    }
+
+    private class InternalFloatingIpLisener implements OpenstackRouterListener {
+
+        @Override
+        public boolean isRelevant(OpenstackRouterEvent event) {
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            if (!Objects.equals(localNodeId, leader)) {
+                return false;
+            }
+            return event.floatingIp() != null;
+        }
+
+        @Override
+        public void event(OpenstackRouterEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_FLOATING_IP_ASSOCIATED:
+                case OPENSTACK_FLOATING_IP_DISASSOCIATED:
+                    eventExecutor.execute(() -> {
+                        NetFloatingIP fip = event.floatingIp();
+                        log.debug("Floating IP {} is updated", fip.getFloatingIpAddress());
+                        floatingIpUpdated(fip, event.portId());
+                    });
+                    break;
+                case OPENSTACK_FLOATING_IP_CREATED:
+                    log.debug("Floating IP {} is created",
+                            event.floatingIp().getFloatingIpAddress());
+                    break;
+                case OPENSTACK_FLOATING_IP_UPDATED:
+                    log.debug("Floating IP {} is updated",
+                            event.floatingIp().getFloatingIpAddress());
+                    break;
+                case OPENSTACK_FLOATING_IP_REMOVED:
+                    log.debug("Floating IP {} is removed",
+                            event.floatingIp().getFloatingIpAddress());
+                    break;
+                case OPENSTACK_ROUTER_CREATED:
+                case OPENSTACK_ROUTER_UPDATED:
+                case OPENSTACK_ROUTER_REMOVED:
+                case OPENSTACK_ROUTER_INTERFACE_ADDED:
+                case OPENSTACK_ROUTER_INTERFACE_UPDATED:
+                case OPENSTACK_ROUTER_INTERFACE_REMOVED:
+                default:
+                    // do nothing for the other events
+                    break;
+            }
+        }
+    }
+
+    private class InternalNodeListener implements OpenstackNodeListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNodeEvent event) {
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            if (!Objects.equals(localNodeId, leader)) {
+                return false;
+            }
+            return event.subject().type() == GATEWAY;
+        }
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNode osNode = event.subject();
+
+            switch (event.type()) {
+                case COMPLETE:
+                    eventExecutor.execute(() -> {
+                        log.info("GATEWAY node {} detected", osNode.hostname());
+                        osRouterService.floatingIps().stream()
+                                .filter(fip -> !Strings.isNullOrEmpty(fip.getPortId()))
+                                .forEach(fip -> {
+                                    floatingIpUpdated(fip, fip.getPortId());
+                                });
+                    });
+                    break;
+                case INIT:
+                case DEVICE_CREATED:
+                case INCOMPLETE:
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
new file mode 100644
index 0000000..7d722ec
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+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.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
+import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
+
+/**
+ * Handles OpenStack router events.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingHandler {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String MSG_ENABLED = "Enabled ";
+    private static final String MSG_DISABLED = "Disabled ";
+    private static final String ERR_SET_FLOWS = "Failed to set flows for router %s:";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ScalableGatewayService gatewayService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackRouterService osRouterService;
+
+    private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+    private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
+    private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
+
+    private ApplicationId appId;
+    private NodeId localNodeId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        localNodeId = clusterService.getLocalNode().id();
+        leadershipService.runForLeadership(appId.name());
+        osNodeService.addListener(osNodeListener);
+        osRouterService.addListener(osRouterListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osRouterService.removeListener(osRouterListener);
+        osNodeService.removeListener(osNodeListener);
+        leadershipService.withdraw(appId.name());
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    private void routerUpdated(Router osRouter) {
+        ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
+        if (exGateway == null) {
+            osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
+                setSourceNat(iface, false);
+            });
+        } else {
+            osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
+                setSourceNat(iface, exGateway.isEnableSnat());
+            });
+        }
+    }
+
+    private void routerIfaceAdded(Router osRouter, RouterInterface osRouterIface) {
+        Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
+        if (osSubnet == null) {
+            final String error = String.format(
+                    ERR_SET_FLOWS + "subnet %s does not exist",
+                    osRouterIface.getId(),
+                    osRouterIface.getSubnetId());
+            throw new IllegalStateException(error);
+        }
+
+        setInternalRoutes(osRouter, osSubnet, true);
+        setGatewayIcmp(osSubnet, true);
+        ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
+        if (exGateway != null && exGateway.isEnableSnat()) {
+            setSourceNat(osRouterIface, true);
+        }
+
+        log.info("Connected subnet({}) to {}", osSubnet.getCidr(), osRouter.getName());
+    }
+
+    private void routerIfaceRemoved(Router osRouter, RouterInterface osRouterIface) {
+        Subnet osSubnet = osNetworkService.subnet(osRouterIface.getSubnetId());
+        if (osSubnet == null) {
+            final String error = String.format(
+                    ERR_SET_FLOWS + "subnet %s does not exist",
+                    osRouterIface.getId(),
+                    osRouterIface.getSubnetId());
+            throw new IllegalStateException(error);
+        }
+
+        setInternalRoutes(osRouter, osSubnet, false);
+        setGatewayIcmp(osSubnet, false);
+        ExternalGateway exGateway = osRouter.getExternalGatewayInfo();
+        if (exGateway != null && exGateway.isEnableSnat()) {
+            setSourceNat(osRouterIface, false);
+        }
+
+        log.info("Disconnected subnet({}) from {}", osSubnet.getCidr(), osRouter.getName());
+    }
+
+    private void setSourceNat(RouterInterface routerIface, boolean install) {
+        Subnet osSubnet = osNetworkService.subnet(routerIface.getSubnetId());
+        Network osNet = osNetworkService.network(osSubnet.getNetworkId());
+
+        osNodeService.completeNodes().stream()
+                .filter(osNode -> osNode.type() == COMPUTE)
+                .forEach(osNode -> {
+                    setRulesToGateway(
+                            osNode.intBridge(),
+                            gatewayService.getGatewayGroupId(osNode.intBridge()),
+                            Long.valueOf(osNet.getProviderSegID()),
+                            IpPrefix.valueOf(osSubnet.getCidr()),
+                            install);
+                });
+
+        // take the first outgoing packet to controller for source NAT
+        gatewayService.getGatewayDeviceIds()
+                .forEach(gwDeviceId -> setRulesToController(
+                        gwDeviceId,
+                        Long.valueOf(osNet.getProviderSegID()),
+                        IpPrefix.valueOf(osSubnet.getCidr()),
+                        install));
+
+        final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
+        log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
+    }
+
+    private void setGatewayIcmp(Subnet osSubnet, boolean install) {
+        if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+            // do nothing if no gateway is set
+            return;
+        }
+
+        // take ICMP request to a subnet gateway through gateway node group
+        Network network = osNetworkService.network(osSubnet.getNetworkId());
+        osNodeService.completeNodes().stream()
+                .filter(osNode -> osNode.type() == COMPUTE)
+                .forEach(osNode -> setRulesToGatewayWithDstIp(
+                        osNode.intBridge(),
+                        gatewayService.getGatewayGroupId(osNode.intBridge()),
+                        Long.valueOf(network.getProviderSegID()),
+                        IpAddress.valueOf(osSubnet.getGateway()),
+                        install));
+
+        IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+        gatewayService.getGatewayDeviceIds()
+                .forEach(gwDeviceId -> setGatewayIcmpRule(
+                        gatewayIp,
+                        gwDeviceId,
+                        install
+                ));
+
+        final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
+        log.debug(updateStr + "ICMP to {}", osSubnet.getGateway());
+    }
+
+    private void setInternalRoutes(Router osRouter, Subnet updatedSubnet, boolean install) {
+        Set<Subnet> routableSubnets = routableSubnets(osRouter, updatedSubnet.getId());
+        Long updatedVni = getVni(updatedSubnet);
+
+        // installs rule from/to my subnet intentionally to fix ICMP failure
+        // to my subnet gateway if no external gateway added to the router
+        osNodeService.completeNodes().stream()
+                .filter(osNode -> osNode.type() == COMPUTE)
+                .forEach(osNode -> {
+                    setInternalRouterRules(
+                            osNode.intBridge(),
+                            updatedVni,
+                            updatedVni,
+                            IpPrefix.valueOf(updatedSubnet.getCidr()),
+                            IpPrefix.valueOf(updatedSubnet.getCidr()),
+                            install
+                    );
+
+                    routableSubnets.forEach(subnet -> {
+                        setInternalRouterRules(
+                                osNode.intBridge(),
+                                updatedVni,
+                                getVni(subnet),
+                                IpPrefix.valueOf(updatedSubnet.getCidr()),
+                                IpPrefix.valueOf(subnet.getCidr()),
+                                install
+                        );
+                        setInternalRouterRules(
+                                osNode.intBridge(),
+                                getVni(subnet),
+                                updatedVni,
+                                IpPrefix.valueOf(subnet.getCidr()),
+                                IpPrefix.valueOf(updatedSubnet.getCidr()),
+                                install
+                        );
+                    });
+                });
+
+        final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
+        routableSubnets.forEach(subnet -> log.debug(
+                updateStr + "route between subnet:{} and subnet:{}",
+                subnet.getCidr(),
+                updatedSubnet.getCidr()));
+    }
+
+    private Set<Subnet> routableSubnets(Router osRouter, String osSubnetId) {
+        Set<Subnet> osSubnets = osRouterService.routerInterfaces(osRouter.getId())
+                .stream()
+                .filter(iface -> !Objects.equals(iface.getSubnetId(), osSubnetId))
+                .map(iface -> osNetworkService.subnet(iface.getSubnetId()))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osSubnets);
+    }
+
+    private Long getVni(Subnet osSubnet) {
+        return Long.parseLong(osNetworkService.network(
+                osSubnet.getNetworkId()).getProviderSegID());
+    }
+
+    private void setGatewayIcmpRule(IpAddress gatewayIp, DeviceId deviceId, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+                .matchIPDst(gatewayIp.toIpPrefix())
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(PortNumber.CONTROLLER)
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                deviceId,
+                selector,
+                treatment,
+                ForwardingObjective.Flag.VERSATILE,
+                PRIORITY_ICMP_RULE,
+                install);
+    }
+
+    private void setInternalRouterRules(DeviceId deviceId, Long srcVni, Long dstVni,
+                                        IpPrefix srcSubnet, IpPrefix dstSubnet, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(srcVni)
+                .matchIPSrc(srcSubnet)
+                .matchIPDst(dstSubnet)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setTunnelId(dstVni)
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                deviceId,
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_INTERNAL_ROUTING_RULE,
+                install);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(dstVni)
+                .matchIPSrc(srcSubnet)
+                .matchIPDst(dstSubnet)
+                .build();
+
+        treatment = DefaultTrafficTreatment.builder()
+                .setTunnelId(dstVni)
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                deviceId,
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_INTERNAL_ROUTING_RULE,
+                install);
+    }
+
+    private void setRulesToGateway(DeviceId deviceId, GroupId groupId, Long vni,
+                                   IpPrefix srcSubnet, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(vni)
+                .matchIPSrc(srcSubnet)
+                .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .group(groupId)
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                deviceId,
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_EXTERNAL_ROUTING_RULE,
+                install);
+    }
+
+    private void setRulesToController(DeviceId deviceId, Long vni, IpPrefix srcSubnet, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(vni)
+                .matchIPSrc(srcSubnet)
+                .matchEthDst(Constants.DEFAULT_GATEWAY_MAC)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(PortNumber.CONTROLLER)
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                deviceId,
+                selector,
+                treatment,
+                ForwardingObjective.Flag.VERSATILE,
+                PRIORITY_EXTERNAL_ROUTING_RULE,
+                install);
+    }
+
+    private void setRulesToGatewayWithDstIp(DeviceId deviceId, GroupId groupId, Long vni,
+                                            IpAddress dstIp, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(vni)
+                .matchIPDst(dstIp.toIpPrefix())
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .group(groupId)
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                deviceId,
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_SWITCHING_RULE,
+                install);
+    }
+
+    private class InternalRouterEventListener implements OpenstackRouterListener {
+
+        @Override
+        public boolean isRelevant(OpenstackRouterEvent event) {
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNodeId, leader);
+        }
+
+        // FIXME only one leader in the cluster should process
+        @Override
+        public void event(OpenstackRouterEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_ROUTER_CREATED:
+                    log.debug("Router(name:{}, ID:{}) is created",
+                            event.subject().getName(),
+                            event.subject().getId());
+                    eventExecutor.execute(() -> routerUpdated(event.subject()));
+                    break;
+                case OPENSTACK_ROUTER_UPDATED:
+                    log.debug("Router(name:{}, ID:{}) is updated",
+                            event.subject().getName(),
+                            event.subject().getId());
+                    eventExecutor.execute(() -> routerUpdated(event.subject()));
+                    break;
+                case OPENSTACK_ROUTER_REMOVED:
+                    log.debug("Router(name:{}, ID:{}) is removed",
+                            event.subject().getName(),
+                            event.subject().getId());
+                    break;
+                case OPENSTACK_ROUTER_INTERFACE_ADDED:
+                    log.debug("Router interface {} added to router {}",
+                            event.routerIface().getPortId(),
+                            event.routerIface().getId());
+                    eventExecutor.execute(() -> routerIfaceAdded(
+                            event.subject(),
+                            event.routerIface()));
+                    break;
+                case OPENSTACK_ROUTER_INTERFACE_UPDATED:
+                    log.debug("Router interface {} on {} updated",
+                            event.routerIface().getPortId(),
+                            event.routerIface().getId());
+                    break;
+                case OPENSTACK_ROUTER_INTERFACE_REMOVED:
+                    log.debug("Router interface {} removed from router {}",
+                            event.routerIface().getPortId(),
+                            event.routerIface().getId());
+                    eventExecutor.execute(() -> routerIfaceRemoved(
+                            event.subject(),
+                            event.routerIface()));
+                    break;
+                case OPENSTACK_ROUTER_GATEWAY_ADDED:
+                case OPENSTACK_ROUTER_GATEWAY_REMOVED:
+                case OPENSTACK_FLOATING_IP_CREATED:
+                case OPENSTACK_FLOATING_IP_UPDATED:
+                case OPENSTACK_FLOATING_IP_REMOVED:
+                case OPENSTACK_FLOATING_IP_ASSOCIATED:
+                case OPENSTACK_FLOATING_IP_DISASSOCIATED:
+                default:
+                    // do nothing for the other events
+                    break;
+            }
+        }
+    }
+
+    private class InternalNodeEventListener 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);
+        }
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNode osNode = event.subject();
+
+            switch (event.type()) {
+                case COMPLETE:
+                case INCOMPLETE:
+                    eventExecutor.execute(() -> {
+                        log.info("COMPLETE node {} detected", osNode.hostname());
+                        reconfigureRouters();
+                    });
+                    break;
+                case INIT:
+                case DEVICE_CREATED:
+                default:
+                    break;
+            }
+        }
+
+        private void reconfigureRouters() {
+            osRouterService.routers().forEach(osRouter -> {
+                routerUpdated(osRouter);
+                osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> {
+                    routerIfaceAdded(osRouter, iface);
+                });
+            });
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
new file mode 100644
index 0000000..2cafcd6
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+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.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.GatewayNode;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Handles ICMP packet received from a gateway node.
+ * For a request for virtual network subnet gateway, it generates fake ICMP reply.
+ * For a request for the external network, it does source NAT with the public IP and
+ * forward the request to the external only if the requested virtual subnet has
+ * external connectivity.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingIcmpHandler {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String ERR_REQ = "Failed to handle ICMP request: ";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ScalableGatewayService gatewayService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackRouterService osRouterService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+    private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+    private final InternalNodeListener nodeListener = new InternalNodeListener();
+    private final Map<String, InstancePort> icmpInfoMap = Maps.newHashMap();
+
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
+        osNodeService.addListener(nodeListener);
+        requestPacket(appId);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        packetService.removeProcessor(packetProcessor);
+        osNodeService.removeListener(nodeListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    private void requestPacket(ApplicationId appId) {
+        TrafficSelector icmpSelector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_ICMP)
+                .build();
+
+        gatewayService.getGatewayDeviceIds().forEach(gateway -> {
+            packetService.requestPackets(
+                    icmpSelector,
+                    PacketPriority.CONTROL,
+                    appId,
+                    Optional.of(gateway));
+            log.debug("Requested ICMP packet to {}", gateway);
+        });
+    }
+
+    private void processIcmpPacket(PacketContext context, Ethernet ethernet) {
+        IPv4 ipPacket = (IPv4) ethernet.getPayload();
+        ICMP icmp = (ICMP) ipPacket.getPayload();
+        log.trace("Processing ICMP packet source MAC:{}, source IP:{}," +
+                        "dest MAC:{}, dest IP:{}",
+                ethernet.getSourceMAC(),
+                IpAddress.valueOf(ipPacket.getSourceAddress()),
+                ethernet.getDestinationMAC(),
+                IpAddress.valueOf(ipPacket.getDestinationAddress()));
+
+        switch (icmp.getIcmpType()) {
+            case ICMP.TYPE_ECHO_REQUEST:
+                handleEchoRequest(
+                        context.inPacket().receivedFrom().deviceId(),
+                        ethernet.getSourceMAC(),
+                        ipPacket,
+                        icmp);
+                context.block();
+                break;
+            case ICMP.TYPE_ECHO_REPLY:
+                handleEchoReply(ipPacket, icmp);
+                context.block();
+                break;
+            default:
+                break;
+        }
+    }
+
+    private void handleEchoRequest(DeviceId srcDevice, MacAddress srcMac, IPv4 ipPacket,
+                                   ICMP icmp) {
+        InstancePort instPort = instancePortService.instancePort(srcMac);
+        if (instPort == null) {
+            log.trace(ERR_REQ + "unknown source host(MAC:{})", srcMac);
+            return;
+        }
+
+        IpAddress srcIp = IpAddress.valueOf(ipPacket.getSourceAddress());
+        Subnet srcSubnet = getSourceSubnet(instPort, srcIp);
+        if (srcSubnet == null) {
+            log.trace(ERR_REQ + "unknown source subnet(IP:{})", srcIp);
+            return;
+        }
+        if (Strings.isNullOrEmpty(srcSubnet.getGateway())) {
+            log.trace(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no gateway",
+                    srcSubnet.getId(), srcSubnet.getCidr());
+            return;
+        }
+
+        if (isForSubnetGateway(IpAddress.valueOf(ipPacket.getDestinationAddress()),
+                srcSubnet)) {
+            // this is a request for the subnet gateway
+            processRequestForGateway(ipPacket, instPort);
+        } else {
+            // this is a request for the external network
+            IpAddress externalIp = getExternalIp(srcSubnet);
+            if (externalIp == null) {
+                return;
+            }
+            log.debug("1");
+            sendRequestForExternal(ipPacket, srcDevice, externalIp);
+            log.debug("2");
+            String icmpInfoKey = String.valueOf(getIcmpId(icmp))
+                    .concat(String.valueOf(externalIp.getIp4Address().toInt()))
+                    .concat(String.valueOf(ipPacket.getDestinationAddress()));
+            icmpInfoMap.putIfAbsent(icmpInfoKey, instPort);
+        }
+    }
+
+    private void handleEchoReply(IPv4 ipPacket, ICMP icmp) {
+        String icmpInfoKey = String.valueOf(getIcmpId(icmp))
+                .concat(String.valueOf(ipPacket.getDestinationAddress()))
+                .concat(String.valueOf(ipPacket.getSourceAddress()));
+
+        processReplyFromExternal(ipPacket, icmpInfoMap.get(icmpInfoKey));
+        icmpInfoMap.remove(icmpInfoKey);
+    }
+
+    private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
+        Port osPort = osNetworkService.port(instance.portId());
+        IP fixedIp = osPort.getFixedIps().stream()
+                .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
+                .findAny().orElse(null);
+        if (fixedIp == null) {
+            return null;
+        }
+        return osNetworkService.subnet(fixedIp.getSubnetId());
+    }
+
+    private boolean isForSubnetGateway(IpAddress dstIp, Subnet srcSubnet) {
+        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+                .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
+                .findAny().orElse(null);
+        if (osRouterIface == null) {
+            log.trace(ERR_REQ + "source subnet(ID:{}, CIDR:{}) has no router",
+                    srcSubnet.getId(), srcSubnet.getCidr());
+            return false;
+        }
+
+        Router osRouter = osRouterService.router(osRouterIface.getId());
+        Set<IpAddress> routableGateways = osRouterService.routerInterfaces(osRouter.getId())
+                .stream()
+                .map(iface -> osNetworkService.subnet(iface.getSubnetId()).getGateway())
+                .map(IpAddress::valueOf)
+                .collect(Collectors.toSet());
+
+        return routableGateways.contains(dstIp);
+    }
+
+    private IpAddress getExternalIp(Subnet srcSubnet) {
+        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+                .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
+                .findAny().orElse(null);
+        if (osRouterIface == null) {
+            final String error = String.format(ERR_REQ +
+                    "subnet(ID:%s, CIDR:%s) is not connected to any router",
+                    srcSubnet.getId(), srcSubnet.getCidr());
+            throw new IllegalStateException(error);
+        }
+
+        Router osRouter = osRouterService.router(osRouterIface.getId());
+        if (osRouter.getExternalGatewayInfo() == null) {
+            final String error = String.format(ERR_REQ +
+                    "router(ID:%s, name:%s) does not have external gateway",
+                    osRouter.getId(), osRouter.getName());
+            throw new IllegalStateException(error);
+        }
+
+        // TODO fix openstack4j for ExternalGateway provides external fixed IP list
+        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
+        Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
+                .stream()
+                .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
+                .findAny().orElse(null);
+        if (exGatewayPort == null) {
+            final String error = String.format(ERR_REQ +
+                    "no external gateway port for router (ID:%s, name:%s)",
+                    osRouter.getId(), osRouter.getName());
+            throw new IllegalStateException(error);
+        }
+
+        return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
+                .findFirst().get().getIpAddress());
+    }
+
+    private void processRequestForGateway(IPv4 ipPacket, InstancePort instPort) {
+        ICMP icmpReq = (ICMP) ipPacket.getPayload();
+        icmpReq.setChecksum((short) 0);
+        icmpReq.setIcmpType(ICMP.TYPE_ECHO_REPLY).resetChecksum();
+
+        int destinationAddress = ipPacket.getSourceAddress();
+
+        ipPacket.setSourceAddress(ipPacket.getDestinationAddress())
+                .setDestinationAddress(destinationAddress)
+                .resetChecksum();
+
+        ipPacket.setPayload(icmpReq);
+        Ethernet icmpReply = new Ethernet();
+        icmpReply.setEtherType(Ethernet.TYPE_IPV4)
+                .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
+                .setDestinationMACAddress(instPort.macAddress())
+                .setPayload(ipPacket);
+
+        sendReply(icmpReply, instPort);
+    }
+
+    private void sendRequestForExternal(IPv4 ipPacket, DeviceId srcDevice, IpAddress srcNatIp) {
+        ICMP icmpReq = (ICMP) ipPacket.getPayload();
+        icmpReq.resetChecksum();
+        ipPacket.setSourceAddress(srcNatIp.getIp4Address().toInt()).resetChecksum();
+        ipPacket.setPayload(icmpReq);
+
+        Ethernet icmpRequestEth = new Ethernet();
+        icmpRequestEth.setEtherType(Ethernet.TYPE_IPV4)
+                .setSourceMACAddress(DEFAULT_GATEWAY_MAC)
+                .setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC)
+                .setPayload(ipPacket);
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(gatewayService.getUplinkPort(srcDevice))
+                .build();
+
+        OutboundPacket packet = new DefaultOutboundPacket(
+                srcDevice,
+                treatment,
+                ByteBuffer.wrap(icmpRequestEth.serialize()));
+
+        packetService.emit(packet);
+    }
+
+    private void processReplyFromExternal(IPv4 ipPacket, InstancePort instPort) {
+        ICMP icmpReply = (ICMP) ipPacket.getPayload();
+        icmpReply.resetChecksum();
+
+        ipPacket.setDestinationAddress(instPort.ipAddress().getIp4Address().toInt())
+                .resetChecksum();
+        ipPacket.setPayload(icmpReply);
+
+        Ethernet icmpResponseEth = new Ethernet();
+        icmpResponseEth.setEtherType(Ethernet.TYPE_IPV4)
+                .setSourceMACAddress(Constants.DEFAULT_GATEWAY_MAC)
+                .setDestinationMACAddress(instPort.macAddress())
+                .setPayload(ipPacket);
+
+        sendReply(icmpResponseEth, instPort);
+    }
+
+    private void sendReply(Ethernet icmpReply, InstancePort instPort) {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(instPort.portNumber())
+                .build();
+
+        OutboundPacket packet = new DefaultOutboundPacket(
+                instPort.deviceId(),
+                treatment,
+                ByteBuffer.wrap(icmpReply.serialize()));
+
+        packetService.emit(packet);
+    }
+
+    private short getIcmpId(ICMP icmp) {
+        return ByteBuffer.wrap(icmp.serialize(), 4, 2).getShort();
+    }
+
+    private class InternalPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            if (context.isHandled()) {
+                return;
+            } else if (!gatewayService.getGatewayDeviceIds().contains(
+                    context.inPacket().receivedFrom().deviceId())) {
+                // return if the packet is not from gateway nodes
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            Ethernet ethernet = pkt.parsed();
+            if (ethernet == null || ethernet.getEtherType() == Ethernet.TYPE_ARP) {
+                return;
+            }
+
+            IPv4 iPacket = (IPv4) ethernet.getPayload();
+            if (iPacket.getProtocol() == IPv4.PROTOCOL_ICMP) {
+                eventExecutor.execute(() -> processIcmpPacket(context, ethernet));
+            }
+        }
+    }
+
+    private class InternalNodeListener implements OpenstackNodeListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNodeEvent event) {
+            // do not proceed without mastership
+            OpenstackNode osNode = event.subject();
+            return mastershipService.isLocalMaster(osNode.intBridge());
+        }
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNode osNode = event.subject();
+
+            switch (event.type()) {
+                case COMPLETE:
+                    if (osNode.type() == GATEWAY) {
+                        log.info("GATEWAY node {} detected", osNode.hostname());
+                        eventExecutor.execute(() -> {
+                            // TODO add events to scalable gateway so that we
+                            // don't need to add gateway here and there
+                            GatewayNode gnode = GatewayNode.builder()
+                                    .gatewayDeviceId(osNode.intBridge())
+                                    .dataIpAddress(osNode.dataIp().getIp4Address())
+                                    .uplinkIntf(osNode.externalPortName().get())
+                                    .build();
+                            gatewayService.addGatewayNode(gnode);
+                            requestPacket(appId);
+                        });
+                    }
+                    break;
+                case INIT:
+                case DEVICE_CREATED:
+                case INCOMPLETE:
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java
deleted file mode 100644
index 2482333..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingManager.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.impl;
-
-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.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.core.GroupId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackNetwork;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackRouterInterface;
-import org.onosproject.openstackinterface.OpenstackSubnet;
-import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.onosproject.scalablegateway.api.GatewayNode;
-import org.onosproject.scalablegateway.api.ScalableGatewayService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
-
-@Component(immediate = true)
-@Service
-public class OpenstackRoutingManager extends AbstractVmHandler implements OpenstackRoutingService {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService nodeService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ScalableGatewayService gatewayService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
-            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
-    private final InternalNodeListener nodeListener = new InternalNodeListener();
-
-    private ApplicationId appId;
-
-    @Activate
-    protected void activate() {
-        super.activate();
-        appId = coreService.registerApplication(ROUTING_APP_ID);
-        nodeService.addListener(nodeListener);
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        super.deactivate();
-        nodeService.removeListener(nodeListener);
-        log.info("stopped");
-    }
-
-    @Override
-    protected void hostDetected(Host host) {
-        // Installs forwarding flow rules to VMs in different nodes and different subnets
-        // that are connected via a router
-        Optional<OpenstackRouter> routerOfTheHost = getRouter(host);
-
-        if (!routerOfTheHost.isPresent()) {
-            return;
-        }
-
-        routableSubNets(routerOfTheHost.get().id()).stream()
-                .filter(subnet -> !subnet.id().equals(host.annotations().value(SUBNET_ID)))
-                .forEach(subnet -> setForwardingRulesAmongHostsInDifferentCnodes(host, getHosts(subnet), true));
-    }
-
-    @Override
-    protected void hostRemoved(Host host) {
-        // Removes forwarding flow rules to VMs in different nodes and different subnets
-        // that are connected via a router
-        Optional<OpenstackRouter> routerOfTheHost = getRouter(host);
-        if (!routerOfTheHost.isPresent()) {
-            return;
-        }
-
-        routableSubNets(routerOfTheHost.get().id()).stream()
-                .filter(subnet -> !subnet.id().equals(host.annotations().value(SUBNET_ID)))
-                .forEach(subnet -> setForwardingRulesAmongHostsInDifferentCnodes(host, getHosts(subnet), false));
-    }
-
-    @Override
-    public void createRouter(OpenstackRouter osRouter) {
-    }
-
-    @Override
-    public void updateRouter(OpenstackRouter osRouter) {
-        if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
-            routableSubNets(osRouter.id()).stream()
-                    .forEach(subnet -> setExternalConnection(osRouter, subnet, true));
-
-            log.info("Connected external gateway {} to router {}",
-                     osRouter.gatewayExternalInfo().externalFixedIps(),
-                     osRouter.name());
-        } else {
-            routableSubNets(osRouter.id()).stream()
-                    .forEach(subnet -> setExternalConnection(osRouter, subnet, false));
-
-            log.info("Disconnected external gateway from router {}",
-                     osRouter.name());
-        }
-    }
-
-    @Override
-    public void removeRouter(String osRouterId) {
-        // Nothing to do
-        // All interfaces need to be removed before the router is removed,
-        // and all related flow rues are removed when the interfaces are removed.
-    }
-
-    @Override
-    public void addRouterInterface(OpenstackRouterInterface routerIfaceAdded) {
-        OpenstackRouter osRouter = openstackRouter(routerIfaceAdded.id());
-        OpenstackSubnet osSubnetAdded = openstackService.subnet(routerIfaceAdded.subnetId());
-        if (osRouter == null || osSubnetAdded == null) {
-            log.warn("Failed to add router interface {}", routerIfaceAdded);
-            return;
-        }
-        handleRouterInterfaces(osRouter, osSubnetAdded);
-    }
-
-    @Override
-    public void removeRouterInterface(OpenstackRouterInterface routerIface) {
-        OpenstackRouter osRouter = openstackService.router(routerIface.id());
-        OpenstackSubnet osSubnetRemoved = openstackService.subnet(routerIface.subnetId());
-        if (osRouter == null) {
-            log.warn("Failed to remove router interface {}", routerIface);
-            return;
-        }
-        handleRouterInterfacesRemoved(osRouter, osSubnetRemoved);
-
-        log.info("Disconnected {} from router {}", osSubnetRemoved.cidr(), osRouter.name());
-    }
-
-    private void handleRouterInterfaces(OpenstackRouter osRouter, OpenstackSubnet osSubnetAdded) {
-        OpenstackNetwork osNetworkAdded = openstackService.network(osSubnetAdded.networkId());
-        if (osNetworkAdded == null) {  // in case of external network subnet
-            return;
-        }
-
-        // Sets flow rules for routing among subnets connected to a router.
-        setRoutesAmongSubnets(osRouter, osSubnetAdded, true);
-
-        // Sets flow rules for forwarding "packets going to external networks" to gateway nodes.
-        if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
-            setExternalConnection(osRouter, osSubnetAdded, true);
-        }
-
-        // Sets flow rules to handle ping to the virtual gateway.
-        Ip4Address vGatewayIp = Ip4Address.valueOf(osSubnetAdded.gatewayIp());
-        gatewayService.getGatewayDeviceIds()
-                .forEach(deviceId -> setGatewayIcmpRule(vGatewayIp, deviceId, true));
-
-        // Sets east-west routing rules for VMs in different Cnode to Switching Table.
-        setForwardingRulesForEastWestRouting(osRouter, osSubnetAdded, true);
-
-    }
-
-    private void handleRouterInterfacesRemoved(OpenstackRouter osRouter, OpenstackSubnet osSubnetRemoved) {
-
-        // Removes flow rules for routing among subnets connected to a router.
-        setRoutesAmongSubnets(osRouter, osSubnetRemoved, false);
-
-        // Removes flow rules for forwarding "packets going to external networks" to gateway nodes.
-        if (osRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
-            setExternalConnection(osRouter, osSubnetRemoved, false);
-        }
-
-        // Removes flow rules to handle ping to the virtual gateway.
-        Ip4Address vGatewayIp = Ip4Address.valueOf(osSubnetRemoved.gatewayIp());
-        gatewayService.getGatewayDeviceIds()
-                .forEach(deviceId -> setGatewayIcmpRule(vGatewayIp, deviceId, false));
-
-        // Removes east-west routing rules for VMs in different Cnode to Switching Table.
-        setForwardingRulesForEastWestRouting(osRouter, osSubnetRemoved, false);
-
-        // Resets east-west routing rules for VMs in different Cnode to Switching Table.
-        routableSubNets(osRouter.id()).stream()
-                .forEach(subnet -> setForwardingRulesForEastWestRouting(osRouter, subnet, true));
-    }
-
-    private void setRoutesAmongSubnets(OpenstackRouter osRouter, OpenstackSubnet osSubnetAdded, boolean install) {
-        Set<OpenstackSubnet> routableSubNets = routableSubNets(osRouter.id());
-        if (routableSubNets.size() < 2) {
-            // no other subnet interface is connected to this router, do nothing
-            return;
-        }
-
-        Map<String, String> vniMap = new HashMap<>();
-        openstackService.networks().forEach(n -> vniMap.put(n.id(), n.segmentId()));
-
-        routableSubNets.stream()
-                .filter(subnet -> !subnet.id().equals(osSubnetAdded.id()))
-                .filter(subnet -> vniMap.get(subnet.networkId()) != null)
-                .forEach(subnet -> nodeService.completeNodes().stream()
-                        .filter(node -> node.type().equals(COMPUTE))
-                        .forEach(node -> {
-                                setRoutingRules(node.intBridge(),
-                                        Integer.parseInt(vniMap.get(subnet.networkId())),
-                                        Integer.parseInt(vniMap.get(osSubnetAdded.networkId())),
-                                        subnet, osSubnetAdded, install);
-                                setRoutingRules(node.intBridge(),
-                                        Integer.parseInt(vniMap.get(osSubnetAdded.networkId())),
-                                        Integer.parseInt(vniMap.get(subnet.networkId())),
-                                        osSubnetAdded, subnet, install);
-                                }
-                        ));
-    }
-
-    private void setRoutingRules(DeviceId deviceId, int srcVni, int dstVni,
-                                 OpenstackSubnet subnetSrc, OpenstackSubnet subnetDst, boolean install) {
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(srcVni)
-                .matchIPSrc(IpPrefix.valueOf(subnetSrc.cidr()))
-                .matchIPDst(IpPrefix.valueOf(subnetDst.cidr()));
-
-        tBuilder.setTunnelId(dstVni);
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
-                tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, EW_ROUTING_RULE_PRIORITY, install);
-
-        // Flow rules for destination is in different subnet and different node,
-        // because VNI is converted to destination VNI in the source VM node.
-        sBuilder = DefaultTrafficSelector.builder();
-        tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(dstVni)
-                .matchIPSrc(IpPrefix.valueOf(subnetSrc.cidr()))
-                .matchIPDst(IpPrefix.valueOf(subnetDst.cidr()));
-
-        tBuilder.setTunnelId(dstVni);
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
-                tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, EW_ROUTING_RULE_PRIORITY, install);
-    }
-
-    private void setExternalConnection(OpenstackRouter osRouter, OpenstackSubnet osSubNet, boolean install) {
-        if (!osRouter.gatewayExternalInfo().isEnablePnat()) {
-            log.debug("Source NAT is disabled");
-            return;
-        }
-
-        //OpenstackSubnet osSubNet = openstackService.subnet(osSubNetId);
-        OpenstackNetwork osNet = openstackService.network(osSubNet.networkId());
-
-        nodeService.completeNodes().stream()
-                .filter(node -> node.type().equals(COMPUTE))
-                .forEach(node -> setRulesToGateway(
-                        node.intBridge(),
-                        gatewayService.getGatewayGroupId(node.intBridge()),
-                        Long.valueOf(osNet.segmentId()), osSubNet.cidr(), install));
-
-        // Is this for PNAT ??
-        setRulesForGatewayToController(Long.valueOf(osNet.segmentId()), osSubNet.cidr(), install);
-    }
-
-    private void setRulesToGateway(DeviceId deviceId, GroupId groupId, long vni, String cidr, boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(vni)
-                .matchIPSrc(IpPrefix.valueOf(cidr))
-                .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
-
-        tBuilder.group(groupId);
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
-                tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, ROUTING_RULE_PRIORITY, install);
-    }
-
-    private void setRulesForGatewayToController(long vni, String subNetCidr, boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(vni)
-                .matchIPSrc(IpPrefix.valueOf(subNetCidr))
-                .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
-        tBuilder.setOutput(PortNumber.CONTROLLER);
-
-        gatewayService.getGatewayDeviceIds().forEach(deviceId ->
-                RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
-                        tBuilder.build(), ForwardingObjective.Flag.VERSATILE, ROUTING_RULE_PRIORITY, install));
-    }
-
-    private void setGatewayIcmpRule(Ip4Address gatewayIp, DeviceId deviceId, boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPProtocol(IPv4.PROTOCOL_ICMP)
-                .matchIPDst(gatewayIp.toIpPrefix());
-
-        tBuilder.setOutput(PortNumber.CONTROLLER);
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
-                tBuilder.build(), ForwardingObjective.Flag.VERSATILE, GATEWAY_ICMP_PRIORITY, install);
-    }
-
-    private void setForwardingRulesForEastWestRouting(OpenstackRouter router, OpenstackSubnet subnetAdded,
-                                                      boolean install) {
-
-        Set<OpenstackSubnet> subnets = routableSubNets(router.id());
-
-        Set<Host> hosts = Tools.stream(hostService.getHosts())
-                .filter(h -> getVni(h).equals(openstackService.network(subnetAdded.networkId()).segmentId()))
-                .collect(Collectors.toSet());
-
-        subnets.stream()
-                .filter(subnet -> !subnet.id().equals(subnetAdded.id()))
-                .forEach(subnet -> getHosts(subnet)
-                        .forEach(h -> setForwardingRulesAmongHostsInDifferentCnodes(h, hosts, install)));
-    }
-
-    private void setForwardingRulesAmongHostsInDifferentCnodes(Host host, Set<Host> remoteHosts, boolean install) {
-        Ip4Address localVmIp = getIp(host);
-        DeviceId localDeviceId = host.location().deviceId();
-        Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
-
-        if (!localDataIp.isPresent()) {
-            log.debug("Failed to get data IP for device {}",
-                    host.location().deviceId());
-            return;
-        }
-
-        remoteHosts.stream()
-                .filter(remoteHost -> !host.location().deviceId().equals(remoteHost.location().deviceId()))
-                .forEach(remoteVm -> {
-                    Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
-                    if (remoteDataIp.isPresent()) {
-                        setVxLanFlowRule(getVni(remoteVm),
-                                localDeviceId,
-                                remoteDataIp.get().getIp4Address(),
-                                getIp(remoteVm), install);
-
-                        setVxLanFlowRule(getVni(host),
-                                remoteVm.location().deviceId(),
-                                localDataIp.get().getIp4Address(),
-                                localVmIp, install);
-                    }
-                });
-    }
-
-    private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
-                                  Ip4Address vmIp, boolean install) {
-        Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
-        if (!tunnelPort.isPresent()) {
-            log.warn("Failed to get tunnel port from {}", deviceId);
-            return;
-        }
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(Long.parseLong(vni))
-                .matchIPDst(vmIp.toIpPrefix());
-        tBuilder.extension(buildExtension(deviceService, deviceId, remoteIp), deviceId)
-                .setOutput(tunnelPort.get());
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId, sBuilder.build(),
-                tBuilder.build(), ForwardingObjective.Flag.SPECIFIC, SWITCHING_RULE_PRIORITY, install);
-    }
-
-
-    private OpenstackRouter openstackRouter(String routerId) {
-        return openstackService.routers().stream().filter(r ->
-                r.id().equals(routerId)).iterator().next();
-    }
-
-    @Override
-    public void reinstallVmFlow(Host host) {
-        // TODO: implements later
-    }
-
-    @Override
-    public void purgeVmFlow(Host host) {
-        // TODO: implements later
-    }
-
-    private class InternalNodeListener implements OpenstackNodeListener {
-
-        @Override
-        public void event(OpenstackNodeEvent event) {
-            OpenstackNode node = event.node();
-
-            switch (event.type()) {
-                case COMPLETE:
-                case INCOMPLETE:
-                    log.info("COMPLETE node {} detected", node.hostname());
-                    eventExecutor.execute(() -> {
-                        if (node.type() == GATEWAY) {
-                            GatewayNode gnode = GatewayNode.builder()
-                                    .gatewayDeviceId(node.intBridge())
-                                    .dataIpAddress(node.dataIp().getIp4Address())
-                                    .uplinkIntf(node.externalPortName().get())
-                                    .build();
-                            gatewayService.addGatewayNode(gnode);
-                        }
-                    });
-                    openstackService.routers().stream()
-                            .forEach(router -> routableSubNets(router.id()).stream()
-                                        .forEach(subnet -> handleRouterInterfaces(router, subnet)));
-                    break;
-                case INIT:
-                case DEVICE_CREATED:
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
new file mode 100644
index 0000000..5b6868b
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -0,0 +1,463 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+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.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TCP;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.UDP;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackRouterService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.scalablegateway.api.ScalableGatewayService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
+import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handle packets needs SNAT.
+ */
+@Component(immediate = true)
+public class OpenstackRoutingSnatHandler {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String ERR_PACKETIN = "Failed to handle packet in: ";
+    private static final int TIME_OUT_SNAT_RULE = 120;
+    private static final int TIME_OUT_SNAT_PORT = 1200 * 1000;
+    private static final int TP_PORT_MINIMUM_NUM = 1024;
+    private static final int TP_PORT_MAXIMUM_NUM = 65535;
+
+    private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API);
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackRouterService osRouterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ScalableGatewayService gatewayService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+    private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+
+    private ConsistentMap<Integer, String> tpPortNumMap;
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
+                .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
+                .withName("openstackrouting-tpportnum")
+                .withApplicationId(appId)
+                .build();
+
+        packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        packetService.removeProcessor(packetProcessor);
+        eventExecutor.shutdown();
+        log.info("Stopped");
+    }
+
+    private void processSnatPacket(PacketContext context, Ethernet eth) {
+        IPv4 iPacket = (IPv4) eth.getPayload();
+        InboundPacket packetIn = context.inPacket();
+
+        int patPort = getPortNum(eth.getSourceMAC(),
+                iPacket.getDestinationAddress());
+
+        InstancePort srcInstPort = instancePortService.instancePort(eth.getSourceMAC());
+        if (srcInstPort == null) {
+            log.trace(ERR_PACKETIN + "source host(MAC:{}) does not exist",
+                    eth.getSourceMAC());
+            return;
+        }
+        IpAddress srcIp = IpAddress.valueOf(iPacket.getSourceAddress());
+        Subnet srcSubnet = getSourceSubnet(srcInstPort, srcIp);
+        IpAddress externalGatewayIp = getExternalIp(srcSubnet);
+        if (externalGatewayIp == null) {
+            return;
+        }
+
+        populateSnatFlowRules(context.inPacket(),
+                srcInstPort,
+                TpPort.tpPort(patPort),
+                externalGatewayIp);
+
+        packetOut((Ethernet) eth.clone(),
+                packetIn.receivedFrom().deviceId(),
+                patPort,
+                externalGatewayIp);
+    }
+
+    private Subnet getSourceSubnet(InstancePort instance, IpAddress srcIp) {
+        Port osPort = osNetworkService.port(instance.portId());
+        IP fixedIp = osPort.getFixedIps().stream()
+                .filter(ip -> IpAddress.valueOf(ip.getIpAddress()).equals(srcIp))
+                .findAny().orElse(null);
+        if (fixedIp == null) {
+            return null;
+        }
+        return osNetworkService.subnet(fixedIp.getSubnetId());
+    }
+
+    private IpAddress getExternalIp(Subnet srcSubnet) {
+        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+                .filter(i -> Objects.equals(i.getSubnetId(), srcSubnet.getId()))
+                .findAny().orElse(null);
+        if (osRouterIface == null) {
+            // this subnet is not connected to the router
+            log.trace(ERR_PACKETIN + "source subnet(ID:{}, CIDR:{}) has no router",
+                    srcSubnet.getId(), srcSubnet.getCidr());
+            return null;
+        }
+
+        Router osRouter = osRouterService.router(osRouterIface.getId());
+        if (osRouter.getExternalGatewayInfo() == null) {
+            // this router does not have external connectivity
+            log.trace(ERR_PACKETIN + "router({}) has no external gateway",
+                    osRouter.getName());
+            return null;
+        }
+
+        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
+        if (!exGatewayInfo.isEnableSnat()) {
+            // SNAT is disabled in this router
+            log.trace(ERR_PACKETIN + "router({}) SNAT is disabled", osRouter.getName());
+            return null;
+        }
+
+        // TODO fix openstack4j for ExternalGateway provides external fixed IP list
+        Port exGatewayPort = osNetworkService.ports(exGatewayInfo.getNetworkId())
+                .stream()
+                .filter(port -> Objects.equals(port.getDeviceId(), osRouter.getId()))
+                .findAny().orElse(null);
+        if (exGatewayPort == null) {
+            log.trace(ERR_PACKETIN + "no external gateway port for router({})",
+                    osRouter.getName());
+            return null;
+        }
+
+        return IpAddress.valueOf(exGatewayPort.getFixedIps().stream()
+                .findFirst().get().getIpAddress());
+    }
+
+    private void populateSnatFlowRules(InboundPacket packetIn, InstancePort srcInstPort,
+                                       TpPort patPort, IpAddress externalIp) {
+        Network osNet = osNetworkService.network(srcInstPort.networkId());
+        if (osNet == null) {
+            final String error = String.format(ERR_PACKETIN + "network %s not found",
+                    srcInstPort.networkId());
+            throw new IllegalStateException(error);
+        }
+
+        setDownstreamRules(srcInstPort,
+                Long.parseLong(osNet.getProviderSegID()),
+                externalIp,
+                patPort,
+                packetIn);
+
+        setUpstreamRules(Long.parseLong(osNet.getProviderSegID()),
+                externalIp,
+                patPort,
+                packetIn);
+    }
+
+    private void setDownstreamRules(InstancePort srcInstPort, Long srcVni,
+                                    IpAddress externalIp, TpPort patPort,
+                                    InboundPacket packetIn) {
+        IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
+        IpAddress internalIp = IpAddress.valueOf(iPacket.getSourceAddress());
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(iPacket.getProtocol())
+                .matchIPDst(IpPrefix.valueOf(externalIp, 32))
+                .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setTunnelId(srcVni)
+                .setEthDst(packetIn.parsed().getSourceMAC())
+                .setIpDst(internalIp);
+
+        switch (iPacket.getProtocol()) {
+            case IPv4.PROTOCOL_TCP:
+                TCP tcpPacket = (TCP) iPacket.getPayload();
+                sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getDestinationPort()))
+                        .matchTcpDst(patPort);
+                tBuilder.setTcpDst(TpPort.tpPort(tcpPacket.getSourcePort()));
+                break;
+            case IPv4.PROTOCOL_UDP:
+                UDP udpPacket = (UDP) iPacket.getPayload();
+                sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getDestinationPort()))
+                        .matchUdpDst(patPort);
+                tBuilder.setUdpDst(TpPort.tpPort(udpPacket.getSourcePort()));
+                break;
+            default:
+                break;
+        }
+
+        gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
+            DeviceId srcDeviceId = srcInstPort.deviceId();
+            TrafficTreatment.Builder tmpBuilder =
+                    DefaultTrafficTreatment.builder(tBuilder.build());
+            tmpBuilder.extension(RulePopulatorUtil.buildExtension(
+                    deviceService,
+                    deviceId,
+                    osNodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId)
+                    .setOutput(osNodeService.tunnelPort(deviceId).get());
+
+            ForwardingObjective fo = DefaultForwardingObjective.builder()
+                    .withSelector(sBuilder.build())
+                    .withTreatment(tmpBuilder.build())
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(PRIORITY_SNAT_RULE)
+                    .makeTemporary(TIME_OUT_SNAT_RULE)
+                    .fromApp(appId)
+                    .add();
+
+            flowObjectiveService.forward(deviceId, fo);
+        });
+    }
+
+    private void setUpstreamRules(Long srcVni, IpAddress externalIp, TpPort patPort,
+                                  InboundPacket packetIn) {
+        IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(iPacket.getProtocol())
+                .matchTunnelId(srcVni)
+                .matchIPSrc(IpPrefix.valueOf(iPacket.getSourceAddress(), 32))
+                .matchIPDst(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+        switch (iPacket.getProtocol()) {
+            case IPv4.PROTOCOL_TCP:
+                TCP tcpPacket = (TCP) iPacket.getPayload();
+                sBuilder.matchTcpSrc(TpPort.tpPort(tcpPacket.getSourcePort()))
+                        .matchTcpDst(TpPort.tpPort(tcpPacket.getDestinationPort()));
+                tBuilder.setTcpSrc(patPort)
+                        .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
+                break;
+            case IPv4.PROTOCOL_UDP:
+                UDP udpPacket = (UDP) iPacket.getPayload();
+                sBuilder.matchUdpSrc(TpPort.tpPort(udpPacket.getSourcePort()))
+                        .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
+                tBuilder.setUdpSrc(patPort)
+                        .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
+
+                break;
+            default:
+                log.debug("Unsupported IPv4 protocol {}");
+                break;
+        }
+
+        tBuilder.setIpSrc(externalIp);
+        gatewayService.getGatewayDeviceIds().forEach(deviceId -> {
+            TrafficTreatment.Builder tmpBuilder =
+                    DefaultTrafficTreatment.builder(tBuilder.build());
+            tmpBuilder.setOutput(gatewayService.getUplinkPort(deviceId));
+            ForwardingObjective fo = DefaultForwardingObjective.builder()
+                    .withSelector(sBuilder.build())
+                    .withTreatment(tmpBuilder.build())
+                    .withFlag(ForwardingObjective.Flag.VERSATILE)
+                    .withPriority(PRIORITY_SNAT_RULE)
+                    .makeTemporary(TIME_OUT_SNAT_RULE)
+                    .fromApp(appId)
+                    .add();
+
+            flowObjectiveService.forward(deviceId, fo);
+        });
+    }
+
+    private void packetOut(Ethernet ethPacketIn, DeviceId srcDevice, int patPort,
+                           IpAddress externalIp) {
+        IPv4 iPacket = (IPv4) ethPacketIn.getPayload();
+
+        switch (iPacket.getProtocol()) {
+            case IPv4.PROTOCOL_TCP:
+                TCP tcpPacket = (TCP) iPacket.getPayload();
+                tcpPacket.setSourcePort(patPort);
+                tcpPacket.resetChecksum();
+                tcpPacket.setParent(iPacket);
+                iPacket.setPayload(tcpPacket);
+                break;
+            case IPv4.PROTOCOL_UDP:
+                UDP udpPacket = (UDP) iPacket.getPayload();
+                udpPacket.setSourcePort(patPort);
+                udpPacket.resetChecksum();
+                udpPacket.setParent(iPacket);
+                iPacket.setPayload(udpPacket);
+                break;
+            default:
+                log.trace("Temporally, this method can process UDP and TCP protocol.");
+                return;
+        }
+
+        iPacket.setSourceAddress(externalIp.toString());
+        iPacket.resetChecksum();
+        iPacket.setParent(ethPacketIn);
+        ethPacketIn.setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC);
+        ethPacketIn.setPayload(iPacket);
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(gatewayService.getUplinkPort(srcDevice))
+                .build();
+        ethPacketIn.resetChecksum();
+        packetService.emit(new DefaultOutboundPacket(
+                srcDevice,
+                treatment,
+                ByteBuffer.wrap(ethPacketIn.serialize())));
+    }
+
+    private int getPortNum(MacAddress sourceMac, int destinationAddress) {
+        int portNum = findUnusedPortNum();
+        if (portNum == 0) {
+            clearPortNumMap();
+            portNum = findUnusedPortNum();
+        }
+        tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
+        return portNum;
+    }
+
+    private int findUnusedPortNum() {
+        for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
+            if (!tpPortNumMap.containsKey(i)) {
+                return i;
+            }
+        }
+        return 0;
+    }
+
+    private void clearPortNumMap() {
+        tpPortNumMap.entrySet().forEach(e -> {
+            if (System.currentTimeMillis() - e.getValue().creationTime() > TIME_OUT_SNAT_PORT) {
+                tpPortNumMap.remove(e.getKey());
+            }
+        });
+    }
+
+    private class InternalPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            if (context.isHandled()) {
+                return;
+            } else if (!gatewayService.getGatewayDeviceIds().contains(
+                    context.inPacket().receivedFrom().deviceId())) {
+                // return if the packet is not from gateway nodes
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            Ethernet eth = pkt.parsed();
+            if (eth == null || eth.getEtherType() == Ethernet.TYPE_ARP) {
+                return;
+            }
+
+            IPv4 iPacket = (IPv4) eth.getPayload();
+            switch (iPacket.getProtocol()) {
+                case IPv4.PROTOCOL_ICMP:
+                    break;
+                case IPv4.PROTOCOL_UDP:
+                    UDP udpPacket = (UDP) iPacket.getPayload();
+                    if (udpPacket.getDestinationPort() == UDP.DHCP_SERVER_PORT &&
+                            udpPacket.getSourcePort() == UDP.DHCP_CLIENT_PORT) {
+                        // don't process DHCP
+                        break;
+                    }
+                default:
+                    eventExecutor.execute(() -> processSnatPacket(context, eth));
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
deleted file mode 100644
index 3f6f480..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSecurityGroupManager.java
+++ /dev/null
@@ -1,425 +0,0 @@
-/*
-* Copyright 2016-present Open Networking Laboratory
-*
-* 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.openstacknetworking.impl;
-
-import com.google.common.collect.Maps;
-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.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.TpPort;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.OpenstackSecurityGroup;
-import org.onosproject.openstackinterface.OpenstackSecurityGroupRule;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-/**
- * Populates flows rules for Security Groups of VMs.
- *
- */
-@Component(immediate = true)
-@Service
-public class OpenstackSecurityGroupManager extends AbstractVmHandler
-        implements OpenstackSecurityGroupService {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
-
-    private static final String PROTO_ICMP = "ICMP";
-    private static final String PROTO_TCP = "TCP";
-    private static final String PROTO_UDP = "UDP";
-    private static final String ETHTYPE_IPV4 = "IPV4";
-
-    private final Map<Host, Set<SecurityGroupRule>> securityGroupRuleMap = Maps.newConcurrentMap();
-    private ApplicationId appId;
-
-    @Activate
-    protected void activate() {
-        super.activate();
-        appId = coreService.registerApplication(SWITCHING_APP_ID);
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        super.deactivate();
-    }
-
-    @Override
-    public void updateSecurityGroup(OpenstackPort osPort) {
-        if (!osPort.status().equals(OpenstackPort.PortStatus.ACTIVE)) {
-            return;
-        }
-
-        Optional<Host> host = getVmByPortId(osPort.id());
-        if (!host.isPresent()) {
-            log.debug("No host found with {}", osPort.id());
-            return;
-        }
-        eventExecutor.execute(() -> updateSecurityGroupRules(host.get(), true));
-    }
-
-    /**
-     * Populates security group rules for all VMs in the supplied tenant ID.
-     * VMs in the same tenant tend to be engaged to each other by sharing the
-     * same security groups or setting the remote to another security group.
-     * To make the implementation simpler and robust, it tries to reinstall
-     * security group rules for all the VMs in the same tenant whenever a new
-     * VM is detected or port is updated.
-     *
-     * @param tenantId tenant id to update security group rules
-     */
-    private void populateSecurityGroupRules(String tenantId, boolean install) {
-        securityGroupRuleMap.entrySet().stream()
-                .filter(entry -> getTenantId(entry.getKey()).equals(tenantId))
-                .forEach(entry -> {
-                    Host local = entry.getKey();
-                    entry.getValue().forEach(sgRule -> {
-                        setSecurityGroupRule(local.location().deviceId(),
-                                sgRule.rule(),
-                                getIp(local),
-                                sgRule.remoteIp(), install);
-                    });
-                });
-        log.debug("Updated security group rules for {}", tenantId);
-    }
-
-    private void setSecurityGroupRule(DeviceId deviceId, OpenstackSecurityGroupRule sgRule,
-                                      Ip4Address vmIp, IpPrefix remoteIp,
-                                      boolean install) {
-        ForwardingObjective.Builder foBuilder = buildFlowObjective(sgRule, vmIp, remoteIp);
-        if (foBuilder == null) {
-            return;
-        }
-
-        if (install) {
-            flowObjectiveService.forward(deviceId, foBuilder.add());
-        } else {
-            flowObjectiveService.forward(deviceId, foBuilder.remove());
-        }
-    }
-
-    private ForwardingObjective.Builder buildFlowObjective(OpenstackSecurityGroupRule sgRule,
-                                                           Ip4Address vmIp,
-                                                           IpPrefix remoteIp) {
-        if (remoteIp != null && remoteIp.equals(IpPrefix.valueOf(vmIp, 32))) {
-            // do nothing if the remote IP is my IP
-            return null;
-        }
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        buildMatchs(sBuilder, sgRule, vmIp, remoteIp);
-
-        return DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(DefaultTrafficTreatment.builder().build())
-                .withPriority(ACL_RULE_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .fromApp(appId);
-    }
-
-    private void buildMatchs(TrafficSelector.Builder sBuilder, OpenstackSecurityGroupRule sgRule,
-                             Ip4Address vmIp, IpPrefix remoteIp) {
-        buildMatchEthType(sBuilder, sgRule.ethertype());
-        buildMatchDirection(sBuilder, sgRule.direction(), vmIp);
-        buildMatchProto(sBuilder, sgRule.protocol());
-        buildMatchPort(sBuilder, sgRule.protocol(), sgRule.direction(),
-                sgRule.portRangeMax(), sgRule.portRangeMin());
-        buildMatchRemoteIp(sBuilder, remoteIp, sgRule.direction());
-    }
-
-    private void buildMatchDirection(TrafficSelector.Builder sBuilder,
-                                     OpenstackSecurityGroupRule.Direction direction,
-                                     Ip4Address vmIp) {
-        if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
-            sBuilder.matchIPSrc(IpPrefix.valueOf(vmIp, 32));
-        } else {
-            sBuilder.matchIPDst(IpPrefix.valueOf(vmIp, 32));
-        }
-    }
-
-    private void buildMatchEthType(TrafficSelector.Builder sBuilder, String etherType) {
-        // Either IpSrc or IpDst (or both) is set by default, and we need to set EthType as IPv4.
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4);
-        if (etherType != null && !Objects.equals(etherType, "null") &&
-                !etherType.toUpperCase().equals(ETHTYPE_IPV4)) {
-            log.debug("EthType {} is not supported yet in Security Group", etherType);
-        }
-    }
-
-    private void buildMatchRemoteIp(TrafficSelector.Builder sBuilder, IpPrefix remoteIpPrefix,
-                                    OpenstackSecurityGroupRule.Direction direction) {
-        if (remoteIpPrefix != null && !remoteIpPrefix.getIp4Prefix().equals(IP_PREFIX_ANY)) {
-            if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
-                sBuilder.matchIPDst(remoteIpPrefix);
-            } else {
-                sBuilder.matchIPSrc(remoteIpPrefix);
-            }
-        }
-    }
-
-    private void buildMatchProto(TrafficSelector.Builder sBuilder, String protocol) {
-        if (protocol != null) {
-            switch (protocol.toUpperCase()) {
-                case PROTO_ICMP:
-                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_ICMP);
-                    break;
-                case PROTO_TCP:
-                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_TCP);
-                    break;
-                case PROTO_UDP:
-                    sBuilder.matchIPProtocol(IPv4.PROTOCOL_UDP);
-                    break;
-                default:
-            }
-        }
-    }
-
-    private void buildMatchPort(TrafficSelector.Builder sBuilder, String protocol,
-                                OpenstackSecurityGroupRule.Direction direction,
-                                int portMin, int portMax) {
-        if (portMin > 0 && portMax > 0 && portMin == portMax) {
-            if (protocol.toUpperCase().equals(PROTO_TCP)) {
-                if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
-                    sBuilder.matchTcpSrc(TpPort.tpPort(portMax));
-                } else {
-                    sBuilder.matchTcpDst(TpPort.tpPort(portMax));
-                }
-            } else if (protocol.toUpperCase().equals(PROTO_UDP)) {
-                if (direction.equals(OpenstackSecurityGroupRule.Direction.EGRESS)) {
-                    sBuilder.matchUdpSrc(TpPort.tpPort(portMax));
-                } else {
-                    sBuilder.matchUdpDst(TpPort.tpPort(portMax));
-                }
-            }
-        }
-    }
-
-    private void updateSecurityGroupRulesMap(Host host) {
-        OpenstackPort osPort = openstackService.port(host.annotations().value(PORT_ID));
-        if (osPort == null) {
-            log.debug("Failed to get OpenStack port information for {}", host);
-            return;
-        }
-
-        Set<SecurityGroupRule> rules = Sets.newHashSet();
-        osPort.securityGroups().forEach(sgId -> {
-            OpenstackSecurityGroup osSecGroup = openstackService.securityGroup(sgId);
-            if (osSecGroup != null) {
-                osSecGroup.rules().forEach(rule -> rules.addAll(getSgRules(rule)));
-            } else {
-                // TODO handle the case that the security group removed
-                log.warn("Failed to get security group {}", sgId);
-            }
-        });
-        securityGroupRuleMap.put(host, rules);
-    }
-
-    /**
-     * Returns set of security group rules with individual remote IP by
-     * converting remote group to actual IP address.
-     *
-     * @param sgRule security group rule
-     * @return set of security group rules
-     */
-    private Set<SecurityGroupRule> getSgRules(OpenstackSecurityGroupRule sgRule) {
-        Set<SecurityGroupRule> sgRules = Sets.newHashSet();
-        if (sgRule.remoteGroupId() != null && !sgRule.remoteGroupId().equals("null")) {
-            sgRules = getRemoteIps(sgRule.tenantId(), sgRule.remoteGroupId())
-                    .stream()
-                    .map(remoteIp -> new SecurityGroupRule(sgRule, remoteIp))
-                    .collect(Collectors.toSet());
-        } else {
-            sgRules.add(new SecurityGroupRule(sgRule, sgRule.remoteIpPrefix()));
-        }
-        return sgRules;
-    }
-
-    /**
-     * Returns a set of host IP addresses engaged with supplied security group ID.
-     * It only searches a VM in the same tenant boundary.
-     *
-     * @param tenantId tenant id
-     * @param sgId security group id
-     * @return set of ip addresses in ip prefix format
-     */
-    private Set<IpPrefix> getRemoteIps(String tenantId, String sgId) {
-        Set<IpPrefix> remoteIps = Sets.newHashSet();
-        securityGroupRuleMap.entrySet().stream()
-                .filter(entry -> Objects.equals(getTenantId(entry.getKey()), tenantId))
-                .forEach(entry -> {
-                    if (entry.getValue().stream()
-                            .anyMatch(rule -> rule.rule().secuityGroupId().equals(sgId))) {
-                        remoteIps.add(IpPrefix.valueOf(getIp(entry.getKey()), 32));
-                    }
-                });
-        return remoteIps;
-    }
-
-    private void updateSecurityGroupRules(Host host, boolean isHostAdded) {
-        String tenantId = getTenantId(host);
-        populateSecurityGroupRules(tenantId, false);
-
-        if (isHostAdded) {
-            updateSecurityGroupRulesMap(host);
-        } else {
-            securityGroupRuleMap.remove(host);
-        }
-
-        Tools.stream(hostService.getHosts())
-                .filter(h -> Objects.equals(getTenantId(h), getTenantId(host)))
-                .forEach(this::updateSecurityGroupRulesMap);
-
-        populateSecurityGroupRules(tenantId, true);
-    }
-
-    @Override
-    protected void hostDetected(Host host) {
-        updateSecurityGroupRules(host, true);
-        log.info("Applied security group rules for {}", host);
-    }
-
-    @Override
-    protected void hostRemoved(Host host) {
-        updateSecurityGroupRules(host, false);
-        log.info("Applied security group rules for {}", host);
-    }
-
-    @Override
-    public void reinstallVmFlow(Host host) {
-        if (host == null) {
-            hostService.getHosts().forEach(h -> {
-                updateSecurityGroupRules(h, true);
-                log.info("Re-Install data plane flow of virtual machine {}", h);
-            });
-        } else {
-            securityGroupRuleMap.entrySet().stream()
-                .filter(entry -> entry.getKey().id().equals(host.id()))
-                .forEach(entry -> {
-                    Host local = entry.getKey();
-                    entry.getValue().forEach(sgRule -> {
-                        setSecurityGroupRule(local.location().deviceId(),
-                                sgRule.rule(),
-                                getIp(local),
-                                sgRule.remoteIp(), true);
-                    });
-                });
-            log.info("Re-Install data plane flow of virtual machine {}", host);
-        }
-    }
-
-    @Override
-    public void purgeVmFlow(Host host) {
-        if (host == null) {
-            securityGroupRuleMap.entrySet().stream()
-                .forEach(entry -> {
-                    Host local = entry.getKey();
-                    entry.getValue().forEach(sgRule -> {
-                        setSecurityGroupRule(local.location().deviceId(),
-                                sgRule.rule(),
-                                getIp(local),
-                                sgRule.remoteIp(), false);
-                    });
-                    log.info("Purge data plane flow of virtual machine {}", local);
-                });
-        } else {
-            securityGroupRuleMap.entrySet().stream()
-                .filter(entry -> entry.getKey().id().equals(host.id()))
-                .forEach(entry -> {
-                    Host local = entry.getKey();
-                    entry.getValue().forEach(sgRule -> {
-                        setSecurityGroupRule(local.location().deviceId(),
-                                sgRule.rule(),
-                                getIp(local),
-                                sgRule.remoteIp(), false);
-                    });
-                });
-            log.info("Purge data plane flow of virtual machine {}", host);
-        }
-    }
-
-    private final class SecurityGroupRule {
-        private final OpenstackSecurityGroupRule rule;
-        private final IpPrefix remoteIp;
-
-        private SecurityGroupRule(OpenstackSecurityGroupRule rule, IpPrefix remoteIp) {
-            this.rule = rule;
-            this.remoteIp = remoteIp;
-        }
-
-        private OpenstackSecurityGroupRule rule() {
-            return rule;
-        }
-
-        private IpPrefix remoteIp() {
-            return remoteIp;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-
-            if (obj instanceof SecurityGroupRule) {
-                SecurityGroupRule that = (SecurityGroupRule) obj;
-                if (Objects.equals(rule, that.rule) &&
-                        Objects.equals(remoteIp, that.remoteIp)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(rule, remoteIp);
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index b4ae36c..2a980d3 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -25,64 +25,97 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.ARP;
+import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.util.Tools;
-import org.onosproject.net.Host;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackNetwork;
-import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.model.network.Subnet;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import java.nio.ByteBuffer;
 import java.util.Dictionary;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 
 /**
  * Handles ARP packet from VMs.
  */
 @Component(immediate = true)
-public final class OpenstackSwitchingArpHandler extends AbstractVmHandler {
+public final class OpenstackSwitchingArpHandler {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String GATEWAY_MAC = "gatewayMac";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
+    protected ComponentConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
 
     @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
             label = "Fake MAC address for virtual network subnet gateway")
     private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
 
     private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
-    private final Set<Ip4Address> gateways = Sets.newConcurrentHashSet();
+    private final InternalOpenstackNetworkListener osNetworkListener =
+            new InternalOpenstackNetworkListener();
+    private final Set<IpAddress> gateways = Sets.newConcurrentHashSet();
+
+    private ApplicationId appId;
 
     @Activate
     protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        configService.registerProperties(getClass());
+        osNetworkService.addListener(osNetworkListener);
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
-        super.activate();
+        osNetworkService.subnets().forEach(this::addSubnetGateway);
+        requestPacket();
+
+        log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
-        super.deactivate();
+        osNetworkService.removeListener(osNetworkListener);
+        configService.unregisterProperties(getClass(), false);
+
+        log.info("Stopped");
     }
 
     @Modified
@@ -98,12 +131,41 @@
         log.info("Modified");
     }
 
+    private void requestPacket() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                .build();
+
+        packetService.requestPackets(
+                selector,
+                PacketPriority.CONTROL,
+                appId);
+    }
+
+    private void addSubnetGateway(Subnet osSubnet) {
+        if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+            return;
+        }
+        IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+        gateways.add(gatewayIp);
+        log.debug("Added ARP proxy entry IP:{}", gatewayIp);
+    }
+
+    private void removeSubnetGateway(Subnet osSubnet) {
+        if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+            return;
+        }
+        IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+        gateways.remove(gatewayIp);
+        log.debug("Removed ARP proxy entry IP:{}", gatewayIp);
+    }
+
     /**
      * Processes ARP request packets.
      * It checks if the target IP is owned by a known host first and then ask to
      * OpenStack if it's not. This ARP proxy does not support overlapping IP.
      *
-     * @param context packet context
+     * @param context   packet context
      * @param ethPacket ethernet packet
      */
     private void processPacketIn(PacketContext context, Ethernet ethPacket) {
@@ -112,15 +174,18 @@
             return;
         }
 
-        Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
-        MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
-                getMacFromHostService(targetIp);
-        if (replyMac.equals(MacAddress.NONE)) {
-            replyMac = getMacFromOpenstack(targetIp);
+        InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
+        if (srcInstPort == null) {
+            log.trace("Failed to find source instance port(MAC:{})",
+                    ethPacket.getSourceMAC());
+            return;
         }
 
+        IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
+        MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
+                getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
         if (replyMac == MacAddress.NONE) {
-            log.debug("Failed to find MAC address for {}", targetIp.toString());
+            log.trace("Failed to find MAC address for {}", targetIp);
             return;
         }
 
@@ -141,66 +206,24 @@
 
     /**
      * Returns MAC address of a host with a given target IP address by asking to
-     * OpenStack. It does not support overlapping IP.
-     *
-     * @param targetIp target ip address
-     * @return mac address, or null if it fails to fetch the mac
-     */
-    private MacAddress getMacFromOpenstack(IpAddress targetIp) {
-        checkNotNull(targetIp);
-
-        OpenstackPort openstackPort = openstackService.ports()
-                .stream()
-                .filter(port -> port.fixedIps().containsValue(targetIp.getIp4Address()))
-                .findFirst()
-                .orElse(null);
-
-        if (openstackPort != null) {
-            log.debug("Found MAC from OpenStack for {}", targetIp.toString());
-            return openstackPort.macAddress();
-        } else {
-            return MacAddress.NONE;
-        }
-    }
-
-    /**
-     * Returns MAC address of a host with a given target IP address by asking to
-     * host service. It does not support overlapping IP.
+     * instance port service.
      *
      * @param targetIp target ip
-     * @return mac address, or null if it fails to find the mac
+     * @param osNetId  openstack network id of the source instance port
+     * @return mac address, or none mac address if it fails to find the mac
      */
-    private MacAddress getMacFromHostService(IpAddress targetIp) {
+    private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
         checkNotNull(targetIp);
 
-        Host host = hostService.getHostsByIp(targetIp)
-                .stream()
-                .findFirst()
-                .orElse(null);
-
-        if (host != null) {
-            log.debug("Found MAC from host service for {}", targetIp.toString());
-            return host.mac();
+        InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
+        if (instPort != null) {
+            log.trace("Found MAC from host service for {}", targetIp);
+            return instPort.macAddress();
         } else {
             return MacAddress.NONE;
         }
     }
 
-    @Override
-    protected void hostDetected(Host host) {
-        OpenstackNetwork osNet = openstackService.network(host.annotations().value(NETWORK_ID));
-        if (osNet == null) {
-            log.warn("Failed to get OpenStack network for {}", host);
-            return;
-        }
-        osNet.subnets().forEach(subnet -> gateways.add(Ip4Address.valueOf(subnet.gatewayIp())));
-    }
-
-    @Override
-    protected void hostRemoved(Host host) {
-        // TODO remove subnet gateway from gateways if no hosts exists on that subnet
-    }
-
     private class InternalPacketProcessor implements PacketProcessor {
 
         @Override
@@ -216,4 +239,38 @@
             processPacketIn(context, ethPacket);
         }
     }
+
+    private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNetworkEvent event) {
+            Subnet osSubnet = event.subnet();
+            if (osSubnet == null) {
+                return false;
+            }
+            return !Strings.isNullOrEmpty(osSubnet.getGateway());
+        }
+
+        @Override
+        public void event(OpenstackNetworkEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_SUBNET_CREATED:
+                case OPENSTACK_SUBNET_UPDATED:
+                    addSubnetGateway(event.subnet());
+                    break;
+                case OPENSTACK_SUBNET_REMOVED:
+                    removeSubnetGateway(event.subnet());
+                    break;
+                case OPENSTACK_NETWORK_CREATED:
+                case OPENSTACK_NETWORK_UPDATED:
+                case OPENSTACK_NETWORK_REMOVED:
+                case OPENSTACK_PORT_CREATED:
+                case OPENSTACK_PORT_UPDATED:
+                case OPENSTACK_PORT_REMOVED:
+                default:
+                    // do nothing for the other events
+                    break;
+            }
+        }
+    }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java
new file mode 100644
index 0000000..d87326d8
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingDhcpHandler.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+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.onlab.packet.DHCP;
+import org.onlab.packet.DHCPOption;
+import org.onlab.packet.DHCPPacketType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.UDP;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.model.network.IP;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Dictionary;
+import java.util.List;
+
+import static org.onlab.packet.DHCP.DHCPOptionCode.*;
+import static org.onlab.packet.DHCPPacketType.DHCPACK;
+import static org.onlab.packet.DHCPPacketType.DHCPOFFER;
+import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Handles DHCP requests for the virtual instances.
+ */
+@Component(immediate = true)
+public class OpenstackSwitchingDhcpHandler {
+    protected final Logger log = getLogger(getClass());
+
+    private static final String DHCP_SERVER_MAC = "dhcpServerMac";
+    private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
+    private static final byte PACKET_TTL = (byte) 127;
+    // TODO add MTU, static route option codes to ONOS DHCP and remove here
+    private static final byte DHCP_OPTION_MTU = (byte) 26;
+    private static final byte[] DHCP_DATA_LEASE_INFINITE =
+            ByteBuffer.allocate(4).putInt(-1).array();
+    private static final byte[] DHCP_DATA_MTU_DEFAULT =
+            ByteBuffer.allocate(2).putShort((short) 1450).array();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Property(name = DHCP_SERVER_MAC, value = DEFAULT_GATEWAY_MAC_STR,
+            label = "Fake MAC address for virtual network subnet gateway")
+    private String dhcpServerMac = DEFAULT_GATEWAY_MAC_STR;
+
+    private final PacketProcessor packetProcessor = new InternalPacketProcessor();
+
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+        configService.registerProperties(getClass());
+        packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
+        requestPackets();
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        cancelPackets();
+        packetService.removeProcessor(packetProcessor);
+        configService.unregisterProperties(getClass(), false);
+
+        log.info("Stopped");
+    }
+
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        String updatedMac;
+
+        updatedMac = Tools.get(properties, DHCP_SERVER_MAC);
+        if (!Strings.isNullOrEmpty(updatedMac) && !updatedMac.equals(dhcpServerMac)) {
+            dhcpServerMac = updatedMac;
+        }
+
+        log.info("Modified");
+    }
+
+    private void requestPackets() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
+                .build();
+        packetService.requestPackets(selector, PacketPriority.CONTROL, appId);
+    }
+
+    private void cancelPackets() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPProtocol(IPv4.PROTOCOL_UDP)
+                .matchUdpDst(TpPort.tpPort(UDP.DHCP_SERVER_PORT))
+                .matchUdpSrc(TpPort.tpPort(UDP.DHCP_CLIENT_PORT))
+                .build();
+        packetService.cancelPackets(selector, PacketPriority.CONTROL, appId);
+    }
+
+    private class InternalPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            if (context.isHandled()) {
+                return;
+            }
+
+            Ethernet ethPacket = context.inPacket().parsed();
+            if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_IPV4) {
+                return;
+            }
+            IPv4 ipv4Packet = (IPv4) ethPacket.getPayload();
+            if (ipv4Packet.getProtocol() != IPv4.PROTOCOL_UDP) {
+                return;
+            }
+            UDP udpPacket = (UDP) ipv4Packet.getPayload();
+            if (udpPacket.getDestinationPort() != UDP.DHCP_SERVER_PORT ||
+                    udpPacket.getSourcePort() != UDP.DHCP_CLIENT_PORT) {
+                return;
+            }
+
+            DHCP dhcpPacket = (DHCP) udpPacket.getPayload();
+            processDhcp(context, dhcpPacket);
+        }
+
+        private void processDhcp(PacketContext context, DHCP dhcpPacket) {
+            if (dhcpPacket == null) {
+                log.trace("DHCP packet without payload received, do nothing");
+                return;
+            }
+
+            DHCPPacketType inPacketType = getPacketType(dhcpPacket);
+            if (inPacketType == null || dhcpPacket.getClientHardwareAddress() == null) {
+                log.trace("Malformed DHCP packet received, ignore it");
+                return;
+            }
+
+            MacAddress clientMac = MacAddress.valueOf(dhcpPacket.getClientHardwareAddress());
+            InstancePort reqInstPort = instancePortService.instancePort(clientMac);
+            if (reqInstPort == null) {
+                log.trace("Failed to find host(MAC:{})", clientMac);
+                return;
+            }
+            Ethernet ethPacket = context.inPacket().parsed();
+            switch (inPacketType) {
+                case DHCPDISCOVER:
+                    log.trace("DHCP DISCOVER received from {}", clientMac);
+                    Ethernet discoverReply = buildReply(
+                            ethPacket,
+                            (byte) DHCPOFFER.getValue(),
+                            reqInstPort);
+                    sendReply(context, discoverReply);
+                    log.trace("DHCP OFFER({}) is sent for {}",
+                              reqInstPort.ipAddress(), clientMac);
+                    break;
+                case DHCPREQUEST:
+                    log.trace("DHCP REQUEST received from {}", clientMac);
+                    Ethernet requestReply = buildReply(
+                            ethPacket,
+                            (byte) DHCPACK.getValue(),
+                            reqInstPort);
+                    sendReply(context, requestReply);
+                    log.trace("DHCP ACK({}) is sent for {}",
+                              reqInstPort.ipAddress(), clientMac);
+                    break;
+                case DHCPRELEASE:
+                    log.trace("DHCP RELEASE received from {}", clientMac);
+                    // do nothing
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private DHCPPacketType getPacketType(DHCP dhcpPacket) {
+            DHCPOption optType = dhcpPacket.getOption(OptionCode_MessageType);
+            if (optType == null) {
+                log.trace("DHCP packet with no message type, ignore it");
+                return null;
+            }
+
+            DHCPPacketType inPacketType = DHCPPacketType.getType(optType.getData()[0]);
+            if (inPacketType == null) {
+                log.trace("DHCP packet with no packet type, ignore it");
+            }
+            return inPacketType;
+        }
+
+        private Ethernet buildReply(Ethernet ethRequest, byte packetType,
+                                    InstancePort reqInstPort) {
+            Port osPort = osNetworkService.port(reqInstPort.portId());
+            // pick one IP address to make a reply
+            IP fixedIp = osPort.getFixedIps().stream().findFirst().get();
+            Subnet osSubnet = osNetworkService.subnet(fixedIp.getSubnetId());
+
+            Ethernet ethReply = new Ethernet();
+            ethReply.setSourceMACAddress(dhcpServerMac);
+            ethReply.setDestinationMACAddress(ethRequest.getSourceMAC());
+            ethReply.setEtherType(Ethernet.TYPE_IPV4);
+
+            IPv4 ipv4Request = (IPv4) ethRequest.getPayload();
+            IPv4 ipv4Reply = new IPv4();
+            ipv4Reply.setSourceAddress(Ip4Address.valueOf(osSubnet.getGateway()).toInt());
+            ipv4Reply.setDestinationAddress(reqInstPort.ipAddress().getIp4Address().toInt());
+            ipv4Reply.setTtl(PACKET_TTL);
+
+            UDP udpRequest = (UDP) ipv4Request.getPayload();
+            UDP udpReply = new UDP();
+            udpReply.setSourcePort((byte) UDP.DHCP_SERVER_PORT);
+            udpReply.setDestinationPort((byte) UDP.DHCP_CLIENT_PORT);
+
+            DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
+            DHCP dhcpReply = buildDhcpReply(
+                    dhcpRequest,
+                    packetType,
+                    reqInstPort.ipAddress().getIp4Address(),
+                    osSubnet);
+
+            udpReply.setPayload(dhcpReply);
+            ipv4Reply.setPayload(udpReply);
+            ethReply.setPayload(ipv4Reply);
+
+            return ethReply;
+        }
+
+        private void sendReply(PacketContext context, Ethernet ethReply) {
+            if (ethReply == null) {
+                return;
+            }
+            ConnectPoint srcPoint = context.inPacket().receivedFrom();
+            TrafficTreatment treatment = DefaultTrafficTreatment
+                    .builder()
+                    .setOutput(srcPoint.port())
+                    .build();
+
+            packetService.emit(new DefaultOutboundPacket(
+                    srcPoint.deviceId(),
+                    treatment,
+                    ByteBuffer.wrap(ethReply.serialize())));
+            context.block();
+        }
+
+        private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
+                                    Subnet osSubnet) {
+            Ip4Address gatewayIp = Ip4Address.valueOf(osSubnet.getGateway());
+            int subnetPrefixLen = IpPrefix.valueOf(osSubnet.getCidr()).prefixLength();
+
+            DHCP dhcpReply = new DHCP();
+            dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
+            dhcpReply.setHardwareType(DHCP.HWTYPE_ETHERNET);
+            dhcpReply.setHardwareAddressLength((byte) 6);
+            dhcpReply.setTransactionId(request.getTransactionId());
+            dhcpReply.setFlags(request.getFlags());
+            dhcpReply.setYourIPAddress(yourIp.toInt());
+            dhcpReply.setServerIPAddress(gatewayIp.toInt());
+            dhcpReply.setClientHardwareAddress(request.getClientHardwareAddress());
+
+            List<DHCPOption> options = Lists.newArrayList();
+            // message type
+            DHCPOption option = new DHCPOption();
+            option.setCode(OptionCode_MessageType.getValue());
+            option.setLength((byte) 1);
+            byte[] optionData = {msgType};
+            option.setData(optionData);
+            options.add(option);
+
+            // server identifier
+            option = new DHCPOption();
+            option.setCode(OptionCode_DHCPServerIp.getValue());
+            option.setLength((byte) 4);
+            option.setData(gatewayIp.toOctets());
+            options.add(option);
+
+            // lease time
+            option = new DHCPOption();
+            option.setCode(OptionCode_LeaseTime.getValue());
+            option.setLength((byte) 4);
+            option.setData(DHCP_DATA_LEASE_INFINITE);
+            options.add(option);
+
+            // subnet mask
+            Ip4Address subnetMask = Ip4Address.makeMaskPrefix(subnetPrefixLen);
+            option = new DHCPOption();
+            option.setCode(OptionCode_SubnetMask.getValue());
+            option.setLength((byte) 4);
+            option.setData(subnetMask.toOctets());
+            options.add(option);
+
+            // broadcast address
+            Ip4Address broadcast = Ip4Address.makeMaskedAddress(yourIp, subnetPrefixLen);
+            option = new DHCPOption();
+            option.setCode(OptionCode_BroadcastAddress.getValue());
+            option.setLength((byte) 4);
+            option.setData(broadcast.toOctets());
+            options.add(option);
+
+            // domain server
+            option = new DHCPOption();
+            option.setCode(OptionCode_DomainServer.getValue());
+            option.setLength((byte) 4);
+            option.setData(DEFAULT_DNS.toOctets());
+            options.add(option);
+
+            // TODO fix MTU value to be configurable
+            option = new DHCPOption();
+            option.setCode(DHCP_OPTION_MTU);
+            option.setLength((byte) 2);
+            option.setData(DHCP_DATA_MTU_DEFAULT);
+            options.add(option);
+
+            // router address
+            option = new DHCPOption();
+            option.setCode(OptionCode_RouterAddress.getValue());
+            option.setLength((byte) 4);
+            option.setData(gatewayIp.toOctets());
+            options.add(option);
+
+            // end option
+            option = new DHCPOption();
+            option.setCode(OptionCode_END.getValue());
+            option.setLength((byte) 1);
+            options.add(option);
+
+            dhcpReply.setOptions(options);
+            return dhcpReply;
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
new file mode 100644
index 0000000..50d9b37
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
@@ -0,0 +1,239 @@
+/*
+* Copyright 2016-present Open Networking Laboratory
+*
+* 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+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.onlab.packet.Ethernet;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortEvent;
+import org.onosproject.openstacknetworking.api.InstancePortListener;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.openstack4j.model.network.Network;
+import org.slf4j.Logger;
+
+import java.util.concurrent.ExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Populates switching flow rules on OVS for the basic connectivity among the
+ * virtual instances in the same network.
+ */
+@Component(immediate = true)
+public final class OpenstackSwitchingHandler {
+
+    private final Logger log = getLogger(getClass());
+
+    private static final String ERR_SET_FLOWS = "Failed to set flows for %s: ";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected FlowObjectiveService flowObjectiveService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler"));
+    private final InstancePortListener instancePortListener = new InternalInstancePortListener();
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        instancePortService.addListener(instancePortListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        instancePortService.removeListener(instancePortListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    private void setNetworkRules(InstancePort instPort, boolean install) {
+        setTunnelTagFlowRules(instPort, install);
+        setForwardingRules(instPort, install);
+    }
+
+    private void setForwardingRules(InstancePort instPort, boolean install) {
+        // switching rules for the instPorts in the same node
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(instPort.ipAddress().toIpPrefix())
+                .matchTunnelId(getVni(instPort))
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setEthDst(instPort.macAddress())
+                .setOutput(instPort.portNumber())
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                instPort.deviceId(),
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_SWITCHING_RULE,
+                install);
+
+        // switching rules for the instPorts in the remote node
+        for (OpenstackNode osNode : osNodeService.completeNodes()) {
+            if (osNode.intBridge().equals(instPort.deviceId())) {
+                continue;
+            }
+
+            TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                    .extension(buildExtension(
+                            deviceService,
+                            osNode.intBridge(),
+                            osNodeService.dataIp(instPort.deviceId()).get().getIp4Address()),
+                            osNode.intBridge())
+                    .setOutput(osNodeService.tunnelPort(osNode.intBridge()).get())
+                    .build();
+
+            RulePopulatorUtil.setRule(
+                    flowObjectiveService,
+                    appId,
+                    osNode.intBridge(),
+                    selector,
+                    treatmentToRemote,
+                    ForwardingObjective.Flag.SPECIFIC,
+                    PRIORITY_SWITCHING_RULE,
+                    install);
+        }
+    }
+
+    private void setTunnelTagFlowRules(InstancePort instPort, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(instPort.portNumber())
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setTunnelId(getVni(instPort))
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                instPort.deviceId(),
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_TUNNEL_TAG_RULE,
+                install);
+    }
+
+    private Long getVni(InstancePort instPort) {
+        Network osNet = osNetworkService.network(instPort.networkId());
+        if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
+            final String error = String.format(
+                    ERR_SET_FLOWS + "Failed to get VNI for %s",
+                    instPort, osNet.getName());
+            throw new IllegalStateException(error);
+        }
+        return Long.valueOf(osNet.getProviderSegID());
+    }
+
+    private class InternalInstancePortListener implements InstancePortListener {
+
+        @Override
+        public boolean isRelevant(InstancePortEvent event) {
+            InstancePort instPort = event.subject();
+            return mastershipService.isLocalMaster(instPort.deviceId());
+        }
+
+        @Override
+        public void event(InstancePortEvent event) {
+            InstancePort instPort = event.subject();
+            switch (event.type()) {
+                case OPENSTACK_INSTANCE_PORT_UPDATED:
+                case OPENSTACK_INSTANCE_PORT_DETECTED:
+                    eventExecutor.execute(() -> {
+                        log.info("Instance port detected MAC:{} IP:{}",
+                                instPort.macAddress(),
+                                instPort.ipAddress());
+                        instPortDetected(event.subject());
+                    });
+                    break;
+                case OPENSTACK_INSTANCE_PORT_VANISHED:
+                    eventExecutor.execute(() -> {
+                        log.info("Instance port vanished MAC:{} IP:{}",
+                                instPort.macAddress(),
+                                instPort.ipAddress());
+                        instPortRemoved(event.subject());
+                    });
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private void instPortDetected(InstancePort instPort) {
+            setNetworkRules(instPort, true);
+            // TODO add something else if needed
+        }
+
+        private void instPortRemoved(InstancePort instPort) {
+            setNetworkRules(instPort, false);
+            // TODO add something else if needed
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java
deleted file mode 100644
index 99dd927..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostManager.java
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * 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.openstacknetworking.impl;
-
-import com.google.common.base.Strings;
-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.Ip4Address;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.CoreService;
-import org.onosproject.dhcp.DhcpService;
-import org.onosproject.dhcp.IpAssignment;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.Device;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.Port;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.host.DefaultHostDescription;
-import org.onosproject.net.host.HostDescription;
-import org.onosproject.net.host.HostProvider;
-import org.onosproject.net.host.HostProviderRegistry;
-import org.onosproject.net.host.HostProviderService;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.provider.AbstractProvider;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackNetwork;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.OpenstackSubnet;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Date;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.openstacknetworking.api.Constants.*;
-
-@Service
-@Component(immediate = true)
-public final class OpenstackSwitchingHostManager extends AbstractProvider
-        implements HostProvider {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostProviderRegistry hostProviderRegistry;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DhcpService dhcpService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService openstackNodeService;
-
-    private final ExecutorService deviceEventExecutor =
-            Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event"));
-    private final ExecutorService configEventExecutor =
-            Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "config-event"));
-    private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
-    private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
-
-    private HostProviderService hostProvider;
-
-    /**
-     * Creates OpenStack switching host provider.
-     */
-    public OpenstackSwitchingHostManager() {
-        super(new ProviderId("host", SWITCHING_APP_ID));
-    }
-
-    @Activate
-    protected void activate() {
-        coreService.registerApplication(SWITCHING_APP_ID);
-        deviceService.addListener(internalDeviceListener);
-        openstackNodeService.addListener(internalNodeListener);
-        hostProvider = hostProviderRegistry.register(this);
-
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        hostProviderRegistry.unregister(this);
-        deviceService.removeListener(internalDeviceListener);
-        openstackNodeService.removeListener(internalNodeListener);
-
-        deviceEventExecutor.shutdown();
-        configEventExecutor.shutdown();
-
-        log.info("Stopped");
-    }
-
-    @Override
-    public void triggerProbe(Host host) {
-        // no probe is required
-    }
-
-    private void processPortAdded(Port port) {
-        // TODO check the node state is COMPLETE
-        OpenstackPort osPort = openstackService.port(port);
-        if (osPort == null) {
-            log.warn("Failed to get OpenStack port for {}", port);
-            return;
-        }
-
-        OpenstackNetwork osNet = openstackService.network(osPort.networkId());
-        if (osNet == null) {
-            log.warn("Failed to get OpenStack network {}",
-                    osPort.networkId());
-            return;
-        }
-
-        Map.Entry<String, Ip4Address> fixedIp = osPort.fixedIps().entrySet().stream().findFirst().get();
-
-        OpenstackSubnet openstackSubnet = openstackService.subnets().stream()
-                .filter(n -> n.networkId().equals(osPort.networkId()) &&
-                n.id().equals(fixedIp.getKey()))
-                .findFirst().orElse(null);
-        if (openstackSubnet == null) {
-            log.warn("Failed to find subnet for {}", osPort);
-            return;
-        }
-
-        registerDhcpInfo(osPort, openstackSubnet);
-        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
-
-        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                .set(NETWORK_ID, osPort.networkId())
-                .set(SUBNET_ID, fixedIp.getKey())
-                .set(PORT_ID, osPort.id())
-                .set(VXLAN_ID, osNet.segmentId())
-                .set(TENANT_ID, osNet.tenantId())
-                // TODO remove gateway IP from host annotation
-                .set(GATEWAY_IP, openstackSubnet.gatewayIp());
-
-        HostDescription hostDesc = new DefaultHostDescription(
-                osPort.macAddress(),
-                VlanId.NONE,
-                new HostLocation(connectPoint, System.currentTimeMillis()),
-                Sets.newHashSet(osPort.fixedIps().values()),
-                annotations.build());
-
-        HostId hostId = HostId.hostId(osPort.macAddress());
-        hostProvider.hostDetected(hostId, hostDesc, false);
-    }
-
-    private void processPortRemoved(Port port) {
-        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
-        removeHosts(connectPoint);
-    }
-
-    private void removeHosts(ConnectPoint connectPoint) {
-        hostService.getConnectedHosts(connectPoint).stream()
-                .forEach(host -> {
-                    dhcpService.removeStaticMapping(host.mac());
-                    hostProvider.hostVanished(host.id());
-                });
-    }
-
-    private void registerDhcpInfo(OpenstackPort openstackPort, OpenstackSubnet openstackSubnet) {
-        checkNotNull(openstackPort);
-        checkNotNull(openstackSubnet);
-        checkArgument(!openstackPort.fixedIps().isEmpty());
-
-        Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get();
-        IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr());
-        Ip4Address broadcast = Ip4Address.makeMaskedAddress(
-                ipAddress,
-                subnetPrefix.prefixLength());
-
-        // TODO: supports multiple DNS servers
-        Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ?
-                DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0);
-
-        IpAssignment ipAssignment = IpAssignment.builder()
-                .ipAddress(ipAddress)
-                .leasePeriod(DHCP_INFINITE_LEASE)
-                .timestamp(new Date())
-                .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength()))
-                .broadcast(broadcast)
-                .domainServer(domainServer)
-                .assignmentStatus(Option_RangeNotEnforced)
-                .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp()))
-                .build();
-
-        dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment);
-    }
-
-    private class InternalDeviceListener implements DeviceListener {
-
-        @Override
-        public void event(DeviceEvent event) {
-            Device device = event.subject();
-            if (!mastershipService.isLocalMaster(device.id())) {
-                // do not allow to proceed without mastership
-                return;
-            }
-
-            Port port = event.port();
-            if (port == null) {
-                return;
-            }
-
-            String portName = port.annotations().value(PORT_NAME);
-            if (Strings.isNullOrEmpty(portName) ||
-                    !portName.startsWith(PORT_NAME_PREFIX_VM)) {
-                // handles VM connected port event only
-                return;
-            }
-
-            switch (event.type()) {
-                case PORT_UPDATED:
-                    if (!event.port().isEnabled()) {
-                        deviceEventExecutor.execute(() -> processPortRemoved(event.port()));
-                    } else if (event.port().isEnabled()) {
-                        deviceEventExecutor.execute(() -> processPortAdded(event.port()));
-                    }
-                    break;
-                case PORT_ADDED:
-                    deviceEventExecutor.execute(() -> processPortAdded(event.port()));
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
-
-        @Override
-        public void event(OpenstackNodeEvent event) {
-            OpenstackNode node = event.node();
-            // TODO check leadership of the node and make only the leader process
-
-            switch (event.type()) {
-                case COMPLETE:
-                    log.info("COMPLETE node {} detected", node.hostname());
-
-                    // adds existing VMs running on the complete state node
-                    deviceService.getPorts(node.intBridge()).stream()
-                            .filter(port -> port.annotations().value(PORT_NAME)
-                                    .startsWith(PORT_NAME_PREFIX_VM) &&
-                                    port.isEnabled())
-                            .forEach(port -> {
-                                deviceEventExecutor.execute(() -> processPortAdded(port));
-                                log.info("VM is detected on {}", port);
-                            });
-
-                    // removes stale VMs
-                    hostService.getHosts().forEach(host -> {
-                        if (deviceService.getPort(host.location().deviceId(),
-                                host.location().port()) == null) {
-                            deviceEventExecutor.execute(() -> removeHosts(host.location()));
-                            log.info("Removed stale VM {}", host.location());
-                        }
-                    });
-                    break;
-                case INCOMPLETE:
-                    log.warn("{} is changed to INCOMPLETE state", node);
-                    break;
-                case INIT:
-                case DEVICE_CREATED:
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
new file mode 100644
index 0000000..fe07b9d
--- /dev/null
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.openstacknetworking.impl;
+
+import com.google.common.base.Strings;
+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.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.Tools;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.openstack4j.model.network.Network;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_CREATE_TIME;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
+import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
+
+@Service
+@Component(immediate = true)
+public final class OpenstackSwitchingHostProvider extends AbstractProvider implements HostProvider {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private static final String PORT_NAME_PREFIX_VM = "tap";
+    private static final String ERR_ADD_HOST = "Failed to add host: ";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostProviderRegistry hostProviderRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    private final ExecutorService deviceEventExecutor =
+            Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
+    private final ExecutorService configEventExecutor =
+            Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "config-event"));
+    private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
+    private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
+
+    private HostProviderService hostProvider;
+
+    /**
+     * Creates OpenStack switching host provider.
+     */
+    public OpenstackSwitchingHostProvider() {
+        super(new ProviderId("host", OPENSTACK_NETWORKING_APP_ID));
+    }
+
+    @Activate
+    protected void activate() {
+        coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        deviceService.addListener(internalDeviceListener);
+        osNodeService.addListener(internalNodeListener);
+        hostProvider = hostProviderRegistry.register(this);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        hostProviderRegistry.unregister(this);
+        osNodeService.removeListener(internalNodeListener);
+        deviceService.removeListener(internalDeviceListener);
+
+        deviceEventExecutor.shutdown();
+        configEventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void triggerProbe(Host host) {
+        // no probe is required
+    }
+
+    private void processPortAdded(Port port) {
+        // TODO check the node state is COMPLETE
+        org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
+        if (osPort == null) {
+            log.warn(ERR_ADD_HOST + "OpenStack port for {} not found", port);
+            return;
+        }
+
+        Network osNet = osNetworkService.network(osPort.getNetworkId());
+        if (osNet == null) {
+            log.warn(ERR_ADD_HOST + "OpenStack network {} not found",
+                    osPort.getNetworkId());
+            return;
+        }
+
+        if (osPort.getFixedIps().isEmpty()) {
+            log.warn(ERR_ADD_HOST + "no fixed IP for port {}", osPort.getId());
+            return;
+        }
+
+        MacAddress macAddr = MacAddress.valueOf(osPort.getMacAddress());
+        Set<IpAddress> fixedIps = osPort.getFixedIps().stream()
+                .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
+                .collect(Collectors.toSet());
+        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+
+        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+                .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
+                .set(ANNOTATION_PORT_ID, osPort.getId())
+                .set(ANNOTATION_CREATE_TIME, String.valueOf(System.currentTimeMillis()));
+
+        HostDescription hostDesc = new DefaultHostDescription(
+                macAddr,
+                VlanId.NONE,
+                new HostLocation(connectPoint, System.currentTimeMillis()),
+                fixedIps,
+                annotations.build());
+
+        HostId hostId = HostId.hostId(macAddr);
+        hostProvider.hostDetected(hostId, hostDesc, false);
+    }
+
+    private void processPortRemoved(Port port) {
+        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+        hostService.getConnectedHosts(connectPoint).forEach(host -> {
+                    hostProvider.hostVanished(host.id());
+                });
+    }
+
+    private class InternalDeviceListener implements DeviceListener {
+
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            Device device = event.subject();
+            if (!mastershipService.isLocalMaster(device.id())) {
+                // do not allow to proceed without mastership
+                return false;
+            }
+            Port port = event.port();
+            if (port == null) {
+                return false;
+            }
+            String portName = port.annotations().value(PORT_NAME);
+            if (Strings.isNullOrEmpty(portName) ||
+                    !portName.startsWith(PORT_NAME_PREFIX_VM)) {
+                // handles Nova created port event only
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        public void event(DeviceEvent event) {
+            switch (event.type()) {
+                case PORT_UPDATED:
+                    if (!event.port().isEnabled()) {
+                        deviceEventExecutor.execute(() -> {
+                            log.debug("Instance port {} is removed from {}",
+                                     event.port().annotations().value(PORT_NAME),
+                                     event.subject().id());
+                            processPortRemoved(event.port());
+                        });
+                    } else if (event.port().isEnabled()) {
+                        deviceEventExecutor.execute(() -> {
+                            log.debug("Instance Port {} is detected from {}",
+                                     event.port().annotations().value(PORT_NAME),
+                                     event.subject().id());
+                            processPortAdded(event.port());
+                        });
+                    }
+                    break;
+                case PORT_ADDED:
+                    deviceEventExecutor.execute(() -> {
+                        log.debug("Instance port {} is detected from {}",
+                                 event.port().annotations().value(PORT_NAME),
+                                 event.subject().id());
+                        processPortAdded(event.port());
+                    });
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNode osNode = event.subject();
+            // TODO check leadership of the node and make only the leader process
+
+            switch (event.type()) {
+                case COMPLETE:
+                    deviceEventExecutor.execute(() -> {
+                        log.info("COMPLETE node {} is detected", osNode.hostname());
+                        processCompleteNode(event.subject());
+                    });
+                    break;
+                case INCOMPLETE:
+                    log.warn("{} is changed to INCOMPLETE state", osNode);
+                    break;
+                case INIT:
+                case DEVICE_CREATED:
+                default:
+                    break;
+            }
+        }
+
+        private void processCompleteNode(OpenstackNode osNode) {
+            deviceService.getPorts(osNode.intBridge()).stream()
+                    .filter(port -> port.annotations().value(PORT_NAME)
+                            .startsWith(PORT_NAME_PREFIX_VM) &&
+                            port.isEnabled())
+                    .forEach(port -> {
+                        log.debug("Instance port {} is detected from {}",
+                                  port.annotations().value(PORT_NAME),
+                                  osNode.hostname());
+                        processPortAdded(port);
+                    });
+
+            Tools.stream(hostService.getHosts())
+                    .filter(host -> deviceService.getPort(
+                            host.location().deviceId(),
+                            host.location().port()) == null)
+                    .forEach(host -> {
+                        log.info("Remove stale host {}", host.id());
+                        hostProvider.hostVanished(host.id());
+                });
+        }
+    }
+}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java
deleted file mode 100644
index d9efb3f..0000000
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingManager.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
-* Copyright 2016-present Open Networking Laboratory
-*
-* 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.openstacknetworking.impl;
-
-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.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.openstacknetworking.api.OpenstackSwitchingService;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import static org.onosproject.openstacknetworking.api.Constants.*;
-import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-
-
-/**
- * Populates switching flow rules.
- */
-@Service
-@Component(immediate = true)
-public final class OpenstackSwitchingManager extends AbstractVmHandler
-        implements OpenstackSwitchingService {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService nodeService;
-
-    private ApplicationId appId;
-
-    @Activate
-    protected void activate() {
-        super.activate();
-        appId = coreService.registerApplication(SWITCHING_APP_ID);
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        super.deactivate();
-    }
-
-    private void populateSwitchingRules(Host host) {
-        setFlowRulesForTunnelTag(host, true);
-        setFlowRulesForTrafficToSameCnode(host, true);
-        setFlowRulesForTrafficToDifferentCnode(host, true);
-
-        log.debug("Populated switching rule for {}", host);
-    }
-
-    private void removeSwitchingRules(Host host) {
-        setFlowRulesForTunnelTag(host, false);
-        setFlowRulesForTrafficToSameCnode(host, false);
-        removeFlowRuleForVMsInDiffrentCnode(host);
-
-        log.debug("Removed switching rule for {}", host);
-    }
-
-    private void setFlowRulesForTunnelTag(Host host, boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchInPort(host.location().port());
-
-        tBuilder.setTunnelId(Long.valueOf(getVni(host)));
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, host.location().deviceId(),
-                sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
-                TUNNELTAG_RULE_PRIORITY, install);
-    }
-
-    private void setFlowRulesForTrafficToSameCnode(Host host, boolean install) {
-        //For L2 Switching Case
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(getIp(host).toIpPrefix())
-                .matchTunnelId(Long.valueOf(getVni(host)));
-
-        // Destination setting is required for routing cases.
-        // We do not believe the rule would not degrade the forwarding performance.
-        // But, if it does, we need to move the rule in a separate routing table.
-        tBuilder.setEthDst(host.mac())
-                .setOutput(host.location().port());
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, host.location().deviceId(),
-                sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
-                SWITCHING_RULE_PRIORITY, install);
-    }
-
-    private void setFlowRulesForTrafficToDifferentCnode(Host host, boolean install) {
-        Ip4Address localVmIp = getIp(host);
-        DeviceId localDeviceId = host.location().deviceId();
-        Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
-
-        if (!localDataIp.isPresent()) {
-            log.debug("Failed to get data IP for device {}",
-                    host.location().deviceId());
-            return;
-        }
-
-        String vni = getVni(host);
-        getVmsInDifferentCnode(host).forEach(remoteVm -> {
-            Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
-            if (remoteDataIp.isPresent()) {
-                setVxLanFlowRule(vni,
-                        localDeviceId,
-                        remoteDataIp.get().getIp4Address(),
-                        getIp(remoteVm), install);
-
-                setVxLanFlowRule(vni,
-                        remoteVm.location().deviceId(),
-                        localDataIp.get().getIp4Address(),
-                        localVmIp, install);
-            }
-        });
-    }
-
-    private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
-                                  Ip4Address vmIp, boolean install) {
-        Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
-        if (!tunnelPort.isPresent()) {
-            log.warn("Failed to get tunnel port from {}", deviceId);
-            return;
-        }
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(Long.parseLong(vni))
-                .matchIPDst(vmIp.toIpPrefix());
-        tBuilder.extension(buildExtension(deviceService, deviceId, remoteIp), deviceId)
-                .setOutput(tunnelPort.get());
-
-        RulePopulatorUtil.setRule(flowObjectiveService, appId, deviceId,
-                sBuilder.build(), tBuilder.build(), ForwardingObjective.Flag.SPECIFIC,
-                SWITCHING_RULE_PRIORITY, install);
-    }
-
-    private void removeFlowRuleForVMsInDiffrentCnode(Host host) {
-        DeviceId deviceId = host.location().deviceId();
-        final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId)
-                .stream()
-                .filter(this::isValidHost)
-                .anyMatch(h -> Objects.equals(getVni(h), getVni(host)));
-
-        getVmsInDifferentCnode(host).forEach(h -> {
-            setVxLanFlowRule(getVni(host), h.location().deviceId(), Ip4Address.valueOf(0), getIp(host),  false);
-            if (!anyPortRemainedInSameCnode) {
-                setVxLanFlowRule(getVni(host), deviceId, Ip4Address.valueOf(0), getIp(h), false);
-            }
-        });
-    }
-
-    @Override
-    protected void hostDetected(Host host) {
-        populateSwitchingRules(host);
-        log.info("Added new virtual machine to switching service {}", host);
-    }
-
-    @Override
-    protected void hostRemoved(Host host) {
-        removeSwitchingRules(host);
-        log.info("Removed virtual machine from switching service {}", host);
-    }
-
-    @Override
-    public void reinstallVmFlow(Host host) {
-        if (host == null) {
-            hostService.getHosts().forEach(h -> {
-                populateSwitchingRules(h);
-                log.info("Re-Install data plane flow of virtual machine {}", h);
-            });
-        } else {
-            populateSwitchingRules(host);
-            log.info("Re-Install data plane flow of virtual machine {}", host);
-        }
-    }
-
-    @Override
-    public void purgeVmFlow(Host host) {
-        if (host == null) {
-            hostService.getHosts().forEach(h -> {
-                removeSwitchingRules(h);
-                log.info("Purge data plane flow of virtual machine {}", h);
-            });
-        } else {
-            removeSwitchingRules(host);
-            log.info("Purge data plane flow of virtual machine {}", host);
-        }
-    }
-}
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
index c39afd6..d58cc61 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpensatckRouterWebResource.java
@@ -15,14 +15,13 @@
  */
 package org.onosproject.openstacknetworking.web;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.openstackinterface.OpenstackRouter;
-import org.onosproject.openstackinterface.OpenstackRouterInterface;
-import org.onosproject.openstackinterface.web.OpenstackRouterCodec;
-import org.onosproject.openstackinterface.web.OpenstackRouterInterfaceCodec;
-import org.onosproject.openstacknetworking.api.OpenstackRoutingService;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
 import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronRouter;
+import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,10 +32,17 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
-import static com.google.common.base.Preconditions.checkNotNull;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
 
 /**
  * Handles REST API call of Neturon L3 plugin.
@@ -46,142 +52,140 @@
 public class OpensatckRouterWebResource extends AbstractWebResource {
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    private static final OpenstackRouterInterfaceCodec ROUTER_INTERFACE_CODEC
-            = new OpenstackRouterInterfaceCodec();
-    private static final OpenstackRouterCodec ROUTER_CODEC
-            = new OpenstackRouterCodec();
+    private static final String MESSAGE_ROUTER = "Received router %s request";
+    private static final String MESSAGE_ROUTER_IFACE = "Received router interface %s request";
+    private static final String ROUTERS = "routers";
 
+    private final OpenstackRouterAdminService adminService =
+            DefaultServiceDirectory.getService(OpenstackRouterAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a router from the JSON input stream.
+     *
+     * @param input router JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated router already exists
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createRouter(InputStream input) {
-        checkNotNull(input);
-        try {
-            ObjectMapper mapper = new ObjectMapper();
-            ObjectNode routerNode = (ObjectNode) mapper.readTree(input);
+        log.trace(String.format(MESSAGE_ROUTER, "CREATE"));
 
-            OpenstackRouter openstackRouter
-                    = ROUTER_CODEC.decode(routerNode, this);
+        final NeutronRouter osRouter = readRouter(input);
+        adminService.createRouter(osRouter);
 
-            OpenstackRoutingService routingService
-                    = getService(OpenstackRoutingService.class);
-            routingService.createRouter(openstackRouter);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(ROUTERS)
+                .path(osRouter.getId());
 
-            log.debug("REST API CREATE router is called {}", input.toString());
-            return Response.status(Response.Status.OK).build();
-        } catch (Exception e) {
-            log.error("Create Router failed because of exception {}",
-                    e.toString());
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
-                    .build();
-        }
+        return created(locationBuilder.build()).build();
     }
 
+    /**
+     * Updates the router with the specified identifier.
+     *
+     * @param id router identifier
+     * @param input router JSON input stream
+     * @return 200 OK with the updated router, 400 BAD_REQUEST if the requested
+     * router does not exist
+     */
     @PUT
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response updateRouter(@PathParam("id") String id, InputStream input) {
-        checkNotNull(input);
-        try {
-            ObjectMapper mapper = new ObjectMapper();
-            ObjectNode routerNode = (ObjectNode) mapper.readTree(input);
+        log.trace(String.format(MESSAGE_ROUTER, "UPDATE " + id));
 
-            OpenstackRouter or = ROUTER_CODEC.decode(routerNode, this);
+        final NeutronRouter osRouter = readRouter(input);
+        osRouter.setId(id);
+        adminService.updateRouter(osRouter);
 
-            OpenstackRouter.Builder osBuilder = new OpenstackRouter.Builder()
-                    .tenantId(or.tenantId())
-                    .id(id)
-                    .name(or.name())
-                    .status(OpenstackRouter.RouterStatus.ACTIVE)
-                    .adminStateUp(Boolean.valueOf(or.adminStateUp()))
-                    .gatewayExternalInfo(or.gatewayExternalInfo());
-
-            OpenstackRoutingService routingService
-                    = getService(OpenstackRoutingService.class);
-            routingService.updateRouter(osBuilder.build());
-
-            log.debug("REST API UPDATE router is called from router {}", input.toString());
-            return Response.status(Response.Status.OK).build();
-        } catch (Exception e) {
-            log.error("Updates Router failed because of exception {}",
-                    e.toString());
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
-                    .build();
-        }
+        return status(Response.Status.OK).build();
     }
 
+    /**
+     * Updates the router with the specified router interface.
+     *
+     * @param id router identifier
+     * @param input router interface JSON input stream
+     * @return 200 OK with the updated router interface, 400 BAD_REQUEST if the
+     * requested router does not exist
+     */
     @PUT
     @Path("{id}/add_router_interface")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response addRouterInterface(@PathParam("id") String id, InputStream input) {
-        checkNotNull(input);
-        try {
-            ObjectMapper mapper = new ObjectMapper();
-            ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input);
+        log.trace(String.format(MESSAGE_ROUTER_IFACE, "UPDATE " + id));
 
-            OpenstackRouterInterface openstackRouterInterface
-                    = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this);
+        final NeutronRouterInterface osRouterIface = readRouterInterface(input);
+        adminService.addRouterInterface(osRouterIface);
 
-            OpenstackRoutingService routingService
-                    = getService(OpenstackRoutingService.class);
-            routingService.addRouterInterface(openstackRouterInterface);
-
-            log.debug("REST API AddRouterInterface is called from router {} portId: {}, subnetId: {}, tenantId: {}",
-                    openstackRouterInterface.id(), openstackRouterInterface.portId(),
-                    openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId());
-
-            return Response.status(Response.Status.OK).build();
-        } catch (Exception e) {
-            log.error("AddRouterInterface failed because of exception {}",
-                    e.toString());
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
-                    .build();
-        }
+        return status(Response.Status.OK).build();
     }
 
-    @DELETE
-    @Path("{id}")
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response deleteRouter(@PathParam("id") String id) {
-        checkNotNull(id);
-        OpenstackRoutingService routingService
-                = getService(OpenstackRoutingService.class);
-        routingService.removeRouter(id);
-
-        log.debug("REST API DELETE routers is called {}", id);
-        return Response.noContent().build();
-    }
-
+    /**
+     * Updates the router with the specified router interface.
+     *
+     * @param id router identifier
+     * @param input router interface JSON input stream
+     * @return 200 OK with the updated router interface, 400 BAD_REQUEST if the
+     * requested router does not exist
+     */
     @PUT
     @Path("{id}/remove_router_interface")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response removeRouterInterface(@PathParam("id") String id, InputStream input) {
-        checkNotNull(id);
-        checkNotNull(input);
+        log.trace(String.format(MESSAGE_ROUTER_IFACE, "DELETE " + id));
+
+        final NeutronRouterInterface osRouterIface = readRouterInterface(input);
+        adminService.removeRouterInterface(osRouterIface.getPortId());
+
+        return status(Response.Status.OK).build();
+    }
+
+    /**
+     * Removes the router.
+     *
+     * @param id router identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the router does not exist
+     */
+    @DELETE
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    public Response deleteRouter(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE_ROUTER, "DELETE " + id));
+
+        adminService.removeRouter(id);
+        return noContent().build();
+    }
+
+    private NeutronRouter readRouter(InputStream input) {
         try {
-            ObjectMapper mapper = new ObjectMapper();
-            ObjectNode routerIfNode = (ObjectNode) mapper.readTree(input);
-
-            OpenstackRouterInterface openstackRouterInterface
-                    = ROUTER_INTERFACE_CODEC.decode(routerIfNode, this);
-
-            OpenstackRoutingService routingService
-                    = getService(OpenstackRoutingService.class);
-            routingService.removeRouterInterface(openstackRouterInterface);
-
-            log.debug("REST API RemoveRouterInterface is called from router {} portId: {}, subnetId: {}," +
-                    "tenantId: {}", openstackRouterInterface.id(), openstackRouterInterface.portId(),
-                    openstackRouterInterface.subnetId(), openstackRouterInterface.tenantId());
-
-            return Response.status(Response.Status.OK).build();
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronRouter.class)
+                    .readerFor(NeutronRouter.class)
+                    .readValue(jsonTree);
         } catch (Exception e) {
-            log.error("RemoveRouterInterface failed because of exception {}",
-                    e.toString());
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
-                    .build();
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private NeutronRouterInterface readRouterInterface(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronRouterInterface.class)
+                    .readerFor(NeutronRouterInterface.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
         }
     }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
index a6880fd..87a8e1a 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackFloatingIpWebResource.java
@@ -16,12 +16,12 @@
 
 package org.onosproject.openstacknetworking.web;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.openstackinterface.OpenstackFloatingIP;
-import org.onosproject.openstackinterface.web.OpenstackFloatingIpCodec;
-import org.onosproject.openstacknetworking.api.OpenstackFloatingIpService;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
 import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronFloatingIP;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -32,11 +32,17 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
 
 /**
  * Handles REST API call of Neutron L3 plugin.
@@ -45,89 +51,84 @@
 public class OpenstackFloatingIpWebResource extends AbstractWebResource {
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    private static final OpenstackFloatingIpCodec FLOATING_IP_CODEC = new OpenstackFloatingIpCodec();
+    private static final String MESSAGE = "Received floating IP %s request";
+    private static final String FLOATING_IPS = "floatingips";
+
+    private final OpenstackRouterAdminService adminService =
+            DefaultServiceDirectory.getService(OpenstackRouterAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
 
     /**
-     * Create FloatingIP.
+     * Creates a floating IP from the JSON input stream.
      *
-     * @param input JSON data describing FloatingIP
-     * @return 200 OK
+     * @param input floating ip JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated floating ip already exists
      */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createFloatingIp(InputStream input) {
-        checkNotNull(input);
+        log.trace(String.format(MESSAGE, "CREATE"));
 
-        try {
-            ObjectMapper mapper = new ObjectMapper();
-            ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input);
+        final NeutronFloatingIP floatingIp = readFloatingIp(input);
+        adminService.createFloatingIp(floatingIp);
 
-            OpenstackFloatingIP osFloatingIp = FLOATING_IP_CODEC.decode(floatingIpNode, this);
-            OpenstackFloatingIpService floatingIpService =
-                    getService(OpenstackFloatingIpService.class);
-            floatingIpService.createFloatingIp(osFloatingIp);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(FLOATING_IPS)
+                .path(floatingIp.getId());
 
-            log.debug("REST API CREATE floatingip called");
-            return Response.status(Response.Status.OK).build();
-        } catch (Exception e) {
-            log.error("createFloatingIp failed with {}", e.toString());
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
-                    .build();
-        }
+        return created(locationBuilder.build()).build();
     }
 
     /**
-     * Update FloatingIP.
+     * Updates the floating IP with the specified identifier.
      *
-     * @param id    FloatingIP identifier
-     * @param input JSON data describing FloatingIP
-     * @return 200 OK
+     * @param id    floating ip identifier
+     * @param input floating ip JSON input stream
+     * @return 200 OK with the updated floating ip, 400 BAD_REQUEST if the requested
+     * floating ip does not exist
      */
     @PUT
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response updateFloatingIp(@PathParam("id") String id, InputStream input) {
-        checkNotNull(id);
-        checkNotNull(input);
+        log.trace(String.format(MESSAGE, "UPDATE " + id));
 
-        try {
-            ObjectMapper mapper = new ObjectMapper();
-            ObjectNode floatingIpNode = (ObjectNode) mapper.readTree(input);
+        final NeutronFloatingIP floatingIp = readFloatingIp(input);
+        adminService.updateFloatingIp(floatingIp);
 
-            OpenstackFloatingIP osFloatingIp = FLOATING_IP_CODEC.decode(floatingIpNode, this);
-            OpenstackFloatingIpService floatingIpService =
-                    getService(OpenstackFloatingIpService.class);
-            floatingIpService.updateFloatingIp(osFloatingIp);
-
-            log.debug("REST API UPDATE floatingip called {}", id);
-            return Response.status(Response.Status.OK).build();
-        } catch (Exception e) {
-            log.error("updateFloatingIp failed with {}", e.toString());
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.toString())
-                    .build();
-        }
+        return status(Response.Status.OK).build();
     }
 
     /**
-     * Delete FloatingIP.
+     * Removes the floating IP.
      *
-     * @param id FloatingIP identifier
-     * @return 204 OK
+     * @param id floating ip identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the floating ip does not exist
      */
     @DELETE
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
     public Response deleteFloatingIp(@PathParam("id") String id) {
-        checkNotNull(id);
+        log.trace(String.format(MESSAGE, "DELETE " + id));
 
-        OpenstackFloatingIpService floatingIpService =
-                getService(OpenstackFloatingIpService.class);
-        floatingIpService.deleteFloatingIp(id);
-
-        log.debug("REST API DELETE floatingip is called {}", id);
-        return Response.noContent().build();
+        adminService.removeFloatingIp(id);
+        return noContent().build();
     }
 
+    private NeutronFloatingIP readFloatingIp(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronFloatingIP.class)
+                    .readerFor(NeutronFloatingIP.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+    }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
index c547b2e..6428e60 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackNetworkWebResource.java
@@ -15,7 +15,12 @@
  */
 package org.onosproject.openstacknetworking.web;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -24,41 +29,106 @@
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
 
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
+
 /**
  * Handles REST API call of Neutron ML2 plugin.
  */
 @Path("networks")
 public class OpenstackNetworkWebResource extends AbstractWebResource {
+    protected final Logger log = LoggerFactory.getLogger(getClass());
 
-    private final Logger log = LoggerFactory.getLogger(getClass());
+    private static final String MESSAGE = "Received networks %s request";
+    private static final String NETWORKS = "networks";
 
+    private final OpenstackNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(OpenstackNetworkAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a network from the JSON input stream.
+     *
+     * @param input network JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated network already exists
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createNetwork(InputStream input) {
-        log.debug("REST API networks is called {}", input.toString());
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronNetwork net = readNetwork(input);
+        adminService.createNetwork(net);
+
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(NETWORKS)
+                .path(net.getId());
+
+        return created(locationBuilder.build()).build();
     }
 
+    /**
+     * Updates the network with the specified identifier.
+     *
+     * @param id network identifier
+     * @param input network JSON input stream
+     * @return 200 OK with the updated network, 400 BAD_REQUEST if the requested
+     * network does not exist
+     */
     @PUT
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response updateNetwork(InputStream input) {
-        log.debug("REST API networks is called {}", input.toString());
-        return Response.status(Response.Status.OK).build();
+    public Response updateNetwork(@PathParam("id") String id, InputStream input) {
+        log.trace(String.format(MESSAGE, "UPDATE " + id));
+
+        final NeutronNetwork net = readNetwork(input);
+        adminService.updateNetwork(net);
+
+        return status(Response.Status.OK).build();
     }
 
+    /**
+     * Removes the service network.
+     *
+     * @param id network identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the network does not exist
+     */
     @DELETE
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
-    public Response deleteNetwork(InputStream input) {
-        log.debug("REST API networks is called {}", input.toString());
-        return Response.noContent().build();
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deleteNetwork(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "DELETE " + id));
+
+        adminService.removeNetwork(id);
+        return noContent().build();
+    }
+
+    private NeutronNetwork readNetwork(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronNetwork.class)
+                    .readerFor(NeutronNetwork.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
     }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
index 7683b0c..c5bb33c 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackPortWebResource.java
@@ -15,12 +15,12 @@
  */
 package org.onosproject.openstacknetworking.web;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.web.OpenstackPortCodec;
-import org.onosproject.openstacknetworking.api.OpenstackSecurityGroupService;
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -31,60 +31,103 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
-import java.io.IOException;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
 
 /**
  * Handles Rest API call from Neutron ML2 plugin.
  */
 @Path("ports")
 public class OpenstackPortWebResource extends AbstractWebResource {
-    private final Logger log = LoggerFactory.getLogger(getClass());
+    protected final Logger log = LoggerFactory.getLogger(getClass());
 
-    private static final OpenstackPortCodec PORT_CODEC
-            = new OpenstackPortCodec();
+    private static final String MESSAGE = "Received ports %s request";
+    private static final String PORTS = "ports";
 
+    private final OpenstackNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(OpenstackNetworkAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a port from the JSON input stream.
+     *
+     * @param input port JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated port already exists
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createPorts(InputStream input) {
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronPort port = readPort(input);
+        adminService.createPort(port);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(PORTS)
+                .path(port.getId());
+
+        return created(locationBuilder.build()).build();
     }
 
-    @Path("{portUUID}")
-    @DELETE
-    @Consumes(MediaType.APPLICATION_JSON)
-    public Response deletePorts(@PathParam("portUUID") String id) {
-        return Response.noContent().build();
-    }
-
+    /**
+     * Updates the port with the specified identifier.
+     *
+     * @param id    port identifier
+     * @param input port JSON input stream
+     * @return 200 OK with the updated port, 400 BAD_REQUEST if the requested
+     * port does not exist
+     */
     @PUT
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response updatePorts(@PathParam("id") String id, InputStream input) {
-        checkNotNull(input);
-        checkNotNull(id);
+    public Response updatePort(@PathParam("id") String id, InputStream input) {
+        log.trace(String.format(MESSAGE, "UPDATE " + id));
 
+        final NeutronPort port = readPort(input);
+        adminService.updatePort(port);
+
+        return status(Response.Status.OK).build();
+    }
+
+    /**
+     * Removes the port with the given id.
+     *
+     * @param id port identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the port does not exist
+     */
+    @DELETE
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response deletePorts(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "DELETE " + id));
+
+        adminService.removePort(id);
+        return noContent().build();
+    }
+
+    private NeutronPort readPort(InputStream input) {
         try {
-            ObjectMapper mapper = new ObjectMapper();
-            ObjectNode portNode = (ObjectNode) mapper.readTree(input);
-            OpenstackPort osPort = PORT_CODEC.decode(portNode, this);
-
-            OpenstackSecurityGroupService sgService
-                    = getService(OpenstackSecurityGroupService.class);
-            sgService.updateSecurityGroup(osPort);
-
-            return Response.status(Response.Status.OK).build();
-        } catch (IOException e) {
-            log.error("UpdatePort post process failed due to {}", e.getMessage());
-
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(e.getMessage())
-                    .build();
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronPort.class)
+                    .readerFor(NeutronPort.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
         }
     }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
index ca9f3ab..1897a3b 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/web/OpenstackSubnetWebResource.java
@@ -18,7 +18,13 @@
 /**
  * Handles Rest API call from Neutron ML2 plugin.
  */
+
+import com.fasterxml.jackson.databind.JsonNode;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.openstack.networking.domain.NeutronSubnet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -29,36 +35,101 @@
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
 
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
+
 @Path("subnets")
 public class OpenstackSubnetWebResource extends AbstractWebResource {
-    protected static final Logger log = LoggerFactory
-            .getLogger(OpenstackSubnetWebResource.class);
+    protected final Logger log = LoggerFactory.getLogger(getClass());
 
+    private static final String MESSAGE = "Received subnets %s request";
+    private static final String SUBNETS = "subnets";
+
+    private final OpenstackNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(OpenstackNetworkAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a subnet from the JSON input stream.
+     *
+     * @param input subnet JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated subnet already exists
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createSubnet(InputStream input) {
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronSubnet subnet = readSubnet(input);
+        adminService.createSubnet(subnet);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(SUBNETS)
+                .path(subnet.getId());
+
+        // TODO fix networking-onos to send Network UPDATE when subnet created
+        return created(locationBuilder.build()).build();
     }
 
-
+    /**
+     * Updates the subnet with the specified identifier.
+     *
+     * @param id    subnet identifier
+     * @param input subnet JSON input stream
+     * @return 200 OK with the updated subnet, 400 BAD_REQUEST if the requested
+     * subnet does not exist
+     */
     @PUT
-    @Path("{subnetId}")
+    @Path("{id}")
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
-    public Response updateSubnet(@PathParam("subnetId") String id,
-                                 final InputStream input) {
-        return Response.status(Response.Status.OK).build();
+    public Response updateSubnet(@PathParam("id") String id, InputStream input) {
+        log.trace(String.format(MESSAGE, "UPDATE " + id));
+
+        final NeutronSubnet subnet = readSubnet(input);
+        adminService.updateSubnet(subnet);
+
+        return status(Response.Status.OK).build();
     }
 
+    /**
+     * Removes the subnet.
+     *
+     * @param id subnet identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the subnet does not exist
+     */
     @DELETE
-    @Path("{subnetId}")
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response deleteSubnet(@PathParam("subnetId") String id) {
-        return Response.noContent().build();
+    public Response deleteSubnet(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "DELETE " + id));
+
+        adminService.removeSubnet(id);
+        return noContent().build();
+    }
+
+    private NeutronSubnet readSubnet(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronSubnet.class)
+                    .readerFor(NeutronSubnet.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
     }
 }
diff --git a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 93076d8..e14985d 100644
--- a/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/openstacknetworking/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -1,5 +1,5 @@
 <!--
-~ Copyright 2015-present Open Networking Laboratory
+~ Copyright 2017-present Open Networking Laboratory
 ~
 ~ Licensed under the Apache License, Version 2.0 (the "License");
 ~ you may not use this file except in compliance with the License.
@@ -16,10 +16,16 @@
 <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
     <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
         <command>
-            <action class="org.onosproject.openstacknetworking.cli.OpenstackInstancePurgeFlowsCommand"/>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackNetworkListCommand"/>
         </command>
         <command>
-            <action class="org.onosproject.openstacknetworking.cli.OpenstackInstanceReInstallFlowCommand"/>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackPortListCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackRouterListCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.openstacknetworking.cli.OpenstackFloatingIpListCommand"/>
         </command>
     </command-bundle>
 </blueprint>