ONOS-6742 Refactored OpenstackNode

- Removed gateway node uplink interface configuration steps
- Added checking group states
- Refactored interface, store, manager and handler

Change-Id: I9149edbec6481b15377848c8f24bdc5c6c73adc4
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncRulesCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncRulesCommand.java
index 30b040c..7336d92 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncRulesCommand.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackSyncRulesCommand.java
@@ -17,7 +17,10 @@
 
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 
 /**
  * Re-installs flow rules for OpenStack networking.
@@ -30,12 +33,16 @@
     protected void execute() {
         // All handlers in this application reacts the node complete event and
         // tries to re-configure flow rules for the complete node.
-        OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
-        if (nodeService == null) {
+        OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+        OpenstackNodeAdminService osNodeAdminService = AbstractShellCommand.get(OpenstackNodeAdminService.class);
+        if (osNodeService == null) {
             error("Failed to re-install flow rules for OpenStack networking.");
             return;
         }
-        nodeService.completeNodes().forEach(nodeService::processCompleteState);
+        osNodeService.completeNodes().forEach(osNode -> {
+            OpenstackNode updated = osNode.updateState(NodeState.INIT);
+            osNodeAdminService.updateNode(updated);
+        });
         print("Successfully requested re-installing flow rules.");
     }
 }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java
index 4f7ead7..8b93115 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackFlowRuleManager.java
@@ -36,10 +36,10 @@
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.openstacknetworking.api.Constants;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.slf4j.Logger;
 
 import java.util.concurrent.ExecutorService;
@@ -73,7 +73,7 @@
 
     private final ExecutorService deviceEventExecutor =
             Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
-    private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
+    private final OpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
 
     private ApplicationId appId;
 
@@ -228,25 +228,25 @@
             // TODO check leadership of the node and make only the leader process
 
             switch (event.type()) {
-                case COMPLETE:
+                case OPENSTACK_NODE_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:
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                case OPENSTACK_NODE_REMOVED:
+                case OPENSTACK_NODE_INCOMPLETE:
                 default:
+                    // do nothing
                     break;
             }
         }
 
         private void processCompleteNode(OpenstackNode osNode) {
-            if (osNode.type().equals(OpenstackNodeService.NodeType.COMPUTE)) {
-                initializePipeline(osNode.intBridge());
+            if (osNode.type().equals(OpenstackNode.NodeType.COMPUTE)) {
+                initializePipeline(osNode.intgBridge());
             }
         }
     }
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 f99d2ae..fce18a0 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
@@ -25,6 +25,7 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.packet.DefaultOutboundPacket;
@@ -34,15 +35,19 @@
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
 import java.util.Objects;
+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.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -68,7 +73,7 @@
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
 
-    private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+    private final PacketProcessor packetProcessor = new InternalPacketProcessor();
 
     @Activate
     protected void activate() {
@@ -123,8 +128,13 @@
         public void process(PacketContext context) {
             if (context.isHandled()) {
                 return;
-            } else if (!osNodeService.gatewayDeviceIds().contains(
-                    context.inPacket().receivedFrom().deviceId())) {
+            }
+
+            Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
+                    .stream().map(OpenstackNode::intgBridge)
+                    .collect(Collectors.toSet());
+
+            if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
                 // return if the packet is not from gateway nodes
                 return;
             }
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
index f1fdf29..50110a2 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -44,9 +44,10 @@
 import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
 import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
 import org.onosproject.openstacknetworking.api.OpenstackRouterService;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.openstack4j.model.network.NetFloatingIP;
 import org.openstack4j.model.network.Network;
 import org.openstack4j.model.network.NetworkType;
@@ -55,7 +56,6 @@
 import org.slf4j.LoggerFactory;
 
 import java.util.Objects;
-import java.util.Optional;
 import java.util.concurrent.ExecutorService;
 
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
@@ -65,7 +65,7 @@
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_EXTERNAL;
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_FLOATING_INTERNAL;
 import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 
 /**
  * Handles OpenStack floating IP events.
@@ -107,7 +107,7 @@
 
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
-    private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpLisener();
+    private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpListener();
     private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
 
     private ApplicationId appId;
@@ -158,11 +158,21 @@
 
     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;
+        OpenstackNode cNode = osNodeService.node(instPort.deviceId());
+        if (cNode == null) {
+            final String error = String.format("Cannot find openstack node for device %s",
+                    instPort.deviceId());
+            throw new IllegalStateException(error);
+        }
+        if (osNet.getNetworkType() == NetworkType.VXLAN && cNode.dataIp() == null) {
+            final String error = String.format(ERR_FLOW +
+                    "VXLAN mode is not ready for %s", floatingIp, cNode.hostname());
+            throw new IllegalStateException(error);
+        }
+        if (osNet.getNetworkType() == NetworkType.VLAN && cNode.vlanIntf() == null) {
+            final String error = String.format(ERR_FLOW +
+                    "VLAN mode is not ready for %s", floatingIp, cNode.hostname());
+            throw new IllegalStateException(error);
         }
 
         IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
@@ -171,7 +181,7 @@
                 .matchIPDst(floating.toIpPrefix())
                 .build();
 
-        osNodeService.gatewayDeviceIds().forEach(gnodeId -> {
+        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
             TrafficTreatment.Builder externalBuilder = DefaultTrafficTreatment.builder()
                     .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
                     .setEthDst(instPort.macAddress())
@@ -182,37 +192,36 @@
                     externalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
                             .extension(buildExtension(
                                     deviceService,
-                                    gnodeId,
-                                    dataIp.get().getIp4Address()),
-                                    gnodeId)
-                            .setOutput(osNodeService.tunnelPort(gnodeId).get());
+                                    gNode.intgBridge(),
+                                    cNode.dataIp().getIp4Address()),
+                                    gNode.intgBridge())
+                            .setOutput(gNode.tunnelPortNum());
                     break;
                 case VLAN:
                     externalBuilder.pushVlan()
                             .setVlanId(VlanId.vlanId(osNet.getProviderSegID()))
-                            .setOutput(osNodeService.vlanPort(gnodeId).get());
+                            .setOutput(gNode.vlanPortNum());
                     break;
                 default:
-                    final String error = String.format(
-                            ERR_UNSUPPORTED_NET_TYPE + "%s",
-                            osNet.getNetworkType().toString());
+                    final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
+                            osNet.getNetworkType());
                     throw new IllegalStateException(error);
             }
 
             osFlowRuleService.setRule(
                     appId,
-                    gnodeId,
+                    gNode.intgBridge(),
                     externalSelector,
                     externalBuilder.build(),
                     PRIORITY_FLOATING_EXTERNAL,
                     GW_COMMON_TABLE,
                     install);
 
-            // access from one VM to the other via floating IP
+            // access from one VM to the others via floating IP
             TrafficSelector internalSelector = DefaultTrafficSelector.builder()
                     .matchEthType(Ethernet.TYPE_IPV4)
                     .matchIPDst(floating.toIpPrefix())
-                    .matchInPort(osNodeService.tunnelPort(gnodeId).get())
+                    .matchInPort(gNode.tunnelPortNum())
                     .build();
 
             TrafficTreatment.Builder internalBuilder = DefaultTrafficTreatment.builder()
@@ -225,9 +234,9 @@
                     internalBuilder.setTunnelId(Long.valueOf(osNet.getProviderSegID()))
                             .extension(buildExtension(
                                     deviceService,
-                                    gnodeId,
-                                    dataIp.get().getIp4Address()),
-                                    gnodeId)
+                                    gNode.intgBridge(),
+                                    cNode.dataIp().getIp4Address()),
+                                    gNode.intgBridge())
                             .setOutput(PortNumber.IN_PORT);
                     break;
                 case VLAN:
@@ -236,15 +245,14 @@
                             .setOutput(PortNumber.IN_PORT);
                     break;
                 default:
-                    final String error = String.format(
-                            ERR_UNSUPPORTED_NET_TYPE + "%s",
-                            osNet.getNetworkType().toString());
+                    final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
+                            osNet.getNetworkType());
                     throw new IllegalStateException(error);
             }
 
             osFlowRuleService.setRule(
                     appId,
-                    gnodeId,
+                    gNode.intgBridge(),
                     internalSelector,
                     internalBuilder.build(),
                     PRIORITY_FLOATING_INTERNAL,
@@ -256,7 +264,6 @@
     private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
                                   InstancePort instPort, boolean install) {
         IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
-
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPSrc(instPort.ipAddress().toIpPrefix());
@@ -269,13 +276,12 @@
                 sBuilder.matchVlanId(VlanId.vlanId(osNet.getProviderSegID()));
                 break;
             default:
-                final String error = String.format(
-                        ERR_UNSUPPORTED_NET_TYPE + "%s",
-                        osNet.getNetworkType().toString());
+                final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
+                        osNet.getNetworkType());
                 throw new IllegalStateException(error);
         }
 
-        osNodeService.gatewayDeviceIds().forEach(gnodeId -> {
+        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
             TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
                     .setIpSrc(floating.getIp4Address())
                     .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
@@ -287,16 +293,16 @@
 
             osFlowRuleService.setRule(
                     appId,
-                    gnodeId,
+                    gNode.intgBridge(),
                     sBuilder.build(),
-                    tBuilder.setOutput(osNodeService.externalPort(gnodeId).get()).build(),
+                    tBuilder.setOutput(gNode.patchPortNum()).build(),
                     PRIORITY_FLOATING_EXTERNAL,
                     GW_COMMON_TABLE,
                     install);
         });
     }
 
-    private class InternalFloatingIpLisener implements OpenstackRouterListener {
+    private class InternalFloatingIpListener implements OpenstackRouterListener {
 
         @Override
         public boolean isRelevant(OpenstackRouterEvent event) {
@@ -401,7 +407,7 @@
         public void event(OpenstackNodeEvent event) {
 
             switch (event.type()) {
-                case COMPLETE:
+                case OPENSTACK_NODE_COMPLETE:
                     eventExecutor.execute(() -> {
                         for (NetFloatingIP fip : osRouterService.floatingIps()) {
                             if (Strings.isNullOrEmpty(fip.getPortId())) {
@@ -416,10 +422,12 @@
                         }
                     });
                     break;
-                case INIT:
-                case DEVICE_CREATED:
-                case INCOMPLETE:
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                case OPENSTACK_NODE_REMOVED:
+                case OPENSTACK_NODE_INCOMPLETE:
                 default:
+                    // do nothing
                     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
index e0b5ccb..084f1b8 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -26,7 +26,6 @@
 import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.cluster.LeadershipService;
@@ -36,7 +35,6 @@
 import org.onosproject.core.GroupId;
 import org.onosproject.net.DeviceId;
 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;
@@ -47,11 +45,11 @@
 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.openstacknode.OpenstackNodeService.NetworkMode;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNode.NetworkMode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.openstack4j.model.network.ExternalGateway;
 import org.openstack4j.model.network.Network;
 import org.openstack4j.model.network.NetworkType;
@@ -68,16 +66,9 @@
 
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.net.AnnotationKeys.PORT_MAC;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.openstacknetworking.api.Constants.FORWARDING_TABLE;
-import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
-import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_EXTERNAL_ROUTING_RULE;
-import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ICMP_RULE;
-import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_INTERNAL_ROUTING_RULE;
-import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SWITCHING_RULE;
-import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 
 /**
  * Handles OpenStack router events.
@@ -111,9 +102,6 @@
     protected OpenstackRouterService osRouterService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackFlowRuleService osFlowRuleService;
 
     private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
@@ -173,7 +161,6 @@
         if (exGateway != null && exGateway.isEnableSnat()) {
             setSourceNat(osRouterIface, true);
         }
-
         log.info("Connected subnet({}) to {}", osSubnet.getCidr(), osRouter.getName());
     }
 
@@ -193,7 +180,6 @@
         if (exGateway != null && exGateway.isEnableSnat()) {
             setSourceNat(osRouterIface, false);
         }
-
         log.info("Disconnected subnet({}) from {}", osSubnet.getCidr(), osRouter.getName());
     }
 
@@ -201,22 +187,21 @@
         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(), osNet.getProviderSegID(),
-                                IpPrefix.valueOf(osSubnet.getCidr()), osNet.getNetworkType(),
-                                install);
-                });
+        osNodeService.completeNodes(COMPUTE).forEach(cNode -> {
+            setRulesToGateway(cNode, osNet.getProviderSegID(),
+                    IpPrefix.valueOf(osSubnet.getCidr()), osNet.getNetworkType(),
+                    install);
+        });
 
         // take the first outgoing packet to controller for source NAT
-        osNodeService.gatewayDeviceIds()
-                .forEach(gwDeviceId -> setRulesToController(
-                        gwDeviceId,
-                        osNet.getProviderSegID(),
-                        IpPrefix.valueOf(osSubnet.getCidr()),
-                        osNet.getNetworkType(),
-                        install));
+        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
+            setRulesToController(
+                    gNode,
+                    osNet.getProviderSegID(),
+                    IpPrefix.valueOf(osSubnet.getCidr()),
+                    osNet.getNetworkType(),
+                    install);
+        });
 
         final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
         log.info(updateStr + "external access for subnet({})", osSubnet.getCidr());
@@ -232,24 +217,22 @@
         Network network = osNetworkService.network(osSubnet.getNetworkId());
         switch (network.getNetworkType()) {
             case VXLAN:
-                osNodeService.completeNodes().stream()
-                        .filter(osNode -> osNode.type() == COMPUTE)
-                        .filter(osNode -> osNode.dataIp().isPresent())
-                        .forEach(osNode -> setRulesToGatewayWithDstIp(
-                                osNode.intBridge(),
-                                osNodeService.gatewayGroupId(osNode.intBridge(), NetworkMode.VXLAN),
+                osNodeService.completeNodes(COMPUTE).stream()
+                        .filter(cNode -> cNode.dataIp() != null)
+                        .forEach(cNode -> setRulesToGatewayWithDstIp(
+                                cNode,
+                                cNode.gatewayGroupId(NetworkMode.VXLAN),
                                 network.getProviderSegID(),
                                 IpAddress.valueOf(osSubnet.getGateway()),
                                 NetworkMode.VXLAN,
                                 install));
                 break;
             case VLAN:
-                osNodeService.completeNodes().stream()
-                        .filter(osNode -> osNode.type() == COMPUTE)
-                        .filter(osNode -> osNode.vlanPort().isPresent())
-                        .forEach(osNode -> setRulesToGatewayWithDstIp(
-                                osNode.intBridge(),
-                                osNodeService.gatewayGroupId(osNode.intBridge(), NetworkMode.VLAN),
+                osNodeService.completeNodes(COMPUTE).stream()
+                        .filter(cNode -> cNode.vlanPortNum() != null)
+                        .forEach(cNode -> setRulesToGatewayWithDstIp(
+                                cNode,
+                                cNode.gatewayGroupId(NetworkMode.VLAN),
                                 network.getProviderSegID(),
                                 IpAddress.valueOf(osSubnet.getGateway()),
                                 NetworkMode.VLAN,
@@ -263,12 +246,13 @@
         }
 
         IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
-        osNodeService.gatewayDeviceIds()
-                .forEach(gwDeviceId -> setGatewayIcmpRule(
-                        gatewayIp,
-                        gwDeviceId,
-                        install
-                ));
+        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
+            setGatewayIcmpRule(
+                    gatewayIp,
+                    gNode.intgBridge(),
+                    install
+            );
+        });
 
         final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
         log.debug(updateStr + "ICMP to {}", osSubnet.getGateway());
@@ -281,40 +265,38 @@
 
         // 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(),
-                            updatedSegmendId,
-                            updatedSegmendId,
-                            IpPrefix.valueOf(updatedSubnet.getCidr()),
-                            IpPrefix.valueOf(updatedSubnet.getCidr()),
-                            updatedNetwork.getNetworkType(),
-                            install
-                    );
+        osNodeService.completeNodes(COMPUTE).forEach(cNode -> {
+            setInternalRouterRules(
+                    cNode.intgBridge(),
+                    updatedSegmendId,
+                    updatedSegmendId,
+                    IpPrefix.valueOf(updatedSubnet.getCidr()),
+                    IpPrefix.valueOf(updatedSubnet.getCidr()),
+                    updatedNetwork.getNetworkType(),
+                    install
+            );
 
-                    routableSubnets.forEach(subnet -> {
-                        setInternalRouterRules(
-                                osNode.intBridge(),
-                                updatedSegmendId,
-                                getSegmentId(subnet),
-                                IpPrefix.valueOf(updatedSubnet.getCidr()),
-                                IpPrefix.valueOf(subnet.getCidr()),
-                                updatedNetwork.getNetworkType(),
-                                install
-                        );
-                        setInternalRouterRules(
-                                osNode.intBridge(),
-                                getSegmentId(subnet),
-                                updatedSegmendId,
-                                IpPrefix.valueOf(subnet.getCidr()),
-                                IpPrefix.valueOf(updatedSubnet.getCidr()),
-                                updatedNetwork.getNetworkType(),
-                                install
-                        );
-                    });
-                });
+            routableSubnets.forEach(subnet -> {
+                setInternalRouterRules(
+                        cNode.intgBridge(),
+                        updatedSegmendId,
+                        getSegmentId(subnet),
+                        IpPrefix.valueOf(updatedSubnet.getCidr()),
+                        IpPrefix.valueOf(subnet.getCidr()),
+                        updatedNetwork.getNetworkType(),
+                        install
+                );
+                setInternalRouterRules(
+                        cNode.intgBridge(),
+                        getSegmentId(subnet),
+                        updatedSegmendId,
+                        IpPrefix.valueOf(subnet.getCidr()),
+                        IpPrefix.valueOf(updatedSubnet.getCidr()),
+                        updatedNetwork.getNetworkType(),
+                        install
+                );
+            });
+        });
 
 
         final String updateStr = install ? MSG_ENABLED : MSG_DISABLED;
@@ -341,7 +323,7 @@
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
-                .matchIPDst(gatewayIp.toIpPrefix())
+                .matchIPDst(gatewayIp.getIp4Address().toIpPrefix())
                 .build();
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
@@ -368,8 +350,8 @@
                 selector = DefaultTrafficSelector.builder()
                         .matchEthType(Ethernet.TYPE_IPV4)
                         .matchTunnelId(Long.parseLong(srcSegmentId))
-                        .matchIPSrc(srcSubnet)
-                        .matchIPDst(dstSubnet)
+                        .matchIPSrc(srcSubnet.getIp4Prefix())
+                        .matchIPDst(dstSubnet.getIp4Prefix())
                         .build();
 
                 treatment = DefaultTrafficTreatment.builder()
@@ -389,8 +371,8 @@
                 selector = DefaultTrafficSelector.builder()
                         .matchEthType(Ethernet.TYPE_IPV4)
                         .matchTunnelId(Long.parseLong(dstSegmentId))
-                        .matchIPSrc(srcSubnet)
-                        .matchIPDst(dstSubnet)
+                        .matchIPSrc(srcSubnet.getIp4Prefix())
+                        .matchIPDst(dstSubnet.getIp4Prefix())
                         .build();
 
                 treatment = DefaultTrafficTreatment.builder()
@@ -411,8 +393,8 @@
                 selector = DefaultTrafficSelector.builder()
                         .matchEthType(Ethernet.TYPE_IPV4)
                         .matchVlanId(VlanId.vlanId(srcSegmentId))
-                        .matchIPSrc(srcSubnet)
-                        .matchIPDst(dstSubnet)
+                        .matchIPSrc(srcSubnet.getIp4Prefix())
+                        .matchIPDst(dstSubnet.getIp4Prefix())
                         .build();
 
                 treatment = DefaultTrafficTreatment.builder()
@@ -432,8 +414,8 @@
                 selector = DefaultTrafficSelector.builder()
                         .matchEthType(Ethernet.TYPE_IPV4)
                         .matchVlanId(VlanId.vlanId(dstSegmentId))
-                        .matchIPSrc(srcSubnet)
-                        .matchIPDst(dstSubnet)
+                        .matchIPSrc(srcSubnet.getIp4Prefix())
+                        .matchIPDst(dstSubnet.getIp4Prefix())
                         .build();
 
                 treatment = DefaultTrafficTreatment.builder()
@@ -459,26 +441,24 @@
 
     }
 
-    private void setRulesToGateway(DeviceId deviceId, String segmentId, IpPrefix srcSubnet,
+    private void setRulesToGateway(OpenstackNode osNode, String segmentId, IpPrefix srcSubnet,
                                    NetworkType networkType, boolean install) {
         TrafficTreatment treatment;
         GroupId groupId;
 
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(srcSubnet)
+                .matchIPSrc(srcSubnet.getIp4Prefix())
                 .matchEthDst(Constants.DEFAULT_GATEWAY_MAC);
 
         switch (networkType) {
             case VXLAN:
                 sBuilder.matchTunnelId(Long.parseLong(segmentId));
-
-                groupId = osNodeService.gatewayGroupId(deviceId, NetworkMode.VXLAN);
+                groupId = osNode.gatewayGroupId(NetworkMode.VXLAN);
                 break;
             case VLAN:
                 sBuilder.matchVlanId(VlanId.vlanId(segmentId));
-
-                groupId = osNodeService.gatewayGroupId(deviceId, NetworkMode.VLAN);
+                groupId = osNode.gatewayGroupId(NetworkMode.VLAN);
                 break;
             default:
                 final String error = String.format(
@@ -493,7 +473,7 @@
 
         osFlowRuleService.setRule(
                 appId,
-                deviceId,
+                osNode.intgBridge(),
                 sBuilder.build(),
                 treatment,
                 PRIORITY_EXTERNAL_ROUTING_RULE,
@@ -501,11 +481,16 @@
                 install);
     }
 
-    private void setRulesToController(DeviceId deviceId, String segmentId, IpPrefix srcSubnet,
+    private void setRulesToController(OpenstackNode osNode, String segmentId,
+                                      IpPrefix srcSubnet,
                                       NetworkType networkType, boolean install) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(srcSubnet);
+                .matchIPSrc(srcSubnet.getIp4Prefix());
+
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setEthDst(Constants.DEFAULT_GATEWAY_MAC)
+                .setOutput(PortNumber.CONTROLLER);
 
         switch (networkType) {
             case VXLAN:
@@ -514,27 +499,18 @@
                 break;
             case VLAN:
                 sBuilder.matchVlanId(VlanId.vlanId(segmentId))
-                        .matchEthDst(MacAddress.valueOf(vlanPortMac(deviceId)));
+                        .matchEthDst(osNode.vlanPortMac());
+                tBuilder.popVlan();
                 break;
             default:
-                final String error = String.format(
-                        ERR_UNSUPPORTED_NET_TYPE + "%s",
+                final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
                         networkType.toString());
                 throw new IllegalStateException(error);
         }
 
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                .setEthDst(Constants.DEFAULT_GATEWAY_MAC);
-
-        if (networkType.equals(NetworkType.VLAN)) {
-            tBuilder.popVlan();
-        }
-
-        tBuilder.setOutput(PortNumber.CONTROLLER);
-
         osFlowRuleService.setRule(
                 appId,
-                deviceId,
+                osNode.intgBridge(),
                 sBuilder.build(),
                 tBuilder.build(),
                 PRIORITY_EXTERNAL_ROUTING_RULE,
@@ -542,27 +518,21 @@
                 install);
     }
 
-    private String vlanPortMac(DeviceId deviceId) {
-        return deviceService.getPorts(deviceId).stream()
-                .filter(p -> p.annotations()
-                        .value(PORT_NAME).equals(osNodeService.gatewayNode(deviceId).vlanPort().get()) && p.isEnabled())
-                .findFirst().get().annotations().value(PORT_MAC);
-    }
-
-    private void setRulesToGatewayWithDstIp(DeviceId deviceId, GroupId groupId, String segmentId,
-                                            IpAddress dstIp, NetworkMode networkMode, boolean install) {
+    private void setRulesToGatewayWithDstIp(OpenstackNode osNode, GroupId groupId,
+                                            String segmentId, IpAddress dstIp,
+                                            NetworkMode networkMode, boolean install) {
         TrafficSelector selector;
         if (networkMode.equals(NetworkMode.VXLAN)) {
             selector = DefaultTrafficSelector.builder()
                     .matchEthType(Ethernet.TYPE_IPV4)
                     .matchTunnelId(Long.valueOf(segmentId))
-                    .matchIPDst(dstIp.toIpPrefix())
+                    .matchIPDst(dstIp.getIp4Address().toIpPrefix())
                     .build();
         } else {
             selector = DefaultTrafficSelector.builder()
                     .matchEthType(Ethernet.TYPE_IPV4)
                     .matchVlanId(VlanId.vlanId(segmentId))
-                    .matchIPDst(dstIp.toIpPrefix())
+                    .matchIPDst(dstIp.getIp4Address().toIpPrefix())
                     .build();
         }
 
@@ -572,7 +542,7 @@
 
         osFlowRuleService.setRule(
                 appId,
-                deviceId,
+                osNode.intgBridge(),
                 selector,
                 treatment,
                 PRIORITY_SWITCHING_RULE,
@@ -659,15 +629,16 @@
             OpenstackNode osNode = event.subject();
 
             switch (event.type()) {
-                case COMPLETE:
-                case INCOMPLETE:
+                case OPENSTACK_NODE_COMPLETE:
+                case OPENSTACK_NODE_INCOMPLETE:
                     eventExecutor.execute(() -> {
                         log.info("Reconfigure routers for {}", osNode.hostname());
                         reconfigureRouters();
                     });
                     break;
-                case INIT:
-                case DEVICE_CREATED:
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                case OPENSTACK_NODE_REMOVED:
                 default:
                     break;
             }
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
index ea716db..a6f0a39 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingIcmpHandler.java
@@ -47,10 +47,10 @@
 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.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.openstack4j.model.network.ExternalGateway;
 import org.openstack4j.model.network.IP;
 import org.openstack4j.model.network.Port;
@@ -70,7 +70,7 @@
 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.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
 
@@ -111,8 +111,8 @@
 
     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 PacketProcessor packetProcessor = new InternalPacketProcessor();
+    private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
     private final Map<String, InstancePort> icmpInfoMap = Maps.newHashMap();
 
     private ApplicationId appId;
@@ -121,7 +121,7 @@
     protected void activate() {
         appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
         packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
-        osNodeService.addListener(nodeListener);
+        osNodeService.addListener(osNodeListener);
         requestPacket(appId);
 
         log.info("Started");
@@ -130,7 +130,7 @@
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
-        osNodeService.removeListener(nodeListener);
+        osNodeService.removeListener(osNodeListener);
         eventExecutor.shutdown();
 
         log.info("Stopped");
@@ -142,13 +142,13 @@
                 .matchIPProtocol(IPv4.PROTOCOL_ICMP)
                 .build();
 
-        osNodeService.gatewayDeviceIds().forEach(gateway -> {
+        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
             packetService.requestPackets(
                     icmpSelector,
                     PacketPriority.CONTROL,
                     appId,
-                    Optional.of(gateway));
-            log.debug("Requested ICMP packet to {}", gateway);
+                    Optional.of(gNode.intgBridge()));
+            log.debug("Requested ICMP packet to {}", gNode.intgBridge());
         });
     }
 
@@ -210,9 +210,7 @@
             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()));
@@ -329,8 +327,14 @@
                 .setDestinationMACAddress(DEFAULT_EXTERNAL_ROUTER_MAC)
                 .setPayload(ipPacket);
 
+        OpenstackNode osNode = osNodeService.node(srcDevice);
+        if (osNode == null) {
+            final String error = String.format("Cannot find openstack node for %s",
+                    srcDevice);
+            throw new IllegalStateException(error);
+        }
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(osNodeService.externalPort(srcDevice).get())
+                .setOutput(osNode.patchPortNum())
                 .build();
 
         OutboundPacket packet = new DefaultOutboundPacket(
@@ -379,10 +383,13 @@
 
         @Override
         public void process(PacketContext context) {
+            Set<DeviceId> gateways = osNodeService.completeNodes(GATEWAY)
+                    .stream().map(OpenstackNode::intgBridge)
+                    .collect(Collectors.toSet());
+
             if (context.isHandled()) {
                 return;
-            } else if (!osNodeService.gatewayDeviceIds().contains(
-                    context.inPacket().receivedFrom().deviceId())) {
+            } else if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
                 // return if the packet is not from gateway nodes
                 return;
             }
@@ -406,7 +413,7 @@
         public boolean isRelevant(OpenstackNodeEvent event) {
             // do not proceed without mastership
             OpenstackNode osNode = event.subject();
-            return mastershipService.isLocalMaster(osNode.intBridge());
+            return mastershipService.isLocalMaster(osNode.intgBridge());
         }
 
         @Override
@@ -414,7 +421,7 @@
             OpenstackNode osNode = event.subject();
 
             switch (event.type()) {
-                case COMPLETE:
+                case OPENSTACK_NODE_COMPLETE:
                     if (osNode.type() == GATEWAY) {
                         log.info("GATEWAY node {} detected", osNode.hostname());
                         eventExecutor.execute(() -> {
@@ -422,10 +429,12 @@
                         });
                     }
                     break;
-                case INIT:
-                case DEVICE_CREATED:
-                case INCOMPLETE:
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                case OPENSTACK_NODE_REMOVED:
+                case OPENSTACK_NODE_INCOMPLETE:
                 default:
+                    // do nothing
                     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
index 9d62a34..1300188 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingSnatHandler.java
@@ -47,7 +47,8 @@
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterService;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.onosproject.store.serializers.KryoNamespaces;
 import org.onosproject.store.service.ConsistentMap;
 import org.onosproject.store.service.DistributedSet;
@@ -65,15 +66,14 @@
 
 import java.nio.ByteBuffer;
 import java.util.Objects;
+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.DEFAULT_EXTERNAL_ROUTER_MAC;
-import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC;
-import static org.onosproject.openstacknetworking.api.Constants.GW_COMMON_TABLE;
-import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
-import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_SNAT_RULE;
+import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -122,7 +122,7 @@
 
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
-    private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+    private final PacketProcessor packetProcessor = new InternalPacketProcessor();
 
     private ConsistentMap<Integer, Long> allocatedPortNumMap;
     private DistributedSet<Integer> unUsedPortNumSet;
@@ -273,7 +273,8 @@
                 packetIn);
     }
 
-    private void setDownstreamRules(InstancePort srcInstPort, String segmentId, NetworkType networkType,
+    private void setDownstreamRules(InstancePort srcInstPort, String segmentId,
+                                    NetworkType networkType,
                                     IpAddress externalIp, TpPort patPort,
                                     InboundPacket packetIn) {
         IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
@@ -282,7 +283,7 @@
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPProtocol(iPacket.getProtocol())
-                .matchIPDst(IpPrefix.valueOf(externalIp, 32))
+                .matchIPDst(IpPrefix.valueOf(externalIp.getIp4Address(), 32))
                 .matchIPSrc(IpPrefix.valueOf(iPacket.getDestinationAddress(), 32));
 
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
@@ -323,31 +324,30 @@
                 break;
         }
 
-        osNodeService.gatewayDeviceIds().forEach(deviceId -> {
-            DeviceId srcDeviceId = srcInstPort.deviceId();
+        OpenstackNode srcNode = osNodeService.node(srcInstPort.deviceId());
+        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
             TrafficTreatment.Builder tmpBuilder =
                     DefaultTrafficTreatment.builder(tBuilder.build());
             switch (networkType) {
                 case VXLAN:
                     tmpBuilder.extension(RulePopulatorUtil.buildExtension(
                             deviceService,
-                            deviceId,
-                            osNodeService.dataIp(srcDeviceId).get().getIp4Address()), deviceId)
-                            .setOutput(osNodeService.tunnelPort(deviceId).get());
+                            gNode.intgBridge(),
+                            srcNode.dataIp().getIp4Address()), gNode.intgBridge())
+                            .setOutput(gNode.tunnelPortNum());
                     break;
                 case VLAN:
-                    tmpBuilder.setOutput(osNodeService.vlanPort(deviceId).get());
+                    tmpBuilder.setOutput(gNode.vlanPortNum());
                     break;
                 default:
-                    final String error = String.format(
-                            ERR_UNSUPPORTED_NET_TYPE + "%s",
+                    final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
                             networkType.toString());
                     throw new IllegalStateException(error);
             }
 
             osFlowRuleService.setRule(
                     appId,
-                    deviceId,
+                    gNode.intgBridge(),
                     sBuilder.build(),
                     tmpBuilder.build(),
                     PRIORITY_SNAT_RULE,
@@ -356,7 +356,8 @@
         });
     }
 
-    private void setUpstreamRules(String segmentId, NetworkType networkType, IpAddress externalIp, TpPort patPort,
+    private void setUpstreamRules(String segmentId, NetworkType networkType,
+                                  IpAddress externalIp, TpPort patPort,
                                   InboundPacket packetIn) {
         IPv4 iPacket = (IPv4) packetIn.parsed().getPayload();
 
@@ -377,8 +378,7 @@
                 tBuilder.popVlan();
                 break;
             default:
-                final String error = String.format(
-                        ERR_UNSUPPORTED_NET_TYPE + "%s",
+                final String error = String.format(ERR_UNSUPPORTED_NET_TYPE + "%s",
                         networkType.toString());
                 throw new IllegalStateException(error);
         }
@@ -397,7 +397,6 @@
                         .matchUdpDst(TpPort.tpPort(udpPacket.getDestinationPort()));
                 tBuilder.setUdpSrc(patPort)
                         .setEthDst(DEFAULT_EXTERNAL_ROUTER_MAC);
-
                 break;
             default:
                 log.debug("Unsupported IPv4 protocol {}");
@@ -405,14 +404,14 @@
         }
 
         tBuilder.setIpSrc(externalIp);
-        osNodeService.gatewayDeviceIds().forEach(deviceId -> {
+        osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
             TrafficTreatment.Builder tmpBuilder =
                     DefaultTrafficTreatment.builder(tBuilder.build());
-            tmpBuilder.setOutput(osNodeService.externalPort(deviceId).get());
+            tmpBuilder.setOutput(gNode.patchPortNum());
 
             osFlowRuleService.setRule(
                     appId,
-                    deviceId,
+                    gNode.intgBridge(),
                     sBuilder.build(),
                     tmpBuilder.build(),
                     PRIORITY_SNAT_RULE,
@@ -424,7 +423,6 @@
     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();
@@ -452,9 +450,15 @@
         ethPacketIn.setPayload(iPacket);
         ethPacketIn.resetChecksum();
 
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .setOutput(osNodeService.externalPort(srcDevice).get()).build();
+        OpenstackNode srcNode = osNodeService.node(srcDevice);
+        if (srcNode == null) {
+            final String error = String.format("Cannot find openstack node for %s",
+                    srcDevice);
+            throw new IllegalStateException(error);
+        }
 
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(srcNode.patchPortNum()).build();
         packetService.emit(new DefaultOutboundPacket(
                 srcDevice,
                 treatment,
@@ -465,13 +469,11 @@
         if (unUsedPortNumSet.isEmpty()) {
             clearPortNumMap();
         }
-
         int portNum = findUnusedPortNum();
         if (portNum != 0) {
             unUsedPortNumSet.remove(portNum);
             allocatedPortNumMap.put(portNum, System.currentTimeMillis());
         }
-
         return portNum;
     }
 
@@ -492,10 +494,12 @@
 
         @Override
         public void process(PacketContext context) {
+            Set<DeviceId> gateways = osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY)
+                    .stream().map(OpenstackNode::intgBridge)
+                    .collect(Collectors.toSet());
             if (context.isHandled()) {
                 return;
-            } else if (!osNodeService.gatewayDeviceIds().contains(
-                    context.inPacket().receivedFrom().deviceId())) {
+            } else if (!gateways.contains(context.inPacket().receivedFrom().deviceId())) {
                 // return if the packet is not from gateway nodes
                 return;
             }
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
index 10b1776..0ec6ccc 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
@@ -38,7 +38,8 @@
 import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.openstack4j.model.network.Network;
 import org.slf4j.Logger;
 
@@ -53,7 +54,7 @@
 import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_TUNNEL_TAG_RULE;
 import static org.onosproject.openstacknetworking.api.Constants.SRC_VNI_TABLE;
 import static org.onosproject.openstacknetworking.impl.RulePopulatorUtil.buildExtension;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
 import static org.slf4j.LoggerFactory.getLogger;
 
 
@@ -148,22 +149,27 @@
                 install);
 
         // switching rules for the instPorts in the remote node
-        osNodeService.completeNodes().stream()
-                .filter(osNode -> osNode.type() == COMPUTE)
-                .filter(osNode -> !osNode.intBridge().equals(instPort.deviceId()))
-                .forEach(osNode -> {
+        OpenstackNode localNode = osNodeService.node(instPort.deviceId());
+        if (localNode == null) {
+            final String error = String.format("Cannot find openstack node for %s",
+                    instPort.deviceId());
+            throw new IllegalStateException(error);
+        }
+        osNodeService.completeNodes(COMPUTE).stream()
+                .filter(remoteNode -> !remoteNode.intgBridge().equals(localNode.intgBridge()))
+                .forEach(remoteNode -> {
                     TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
                             .extension(buildExtension(
                                     deviceService,
-                                    osNode.intBridge(),
-                                    osNodeService.dataIp(instPort.deviceId()).get().getIp4Address()),
-                                    osNode.intBridge())
-                            .setOutput(osNodeService.tunnelPort(osNode.intBridge()).get())
+                                    remoteNode.intgBridge(),
+                                    localNode.dataIp().getIp4Address()),
+                                    remoteNode.intgBridge())
+                            .setOutput(remoteNode.tunnelPortNum())
                             .build();
 
                     osFlowRuleService.setRule(
                             appId,
-                            osNode.intBridge(),
+                            remoteNode.intgBridge(),
                             selector,
                             treatmentToRemote,
                             PRIORITY_SWITCHING_RULE,
@@ -196,25 +202,23 @@
                 install);
 
         // switching rules for the instPorts in the remote node
-        osNodeService.completeNodes().stream()
-                .filter(osNode -> osNode.type() == COMPUTE)
-                .filter(osNode -> !osNode.intBridge().equals(instPort.deviceId()))
-                .filter(osNode -> osNode.vlanPort().isPresent())
-                .forEach(osNode -> {
+        osNodeService.completeNodes(COMPUTE).stream()
+                .filter(remoteNode -> !remoteNode.intgBridge().equals(instPort.deviceId()) &&
+                        remoteNode.vlanIntf() != null)
+                .forEach(remoteNode -> {
                     TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
-                                    .setOutput(osNodeService.vlanPort(osNode.intBridge()).get())
+                                    .setOutput(remoteNode.vlanPortNum())
                                     .build();
 
                     osFlowRuleService.setRule(
                             appId,
-                            osNode.intBridge(),
+                            remoteNode.intgBridge(),
                             selector,
                             treatmentToRemote,
                             PRIORITY_SWITCHING_RULE,
                             FORWARDING_TABLE,
                             install);
                 });
-
     }
 
     private void setTunnelTagFlowRules(InstancePort instPort, boolean install) {
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
index 5f776b3..c5de792 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
@@ -47,10 +47,10 @@
 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.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 import org.openstack4j.model.network.Network;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -260,24 +260,25 @@
             // TODO check leadership of the node and make only the leader process
 
             switch (event.type()) {
-                case COMPLETE:
+                case OPENSTACK_NODE_COMPLETE:
                     deviceEventExecutor.execute(() -> {
                         log.info("COMPLETE node {} is detected", osNode.hostname());
                         processCompleteNode(event.subject());
                     });
                     break;
-                case INCOMPLETE:
+                case OPENSTACK_NODE_INCOMPLETE:
                     log.warn("{} is changed to INCOMPLETE state", osNode);
                     break;
-                case INIT:
-                case DEVICE_CREATED:
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                case OPENSTACK_NODE_REMOVED:
                 default:
                     break;
             }
         }
 
         private void processCompleteNode(OpenstackNode osNode) {
-            deviceService.getPorts(osNode.intBridge()).stream()
+            deviceService.getPorts(osNode.intgBridge()).stream()
                     .filter(port -> port.annotations().value(PORT_NAME)
                             .startsWith(PORT_NAME_PREFIX_VM) &&
                             port.isEnabled())
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/ConnectionHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/ConnectionHandler.java
deleted file mode 100644
index b355fc6..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/ConnectionHandler.java
+++ /dev/null
@@ -1,35 +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.openstacknode;
-
-/**
- * Entity capable of handling a subject connected and disconnected situation.
- */
-public interface ConnectionHandler<T> {
-    /**
-     * Processes the connected subject.
-     *
-     * @param subject subject
-     */
-    void connected(T subject);
-
-    /**
-     * Processes the disconnected subject.
-     *
-     * @param subject subject.
-     */
-    void disconnected(T subject);
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java
deleted file mode 100644
index 0ff91a9..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNode.java
+++ /dev/null
@@ -1,434 +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.openstacknode;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Strings;
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.DeviceId;
-import org.onosproject.openstacknode.OpenstackNodeEvent.NodeState;
-import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
-
-import java.util.Comparator;
-import java.util.Objects;
-import java.util.Optional;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.openstacknode.Constants.PATCH_INTG_BRIDGE;
-import static org.onosproject.openstacknode.OpenstackNodeEvent.NodeState.INIT;
-
-/**
- * Representation of a compute/gateway node for OpenstackSwitching/Routing service.
- */
-public final class OpenstackNode {
-
-    private final String hostname;
-    private final NodeType type;
-    private final IpAddress managementIp;
-    private final Optional<IpAddress> dataIp;
-    private final DeviceId integrationBridge;
-    private final Optional<DeviceId> routerBridge;
-    private final Optional<String> uplink;
-    // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
-    private final Optional<IpAddress> routerController;
-    private final Optional<String> vlanPort;
-    private final NodeState state;
-
-    public static final Comparator<OpenstackNode> OPENSTACK_NODE_COMPARATOR =
-            (node1, node2) -> node1.hostname().compareTo(node2.hostname());
-
-    private OpenstackNode(String hostname,
-                          NodeType type,
-                          IpAddress managementIp,
-                          Optional<IpAddress> dataIp,
-                          DeviceId integrationBridge,
-                          Optional<DeviceId> routerBridge,
-                          Optional<String> uplink,
-                          Optional<IpAddress> routerController,
-                          Optional<String> vlanPort,
-                          NodeState state) {
-        this.hostname = hostname;
-        this.type = type;
-        this.managementIp = managementIp;
-        this.dataIp = dataIp;
-        this.integrationBridge = integrationBridge;
-        this.routerBridge = routerBridge;
-        this.uplink = uplink;
-        this.routerController = routerController;
-        this.vlanPort = vlanPort;
-        this.state = state;
-    }
-
-    /**
-     * Returns OpenStack node with new state.
-     *
-     * @param node openstack node
-     * @param state openstack node init state
-     * @return openstack node
-     */
-    public static OpenstackNode getUpdatedNode(OpenstackNode node, NodeState state) {
-        return new OpenstackNode(node.hostname,
-                node.type,
-                node.managementIp,
-                node.dataIp,
-                node.integrationBridge,
-                node.routerBridge,
-                node.uplink,
-                node.routerController,
-                node.vlanPort,
-                state);
-    }
-
-    /**
-     * Returns hostname of the node.
-     *
-     * @return hostname
-     */
-    public String hostname() {
-        return hostname;
-    }
-
-    /**
-     * Returns the type of the node.
-     *
-     * @return node type
-     */
-    public NodeType type() {
-        return type;
-    }
-
-    /**
-     * Returns the management network IP address of the node.
-     *
-     * @return management network ip address
-     */
-    public IpAddress managementIp() {
-        return managementIp;
-    }
-
-    /**
-     * Returns the data network IP address of the node.
-     *
-     * @return data network ip address; or empty value
-     */
-    public Optional<IpAddress> dataIp() {
-        return dataIp;
-    }
-
-    /**
-     * Returns the integration bridge device ID.
-     *
-     * @return device id
-     */
-    public DeviceId intBridge() {
-        return integrationBridge;
-    }
-
-    /**
-     * Returns the router bridge device ID.
-     * It returns valid value only if the node type is GATEWAY.
-     *
-     * @return device id; or empty device id
-     */
-    public Optional<DeviceId> routerBridge() {
-        return routerBridge;
-    }
-
-    /**
-     * Returns the router bridge controller.
-     * It returns valid value only if the node type is GATEWAY.
-     *
-     * @return device id; or empty value
-     */
-    // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
-    public Optional<IpAddress> routerController() {
-        return routerController;
-    }
-
-    /**
-     * Returns the uplink interface name.
-     * It returns valid value only if the node type is GATEWAY.
-     *
-     * @return uplink interface name; or empty value
-     */
-    public Optional<String> uplink() {
-        return uplink;
-    }
-
-    /**
-     * Returns the vlan interface name.
-     *
-     * @return vlan interface name; or empty value
-     */
-    public Optional<String> vlanPort() {
-        return vlanPort;
-    }
-
-    /**
-     * Returns the init state of the node.
-     *
-     * @return init state
-     */
-    public NodeState state() {
-        return state;
-    }
-
-    /**
-     * Returns the device ID of the OVSDB session of the node.
-     *
-     * @return device id
-     */
-    public DeviceId ovsdbId() {
-        return DeviceId.deviceId("ovsdb:" + managementIp.toString());
-    }
-
-    /**
-     * Returns the name of the port connected to the external network.
-     * It returns valid value only if the node is gateway node.
-     *
-     * @return external port name
-     */
-    public Optional<String> externalPortName() {
-        if (type == NodeType.GATEWAY) {
-            return Optional.of(PATCH_INTG_BRIDGE);
-        } else {
-            return Optional.empty();
-        }
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (obj instanceof OpenstackNode) {
-            OpenstackNode that = (OpenstackNode) obj;
-            if (Objects.equals(hostname, that.hostname) &&
-                    Objects.equals(type, that.type) &&
-                    Objects.equals(managementIp, that.managementIp) &&
-                    Objects.equals(dataIp, that.dataIp) &&
-                    Objects.equals(integrationBridge, that.integrationBridge) &&
-                    Objects.equals(routerBridge, that.routerBridge) &&
-                    Objects.equals(uplink, that.uplink) &&
-                    Objects.equals(routerController, that.routerController) &&
-                    Objects.equals(vlanPort, that.vlanPort)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(hostname,
-                type,
-                managementIp,
-                dataIp,
-                integrationBridge,
-                routerBridge,
-                uplink,
-                routerController,
-                vlanPort);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("hostname", hostname)
-                .add("type", type)
-                .add("managementIp", managementIp)
-                .add("dataIp", dataIp)
-                .add("integrationBridge", integrationBridge)
-                .add("routerBridge", routerBridge)
-                .add("uplink", uplink)
-                .add("routerController", routerController)
-                .add("vlanport", vlanPort)
-                .add("state", state)
-                .toString();
-    }
-
-    /**
-     * Returns a new builder instance.
-     *
-     * @return openstack node builder
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
-     * Builder of OpenStack node entities.
-     */
-    public static final class Builder {
-        private String hostname;
-        private NodeType type;
-        private IpAddress managementIp;
-        private Optional<IpAddress> dataIp = Optional.empty();
-        private DeviceId integrationBridge;
-        private Optional<DeviceId> routerBridge = Optional.empty();
-        private Optional<String> uplink = Optional.empty();
-        private Optional<String> vlanPort = Optional.empty();
-        // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
-        private Optional<IpAddress> routerController = Optional.empty();
-        private NodeState state = INIT;
-
-        private Builder() {
-        }
-
-        public OpenstackNode build() {
-            checkArgument(!Strings.isNullOrEmpty(hostname));
-            checkNotNull(type);
-            checkNotNull(managementIp);
-            checkNotNull(dataIp);
-            checkNotNull(integrationBridge);
-            checkNotNull(routerBridge);
-            checkNotNull(uplink);
-            checkNotNull(routerController);
-            checkNotNull(vlanPort);
-
-            if (type == NodeType.GATEWAY) {
-                checkArgument(routerBridge.isPresent());
-                checkArgument(uplink.isPresent());
-                checkArgument(routerController.isPresent());
-            }
-
-            return new OpenstackNode(hostname,
-                    type,
-                    managementIp,
-                    dataIp,
-                    integrationBridge,
-                    routerBridge,
-                    uplink,
-                    routerController,
-                    vlanPort,
-                    state);
-        }
-
-        /**
-         * Returns node builder with the hostname.
-         *
-         * @param hostname hostname
-         * @return openstack node builder
-         */
-        public Builder hostname(String hostname) {
-            this.hostname = hostname;
-            return this;
-        }
-
-        /**
-         * Returns node builder with the node type.
-         *
-         * @param type openstack node type
-         * @return openstack node builder
-         */
-        public Builder type(NodeType type) {
-            this.type = type;
-            return this;
-        }
-
-        /**
-         * Returns node builder with the management network IP address.
-         *
-         * @param managementIp management ip address
-         * @return openstack node builder
-         */
-        public Builder managementIp(IpAddress managementIp) {
-            this.managementIp = managementIp;
-            return this;
-        }
-
-        /**
-         * Returns node builder with the data network IP address.
-         *
-         * @param dataIp data network ip address
-         * @return openstack node builder
-         */
-        public Builder dataIp(IpAddress dataIp) {
-            this.dataIp = Optional.ofNullable(dataIp);
-            return this;
-        }
-
-        /**
-         * Returns node builder with the integration bridge ID.
-         *
-         * @param integrationBridge integration bridge device id
-         * @return openstack node builder
-         */
-        public Builder integrationBridge(DeviceId integrationBridge) {
-            this.integrationBridge = integrationBridge;
-            return this;
-        }
-
-        /**
-         * Returns node builder with the router bridge ID.
-         *
-         * @param routerBridge router bridge device ID
-         * @return openstack node builder
-         */
-        public Builder routerBridge(DeviceId routerBridge) {
-            this.routerBridge = Optional.ofNullable(routerBridge);
-            return this;
-        }
-
-        /**
-         * Returns node builder with the uplink interface name.
-         *
-         * @param uplink uplink interface name
-         * @return openstack node builder
-         */
-        public Builder uplink(String uplink) {
-            this.uplink = Optional.ofNullable(uplink);
-            return this;
-        }
-
-        /**
-         * Returns node builder with the router controller.
-         *
-         * @param routerController router contoller
-         * @return openstack node builder
-         */
-        // TODO remove this when we use single ONOS cluster for both openstackNode and vRouter
-        public Builder routerController(IpAddress routerController) {
-            this.routerController = Optional.ofNullable(routerController);
-            return this;
-        }
-
-        /**
-         * Returns node builder with the vlan interface name.
-         *
-         * @param vlanPort vlan interface name
-         * @return openstack node builder
-         */
-        public Builder vlanPort(String vlanPort) {
-            this.vlanPort = Optional.ofNullable(vlanPort);
-            return this;
-        }
-
-        /**
-         * Returns node builder with the init state.
-         *
-         * @param state node init state
-         * @return openstack node builder
-         */
-        public Builder state(NodeState state) {
-            this.state = state;
-            return this;
-        }
-    }
-}
-
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeEvent.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeEvent.java
deleted file mode 100644
index 7e0fb78..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeEvent.java
+++ /dev/null
@@ -1,82 +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.openstacknode;
-
-import org.joda.time.LocalDateTime;
-import org.onosproject.event.AbstractEvent;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Describes OpenStack node init state event.
- */
-public class OpenstackNodeEvent extends AbstractEvent<OpenstackNodeEvent.NodeState, OpenstackNode> {
-
-    public enum NodeState {
-        /**
-         * Indicates the node is newly added.
-         */
-        INIT {
-            @Override
-            public void process(OpenstackNodeService nodeService, OpenstackNode node) {
-                nodeService.processInitState(node);
-            }
-        },
-        /**
-         * Indicates bridge devices are added according to the node state.
-         */
-        DEVICE_CREATED {
-            @Override
-            public void process(OpenstackNodeService nodeService, OpenstackNode node) {
-                nodeService.processDeviceCreatedState(node);
-            }
-        },
-        /**
-         * Indicates all node initialization is done.
-         */
-        COMPLETE {
-            @Override
-            public void process(OpenstackNodeService nodeService, OpenstackNode node) {
-                nodeService.processCompleteState(node);
-            }
-        },
-        /**
-         * Indicates node initialization is not done but unable to proceed to
-         * the next step for some reason.
-         */
-        INCOMPLETE {
-            @Override
-            public void process(OpenstackNodeService nodeService, OpenstackNode node) {
-                nodeService.processIncompleteState(node);
-            }
-        };
-
-        public abstract void process(OpenstackNodeService nodeService, OpenstackNode node);
-    }
-
-    public OpenstackNodeEvent(NodeState state, OpenstackNode node) {
-        super(state, node);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(this)
-                .add("time", new LocalDateTime(time()))
-                .add("state", type())
-                .add("node", subject())
-                .toString();
-    }
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
deleted file mode 100644
index 3eb8095..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
+++ /dev/null
@@ -1,872 +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.openstacknode;
-
-import com.google.common.collect.ImmutableList;
-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.Modified;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.TpPort;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.ControllerNode;
-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.event.ListenerRegistry;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.BridgeConfig;
-import org.onosproject.net.behaviour.BridgeDescription;
-import org.onosproject.net.behaviour.BridgeName;
-import org.onosproject.net.behaviour.ControllerInfo;
-import org.onosproject.net.behaviour.DefaultBridgeDescription;
-import org.onosproject.net.behaviour.DefaultPatchDescription;
-import org.onosproject.net.behaviour.DefaultTunnelDescription;
-import org.onosproject.net.behaviour.InterfaceConfig;
-import org.onosproject.net.behaviour.PatchDescription;
-import org.onosproject.net.behaviour.TunnelDescription;
-import org.onosproject.net.behaviour.TunnelEndPoints;
-import org.onosproject.net.behaviour.TunnelKeys;
-import org.onosproject.net.config.ConfigFactory;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.basics.SubjectFactories;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.GroupService;
-import org.onosproject.openstacknode.OpenstackNodeEvent.NodeState;
-import org.onosproject.ovsdb.controller.OvsdbClientService;
-import org.onosproject.ovsdb.controller.OvsdbController;
-import org.onosproject.ovsdb.controller.OvsdbNodeId;
-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.osgi.service.component.ComponentContext;
-import org.slf4j.Logger;
-
-import java.util.Dictionary;
-import java.util.List;
-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 com.google.common.base.Preconditions.checkArgument;
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.net.Device.Type.SWITCH;
-import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
-import static org.onosproject.openstacknode.Constants.*;
-import static org.onosproject.openstacknode.OpenstackNode.getUpdatedNode;
-import static org.onosproject.openstacknode.OpenstackNodeEvent.NodeState.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Initializes devices in compute/gateway nodes according to there type.
- */
-@Component(immediate = true)
-@Service
-public final class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
-        implements OpenstackNodeService {
-    private final Logger log = getLogger(getClass());
-
-    private static final KryoNamespace.Builder NODE_SERIALIZER = KryoNamespace.newBuilder()
-            .register(KryoNamespaces.API)
-            .register(OpenstackNode.class)
-            .register(NodeType.class)
-            .register(NodeState.class);
-
-    private static final String OVSDB_PORT = "ovsdbPort";
-    private static final int DPID_BEGIN = 3;
-
-    private static final String APP_ID = "org.onosproject.openstacknode";
-
-    private static final Class<OpenstackNodeConfig> CONFIG_CLASS = OpenstackNodeConfig.class;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OvsdbController ovsdbController;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ClusterService clusterService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected StorageService storageService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected ComponentConfigService componentConfigService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected LeadershipService leadershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DriverService driverService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected GroupService groupService;
-
-    @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
-            label = "OVSDB server listen port")
-    private int ovsdbPort = DEFAULT_OVSDB_PORT;
-
-    private final ExecutorService eventExecutor =
-            newSingleThreadScheduledExecutor(groupedThreads("onos/openstack-node", "event-handler", log));
-
-    private final ConfigFactory configFactory =
-            new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
-                    SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "openstacknode") {
-                @Override
-                public OpenstackNodeConfig createConfig() {
-                    return new OpenstackNodeConfig();
-                }
-            };
-
-    private final NetworkConfigListener configListener = new InternalConfigListener();
-    private final DeviceListener deviceListener = new InternalDeviceListener();
-    private final MapEventListener<String, OpenstackNode> nodeStoreListener = new InternalMapListener();
-
-    private final OvsdbHandler ovsdbHandler = new OvsdbHandler();
-    private final BridgeHandler bridgeHandler = new BridgeHandler();
-
-    private ConsistentMap<String, OpenstackNode> nodeStore;
-
-    private SelectGroupHandler selectGroupHandler;
-    private ApplicationId appId;
-    private NodeId localNodeId;
-
-    @Activate
-    protected void activate() {
-        appId = coreService.getAppId(APP_ID);
-
-        localNodeId = clusterService.getLocalNode().id();
-        leadershipService.runForLeadership(appId.name());
-
-        nodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
-                .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
-                .withName("openstack-nodestore")
-                .withApplicationId(appId)
-                .build();
-
-        nodeStore.addListener(nodeStoreListener);
-        deviceService.addListener(deviceListener);
-
-        configRegistry.registerConfigFactory(configFactory);
-        configRegistry.addListener(configListener);
-        componentConfigService.registerProperties(getClass());
-
-        selectGroupHandler = new SelectGroupHandler(groupService, deviceService, driverService, appId);
-
-        readConfiguration();
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        configRegistry.removeListener(configListener);
-        deviceService.removeListener(deviceListener);
-        nodeStore.removeListener(nodeStoreListener);
-
-        componentConfigService.unregisterProperties(getClass(), false);
-        configRegistry.unregisterConfigFactory(configFactory);
-
-        leadershipService.withdraw(appId.name());
-        eventExecutor.shutdown();
-
-        log.info("Stopped");
-    }
-
-    @Modified
-    protected void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
-        if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
-            ovsdbPort = updatedOvsdbPort;
-        }
-
-        log.info("Modified");
-    }
-
-    @Override
-    public void addOrUpdateNode(OpenstackNode node) {
-        nodeStore.computeIf(node.hostname(),
-                v -> v == null || (!v.equals(node) || v.state() != COMPLETE),
-                (k, v) -> getUpdatedNode(node, nodeState(node))
-        );
-    }
-
-    @Override
-    public void deleteNode(OpenstackNode node) {
-        nodeStore.remove(node.hostname());
-        process(new OpenstackNodeEvent(INCOMPLETE, node));
-    }
-
-    @Override
-    public void processInitState(OpenstackNode node) {
-        // make sure there is OVSDB connection
-        if (!isOvsdbConnected(node)) {
-            connectOvsdb(node);
-            return;
-        }
-        process(new OpenstackNodeEvent(INIT, node));
-
-        createBridge(node, INTEGRATION_BRIDGE, node.intBridge());
-        if (node.type().equals(NodeType.GATEWAY)) {
-            createBridge(node, ROUTER_BRIDGE, node.routerBridge().get());
-            // TODO remove this when OVSDB provides port event
-            setNodeState(node, nodeState(node));
-        }
-    }
-
-    @Override
-    public void processDeviceCreatedState(OpenstackNode node) {
-        // make sure there is OVSDB connection
-        if (!isOvsdbConnected(node)) {
-            connectOvsdb(node);
-            return;
-        }
-
-        process(new OpenstackNodeEvent(DEVICE_CREATED, node));
-
-        if (node.dataIp().isPresent()) {
-            createTunnelInterface(node);
-        }
-
-        if (node.vlanPort().isPresent()) {
-            addVlanPort(node);
-        }
-
-        if (node.type().equals(NodeType.GATEWAY)) {
-            createPatchInterface(node);
-            addUplink(node);
-            // TODO remove this when OVSDB provides port event
-            setNodeState(node, nodeState(node));
-        }
-    }
-
-    @Override
-    public void processCompleteState(OpenstackNode node) {
-        process(new OpenstackNodeEvent(COMPLETE, node));
-        switch (node.type()) {
-            case COMPUTE:
-                selectGroupHandler.createGatewayGroup(node, gatewayNodes());
-                break;
-            case GATEWAY:
-                updateGatewayGroup(node, true);
-                break;
-            default:
-                break;
-        }
-        log.info("Finished init {}", node.hostname());
-    }
-
-    @Override
-    public void processIncompleteState(OpenstackNode node) {
-        process(new OpenstackNodeEvent(INCOMPLETE, node));
-        if (node.type().equals(NodeType.GATEWAY)) {
-            updateGatewayGroup(node, false);
-        }
-    }
-
-    @Override
-    public List<OpenstackNode> nodes() {
-        return nodeStore.values().stream().map(Versioned::value).collect(Collectors.toList());
-    }
-
-    @Override
-    public Set<OpenstackNode> completeNodes() {
-        return nodeStore.values().stream().map(Versioned::value)
-                .filter(node -> node.state().equals(COMPLETE))
-                .collect(Collectors.toSet());
-    }
-
-    @Override
-    public Optional<IpAddress> dataIp(DeviceId deviceId) {
-        OpenstackNode node = nodeByDeviceId(deviceId);
-        if (node == null) {
-            log.warn("Failed to get node for {}", deviceId);
-            return Optional.empty();
-        }
-        return node.dataIp();
-    }
-
-    @Override
-    public Optional<PortNumber> tunnelPort(DeviceId deviceId) {
-        return deviceService.getPorts(deviceId).stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL) &&
-                        p.isEnabled())
-                .map(Port::number).findFirst();
-    }
-
-    @Override
-    public Optional<PortNumber> vlanPort(DeviceId intBridgeId) {
-        Optional<String> vlanPortName = nodeByDeviceId(intBridgeId).vlanPort();
-
-        return deviceService.getPorts(intBridgeId).stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(vlanPortName.get()) &&
-                        p.isEnabled())
-                .map(Port::number).findFirst();
-
-    }
-
-    @Override
-    public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
-        OpenstackNode node = nodeByDeviceId(intBridgeId);
-        if (node == null || node.type().equals(NodeType.COMPUTE)) {
-            log.warn("Failed to find router bridge connected to {}", intBridgeId);
-            return Optional.empty();
-        }
-        return node.routerBridge();
-    }
-
-    @Override
-    public Optional<PortNumber> externalPort(DeviceId intBridgeId) {
-        return deviceService.getPorts(intBridgeId).stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(PATCH_INTG_BRIDGE) &&
-                        p.isEnabled())
-                .map(Port::number).findFirst();
-    }
-
-    @Override
-    public OpenstackNode gatewayNode(DeviceId deviceId) {
-        OpenstackNode gatewayNode = nodeByDeviceId(deviceId);
-        if (gatewayNode == null || gatewayNode.type() != NodeType.GATEWAY) {
-            log.warn("Gateway with device ID {} does not exist");
-            return null;
-        }
-        return gatewayNode;
-    }
-
-    @Override
-    public synchronized GroupId gatewayGroupId(DeviceId srcDeviceId, NetworkMode networkMode) {
-        GroupKey groupKey = selectGroupHandler.groupKey(srcDeviceId, networkMode);
-        Group group = groupService.getGroup(srcDeviceId, groupKey);
-
-        if (group == null) {
-            log.info("Created gateway group for {}", srcDeviceId);
-            selectGroupHandler.createGatewayGroup(nodeByDeviceId(srcDeviceId), gatewayNodes());
-
-            return groupService.getGroup(srcDeviceId, selectGroupHandler.groupKey(srcDeviceId, networkMode)).id();
-        } else {
-            return group.id();
-        }
-    }
-
-    @Override
-    public List<OpenstackNode> gatewayNodes() {
-        return nodeStore.values()
-                .stream()
-                .map(Versioned::value)
-                .filter(node -> node.type().equals(NodeType.GATEWAY))
-                .filter(node -> node.state().equals(COMPLETE))
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public List<DeviceId> gatewayDeviceIds() {
-        return gatewayNodes().stream().map(OpenstackNode::intBridge)
-                .collect(Collectors.toList());
-    }
-
-    private void updateGatewayGroup(OpenstackNode gatewayNode, boolean isInsert) {
-        nodeStore.values()
-                .stream()
-                .map(Versioned::value)
-                .filter(node -> node.type().equals(NodeType.COMPUTE))
-                .filter(node -> node.dataIp().isPresent())
-                .filter(node -> node.state().equals(COMPLETE))
-                .forEach(computeNode -> {
-                    selectGroupHandler.updateGatewayGroupBuckets(computeNode,
-                            ImmutableList.of(gatewayNode), NetworkMode.VXLAN, isInsert);
-                    log.trace("Updated gateway group on {} for vxlan mode", computeNode.intBridge());
-                });
-
-        nodeStore.values()
-                .stream()
-                .map(Versioned::value)
-                .filter(node -> node.type().equals(NodeType.COMPUTE))
-                .filter(node -> node.vlanPort().isPresent())
-                .filter(node -> node.state().equals(COMPLETE))
-                .forEach(computeNode -> {
-                    selectGroupHandler.updateGatewayGroupBuckets(computeNode,
-                            ImmutableList.of(gatewayNode), NetworkMode.VLAN, isInsert);
-                    log.trace("Updated gateway group on {} for vlan mode", computeNode.intBridge());
-                });
-
-    }
-
-    private void initNode(OpenstackNode node) {
-        NodeState state = node.state();
-        state.process(this, node);
-        log.debug("Processing node: {} state: {}", node.hostname(), state);
-    }
-
-    private void setNodeState(OpenstackNode node, NodeState newState) {
-        nodeStore.put(node.hostname(), getUpdatedNode(node, newState));
-    }
-
-    private NodeState nodeState(OpenstackNode node) {
-        if (!isOvsdbConnected(node) || !deviceService.isAvailable(node.intBridge()) ||
-                !isBridgeCreated(node.ovsdbId(), INTEGRATION_BRIDGE)) {
-            return INIT;
-        }
-
-        // TODO use device service when we use single ONOS cluster for both openstackNode and vRouter
-        if (node.type().equals(NodeType.GATEWAY) &&
-                !isBridgeCreated(node.ovsdbId(), ROUTER_BRIDGE)) {
-            return INIT;
-        }
-
-        if (node.dataIp().isPresent() && !isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
-            return DEVICE_CREATED;
-        }
-
-        if (node.vlanPort().isPresent() && !isIfaceCreated(node.ovsdbId(), node.vlanPort().get())) {
-            return DEVICE_CREATED;
-        }
-
-        if (node.type().equals(NodeType.GATEWAY) && (
-                !isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE) ||
-                !isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) ||
-                !isIfaceCreated(node.ovsdbId(), node.uplink().get()))) {
-            return DEVICE_CREATED;
-        }
-        return COMPLETE;
-    }
-
-    private boolean isIfaceCreated(DeviceId deviceId, String ifaceName) {
-        Device device = deviceService.getDevice(deviceId);
-        if (device == null || !device.is(BridgeConfig.class)) {
-            return false;
-        }
-
-        BridgeConfig bridgeConfig =  device.as(BridgeConfig.class);
-        return bridgeConfig.getPorts().stream()
-                .anyMatch(port -> port.annotations().value(PORT_NAME).equals(ifaceName));
-    }
-
-    private boolean isBridgeCreated(DeviceId deviceId, String bridgeName) {
-        Device device = deviceService.getDevice(deviceId);
-        if (device == null || !device.is(BridgeConfig.class)) {
-            return false;
-        }
-
-        BridgeConfig bridgeConfig =  device.as(BridgeConfig.class);
-        return bridgeConfig.getBridges().stream()
-                .anyMatch(bridge -> bridge.name().equals(bridgeName));
-    }
-
-    private void createBridge(OpenstackNode node, String bridgeName, DeviceId deviceId) {
-        Device device = deviceService.getDevice(node.ovsdbId());
-        if (device == null || !device.is(BridgeConfig.class)) {
-            log.error("Failed to create integration bridge on {}", node.ovsdbId());
-            return;
-        }
-
-        // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
-        Set<IpAddress> controllerIps;
-        if (bridgeName.equals(ROUTER_BRIDGE)) {
-            controllerIps = Sets.newHashSet(node.routerController().get());
-        } else {
-            controllerIps = clusterService.getNodes().stream()
-                    .map(ControllerNode::ip)
-                    .collect(Collectors.toSet());
-        }
-
-        List<ControllerInfo> controllers = controllerIps.stream()
-                    .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
-                    .collect(Collectors.toList());
-
-        String dpid = deviceId.toString().substring(DPID_BEGIN);
-        BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
-                .name(bridgeName)
-                .failMode(BridgeDescription.FailMode.SECURE)
-                .datapathId(dpid)
-                .disableInBand()
-                .controllers(controllers)
-                .build();
-
-        BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
-        bridgeConfig.addBridge(bridgeDesc);
-    }
-
-    private void createTunnelInterface(OpenstackNode node) {
-        if (isIfaceCreated(node.ovsdbId(), DEFAULT_TUNNEL)) {
-            return;
-        }
-
-        Device device = deviceService.getDevice(node.ovsdbId());
-        if (device == null || !device.is(InterfaceConfig.class)) {
-            log.error("Failed to create tunnel interface on {}", node.ovsdbId());
-            return;
-        }
-
-        TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
-                .deviceId(INTEGRATION_BRIDGE)
-                .ifaceName(DEFAULT_TUNNEL)
-                .type(VXLAN)
-                .remote(TunnelEndPoints.flowTunnelEndpoint())
-                .key(TunnelKeys.flowTunnelKey())
-                .build();
-
-        InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
-        ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
-    }
-
-    private void createPatchInterface(OpenstackNode node) {
-        checkArgument(node.type().equals(NodeType.GATEWAY));
-        if (isIfaceCreated(node.ovsdbId(), PATCH_INTG_BRIDGE) &&
-                isIfaceCreated(node.ovsdbId(), PATCH_ROUT_BRIDGE)) {
-            return;
-        }
-
-        Device device = deviceService.getDevice(node.ovsdbId());
-        if (device == null || !device.is(InterfaceConfig.class)) {
-            log.error("Failed to create patch interfaces on {}", node.hostname());
-            return;
-        }
-
-        PatchDescription patchIntg = DefaultPatchDescription.builder()
-                .deviceId(INTEGRATION_BRIDGE)
-                .ifaceName(PATCH_INTG_BRIDGE)
-                .peer(PATCH_ROUT_BRIDGE)
-                .build();
-
-        PatchDescription patchRout = DefaultPatchDescription.builder()
-                .deviceId(ROUTER_BRIDGE)
-                .ifaceName(PATCH_ROUT_BRIDGE)
-                .peer(PATCH_INTG_BRIDGE)
-                .build();
-
-        InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
-        ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
-        ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
-    }
-
-    private void addUplink(OpenstackNode node) {
-        checkArgument(node.type().equals(NodeType.GATEWAY));
-        if (isIfaceCreated(node.ovsdbId(), node.uplink().get())) {
-            return;
-        }
-
-        Device device = deviceService.getDevice(node.ovsdbId());
-        if (device == null || !device.is(BridgeConfig.class)) {
-            log.error("Failed to add port {} on {}", node.uplink().get(), node.ovsdbId());
-            return;
-        }
-
-        BridgeConfig bridgeConfig =  device.as(BridgeConfig.class);
-        bridgeConfig.addPort(BridgeName.bridgeName(ROUTER_BRIDGE),
-                             node.uplink().get());
-    }
-
-    private void addVlanPort(OpenstackNode node) {
-        if (isIfaceCreated(node.ovsdbId(), node.vlanPort().get())) {
-            return;
-        }
-
-        Device device = deviceService.getDevice(node.ovsdbId());
-        if (device == null || !device.is(BridgeConfig.class)) {
-            log.error("Failed to add port {} on {}", node.vlanPort().get(), node.ovsdbId());
-            return;
-        }
-
-        BridgeConfig bridgeConfig =  device.as(BridgeConfig.class);
-        bridgeConfig.addPort(BridgeName.bridgeName(INTEGRATION_BRIDGE),
-                node.vlanPort().get());
-    }
-
-    private boolean isOvsdbConnected(OpenstackNode node) {
-        OvsdbNodeId ovsdb = new OvsdbNodeId(node.managementIp(), ovsdbPort);
-        OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
-        return deviceService.isAvailable(node.ovsdbId()) &&
-                client != null &&
-                client.isConnected();
-    }
-
-    private void connectOvsdb(OpenstackNode node) {
-        ovsdbController.connect(node.managementIp(), TpPort.tpPort(ovsdbPort));
-    }
-
-    private Set<String> systemIfaces(OpenstackNode node) {
-        Set<String> ifaces = Sets.newHashSet();
-        node.dataIp().ifPresent(ip -> ifaces.add(DEFAULT_TUNNEL));
-        node.vlanPort().ifPresent(ifaces::add);
-        if (node.type().equals(NodeType.GATEWAY)) {
-            ifaces.add(PATCH_INTG_BRIDGE);
-            ifaces.add(PATCH_ROUT_BRIDGE);
-            ifaces.add(node.uplink().get());
-        }
-        return ifaces;
-    }
-
-    private OpenstackNode nodeByDeviceId(DeviceId deviceId) {
-        OpenstackNode node = nodes().stream()
-                .filter(n -> n.intBridge().equals(deviceId))
-                .findFirst().orElseGet(() -> nodes().stream()
-                .filter(n -> n.routerBridge().isPresent())
-                .filter(n -> n.routerBridge().get().equals(deviceId))
-                .findFirst().orElseGet(() -> nodes().stream()
-                                .filter(n -> n.ovsdbId().equals(deviceId))
-                                .findFirst().orElse(null)));
-        return node;
-    }
-
-    private class OvsdbHandler implements ConnectionHandler<Device> {
-
-        @Override
-        public void connected(Device device) {
-            OpenstackNode node = nodes().stream()
-                    .filter(n -> n.ovsdbId().equals(device.id()))
-                    .findFirst()
-                    .orElse(null);
-            if (node != null) {
-                setNodeState(node, nodeState(node));
-            } else {
-                log.debug("{} is detected on unregistered node, ignore it.", device.id());
-            }
-        }
-
-        @Override
-        public void disconnected(Device device) {
-            OpenstackNode node = nodeByDeviceId(device.id());
-            if (node != null) {
-                log.warn("Device {} is disconnected", device.id());
-                setNodeState(node, NodeState.INCOMPLETE);
-            }
-        }
-    }
-
-    private class BridgeHandler implements ConnectionHandler<Device> {
-
-        @Override
-        public void connected(Device device) {
-            OpenstackNode node = nodeByDeviceId(device.id());
-            if (node != null) {
-                setNodeState(node, nodeState(node));
-            } else {
-                log.debug("{} is detected on unregistered node, ignore it.", device.id());
-            }
-        }
-
-        @Override
-        public void disconnected(Device device) {
-            OpenstackNode node = nodeByDeviceId(device.id());
-            if (node != null) {
-                log.warn("Device {} is disconnected", device.id());
-                setNodeState(node, NodeState.INCOMPLETE);
-            }
-        }
-
-        /**
-         * Handles port added situation.
-         * If the added port is tunnel or data plane interface, proceed to the remaining
-         * node initialization. Otherwise, do nothing.
-         *
-         * @param port port
-         */
-        public void portAdded(Port port) {
-            OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
-            String portName = port.annotations().value(PORT_NAME);
-            if (node == null) {
-                log.debug("{} is added to unregistered node, ignore it.", portName);
-                return;
-            }
-
-            log.info("Port {} is added to {}", portName, node.hostname());
-            if (systemIfaces(node).contains(portName)) {
-                setNodeState(node, nodeState(node));
-            }
-        }
-
-        /**
-         * Handles port removed situation.
-         * If the removed port is tunnel or data plane interface, proceed to the remaining
-         * node initialization.Others, do nothing.
-         *
-         * @param port port
-         */
-        public void portRemoved(Port port) {
-            OpenstackNode node = nodeByDeviceId((DeviceId) port.element().id());
-            String portName = port.annotations().value(PORT_NAME);
-
-            if (node == null) {
-                return;
-            }
-
-            log.info("Port {} is removed from {}", portName, node.hostname());
-            if (systemIfaces(node).contains(portName)) {
-                setNodeState(node, NodeState.INCOMPLETE);
-            }
-        }
-    }
-
-    private class InternalDeviceListener implements DeviceListener {
-
-        @Override
-        public void event(DeviceEvent event) {
-
-            NodeId leaderNodeId = leadershipService.getLeader(appId.name());
-            if (!Objects.equals(localNodeId, leaderNodeId)) {
-                // do not allow to proceed without leadership
-                return;
-            }
-
-            Device device = event.subject();
-            ConnectionHandler<Device> handler =
-                    (device.type().equals(SWITCH) ? bridgeHandler : ovsdbHandler);
-
-            switch (event.type()) {
-                // TODO implement OVSDB port event so that we can handle updates on the OVSDB
-                case PORT_ADDED:
-                    eventExecutor.execute(() -> bridgeHandler.portAdded(event.port()));
-                    break;
-                case PORT_UPDATED:
-                    if (!event.port().isEnabled()) {
-                        eventExecutor.execute(() -> bridgeHandler.portRemoved(event.port()));
-                    }
-                    break;
-                case DEVICE_ADDED:
-                case DEVICE_AVAILABILITY_CHANGED:
-                    if (deviceService.isAvailable(device.id())) {
-                        eventExecutor.execute(() -> handler.connected(device));
-                    } else {
-                        eventExecutor.execute(() -> handler.disconnected(device));
-                        log.warn("OpenstackNode with device ID {} is disconnected", device.id());
-                    }
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private void readConfiguration() {
-        OpenstackNodeConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
-        if (config == null) {
-            log.debug("No configuration found");
-            return;
-        }
-
-        Map<String, OpenstackNode> prevNodeMap = Maps.newHashMap(nodeStore.asJavaMap());
-        config.openstackNodes().forEach(node -> {
-            prevNodeMap.remove(node.hostname());
-            addOrUpdateNode(node);
-        });
-        prevNodeMap.values().forEach(this::deleteNode);
-    }
-
-    private class InternalConfigListener implements NetworkConfigListener {
-
-        @Override
-        public void event(NetworkConfigEvent event) {
-            NodeId leaderNodeId = leadershipService.getLeader(appId.name());
-            if (!Objects.equals(localNodeId, leaderNodeId)) {
-                // do not allow to proceed without leadership
-                return;
-            }
-
-            if (!event.configClass().equals(CONFIG_CLASS)) {
-                return;
-            }
-
-            switch (event.type()) {
-                case CONFIG_ADDED:
-                case CONFIG_UPDATED:
-                    eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private class InternalMapListener implements MapEventListener<String, OpenstackNode> {
-
-        @Override
-        public void event(MapEvent<String, OpenstackNode> event) {
-            NodeId leaderNodeId = leadershipService.getLeader(appId.name());
-            if (!Objects.equals(localNodeId, leaderNodeId)) {
-                // do not allow to proceed without leadership
-                return;
-            }
-
-            OpenstackNode oldNode;
-            OpenstackNode newNode;
-
-            switch (event.type()) {
-                case UPDATE:
-                    oldNode = event.oldValue().value();
-                    newNode = event.newValue().value();
-
-                    log.info("Reloaded {}", newNode.hostname());
-                    if (!newNode.equals(oldNode)) {
-                        log.debug("New node: {}", newNode);
-                    }
-                    // performs init procedure even if the node is not changed
-                    // for robustness since it's no harm to run init procedure
-                    // multiple times
-                    eventExecutor.execute(() -> initNode(newNode));
-                    break;
-                case INSERT:
-                    newNode = event.newValue().value();
-                    log.info("Added {}", newNode.hostname());
-                    eventExecutor.execute(() -> initNode(newNode));
-                    break;
-                case REMOVE:
-                    oldNode = event.oldValue().value();
-                    log.info("Removed {}", oldNode.hostname());
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
deleted file mode 100644
index 9600280..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
+++ /dev/null
@@ -1,179 +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.openstacknode;
-
-import org.onlab.packet.IpAddress;
-import org.onosproject.core.GroupId;
-import org.onosproject.event.ListenerService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * Handles the bootstrap request for compute/gateway node.
- */
-public interface OpenstackNodeService
-        extends ListenerService<OpenstackNodeEvent, OpenstackNodeListener> {
-
-    enum NodeType {
-        /**
-         * Compute or Gateway Node.
-         */
-        COMPUTE,
-        GATEWAY
-    }
-
-    enum NetworkMode {
-        /**
-         * VxLAN or VLAN mode.
-         */
-        VXLAN,
-        VLAN
-    }
-
-    /**
-     * Adds or updates a new node to the service.
-     *
-     * @param node openstack node
-     */
-    void addOrUpdateNode(OpenstackNode node);
-
-    /**
-     * Bootstraps node with INIT state.
-     *
-     * @param node openstack node
-     */
-    void processInitState(OpenstackNode node);
-
-    /**
-     * Bootstraps node with DEVICE_CREATED state.
-     *
-     * @param node openstack node
-     */
-    void processDeviceCreatedState(OpenstackNode node);
-
-    /**
-     * Bootstraps node with COMPLETE state.
-     *
-     * @param node openstack node
-     */
-    void processCompleteState(OpenstackNode node);
-
-    /**
-     * Bootstraps node with INCOMPLETE state.
-     *
-     * @param node openstack node
-     */
-    void processIncompleteState(OpenstackNode node);
-
-    /**
-     * Deletes a node from the service.
-     *
-     * @param node openstack node
-     */
-    void deleteNode(OpenstackNode node);
-
-    /**
-     * Returns all nodes known to the service.
-     *
-     * @return list of nodes
-     */
-    List<OpenstackNode> nodes();
-
-    /**
-     * Returns all nodes in complete state.
-     *
-     * @return set of nodes
-     */
-    Set<OpenstackNode> completeNodes();
-
-    /**
-     * Returns data network IP address of a given integration bridge device.
-     *
-     * @param intBridgeId integration bridge device id
-     * @return ip address; empty value otherwise
-     */
-    Optional<IpAddress> dataIp(DeviceId intBridgeId);
-
-    /**
-     * Returns tunnel port number of a given integration bridge device.
-     *
-     * @param intBridgeId integration bridge device id
-     * @return port number; or empty value
-     */
-    Optional<PortNumber> tunnelPort(DeviceId intBridgeId);
-
-    /**
-     * Returns vlan port number of a given integration bridge device.
-     *
-     * @param intBridgeId integration bridge device id
-     * @return port number; or empty value
-     */
-    Optional<PortNumber> vlanPort(DeviceId intBridgeId);
-
-    /**
-     * Returns router bridge device ID connected to a given integration bridge.
-     * It returns valid value only if the node type is GATEWAY.
-     *
-     * @param intBridgeId device id of the integration bridge
-     * @return device id of a router bridge; or empty value
-     */
-    Optional<DeviceId> routerBridge(DeviceId intBridgeId);
-
-    /**
-     * Returns port number connected to the router bridge.
-     * It returns valid value only if the node type is GATEWAY.
-     *
-     * @param intBridgeId integration bridge device id
-     * @return port number; or empty value
-     */
-    Optional<PortNumber> externalPort(DeviceId intBridgeId);
-
-    /**
-     * Returns gateway node with the given device identifier.
-     *
-     * @param deviceId The gateway node deviceId
-     * @return The gateway node information
-     */
-    OpenstackNode gatewayNode(DeviceId deviceId);
-
-    /**
-     * Returns group id for gateway load balance.
-     * If the group does not exist in the supplied source device, creates one.
-     *
-     * @param srcDeviceId source device id
-     * @param networkMode network mode
-     * @return group id
-     */
-    GroupId gatewayGroupId(DeviceId srcDeviceId, NetworkMode networkMode);
-
-    /**
-     * Returns the list of gateway node information with the given device identifier.
-     *
-     * @return The list of gateway node information
-     */
-    List<OpenstackNode> gatewayNodes();
-
-    /**
-     * Returns the list of gateway`s device identifiers.
-     *
-     * @return The list of device identifier]
-     */
-    List<DeviceId> gatewayDeviceIds();
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java
deleted file mode 100644
index 5f8e065..0000000
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * 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.openstacknode;
-
-import com.google.common.collect.Lists;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.MacAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.GroupId;
-import org.onosproject.openstacknode.OpenstackNodeService.NetworkMode;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.driver.DefaultDriverData;
-import org.onosproject.net.driver.DefaultDriverHandler;
-import org.onosproject.net.driver.Driver;
-import org.onosproject.net.driver.DriverHandler;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.ExtensionPropertyException;
-import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flow.instructions.ExtensionTreatmentType;
-import org.onosproject.net.group.DefaultGroupDescription;
-import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.GroupBucket;
-import org.onosproject.net.group.GroupBuckets;
-import org.onosproject.net.group.GroupDescription;
-import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.GroupService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-
-import static org.onosproject.net.AnnotationKeys.PORT_MAC;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.openstacknode.Constants.*;
-import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
-
-/**
- * Handles group generation request from OpenstackNode.
- */
-public class SelectGroupHandler {
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    private static final String TUNNEL_DESTINATION = "tunnelDst";
-    private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
-
-    private final GroupService groupService;
-    private final DeviceService deviceService;
-    private final DriverService driverService;
-    private final ApplicationId appId;
-
-    /**
-     * Default constructor.
-     *
-     * @param targetGroupService group service
-     * @param targetDeviceService device service
-     * @param targetDriverService driver service
-     * @param appId application id for group service
-     */
-    public SelectGroupHandler(GroupService targetGroupService, DeviceService targetDeviceService,
-                              DriverService targetDriverService, ApplicationId appId) {
-        groupService = targetGroupService;
-        deviceService = targetDeviceService;
-        driverService = targetDriverService;
-        this.appId = appId;
-    }
-
-    /**
-     * Creates select type group description according to given deviceId.
-     *
-     * @param computeNode target device id for group description
-     * @param gatewayNodeList gateway node list for bucket action
-     */
-    public void createGatewayGroup(OpenstackNode computeNode, List<OpenstackNode> gatewayNodeList) {
-        List<GroupBucket> bucketList;
-        GroupId groupId;
-
-        if (computeNode.dataIp().isPresent()) {
-            bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, NetworkMode.VXLAN);
-            groupId = groupId(computeNode.intBridge(), NetworkMode.VXLAN);
-
-            GroupDescription groupDescription = new DefaultGroupDescription(
-                    computeNode.intBridge(),
-                    GroupDescription.Type.SELECT,
-                    new GroupBuckets(bucketList),
-                    groupKey(computeNode.intBridge(), NetworkMode.VXLAN),
-                    groupId.id(),
-                    appId);
-
-            groupService.addGroup(groupDescription);
-        }
-
-        if (computeNode.vlanPort().isPresent()) {
-            bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, NetworkMode.VLAN);
-            groupId = groupId(computeNode.intBridge(), NetworkMode.VLAN);
-
-            GroupDescription groupDescription = new DefaultGroupDescription(
-                    computeNode.intBridge(),
-                    GroupDescription.Type.SELECT,
-                    new GroupBuckets(bucketList),
-                    groupKey(computeNode.intBridge(), NetworkMode.VLAN),
-                    groupId.id(),
-                    appId);
-
-            groupService.addGroup(groupDescription);
-        }
-    }
-
-    /**
-     * Returns unique group key with supplied source device ID and network mode as a hash.
-     * @param srcDeviceId source device id
-     * @param networkMode network mode
-     * @return group key
-     */
-    public GroupKey groupKey(DeviceId srcDeviceId, NetworkMode networkMode) {
-        if (networkMode.equals(NetworkMode.VXLAN)) {
-            return new DefaultGroupKey(srcDeviceId.toString().concat(DEFAULT_TUNNEL).getBytes());
-        } else {
-            return new DefaultGroupKey(srcDeviceId.toString().concat(VLAN).getBytes());
-        }
-    }
-
-    private GroupId groupId(DeviceId srcDeviceId, NetworkMode networkMode) {
-        if (networkMode.equals(NetworkMode.VXLAN)) {
-            return new GroupId(srcDeviceId.toString().concat(DEFAULT_TUNNEL).hashCode());
-        } else {
-            return new GroupId(srcDeviceId.toString().concat(VLAN).hashCode());
-        }
-    }
-
-
-    /**
-     * Updates groupBuckets in select type group.
-     *
-     * @param computeNode compute node
-     * @param gatewayNodeList updated gateway node list for bucket action
-     * @param networkMode network mode
-     * @param isInsert update type(add or remove)
-     */
-    public void updateGatewayGroupBuckets(OpenstackNode computeNode,
-                                          List<OpenstackNode> gatewayNodeList,
-                                          NetworkMode networkMode,
-                                          boolean isInsert) {
-        List<GroupBucket> bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, networkMode);
-        GroupKey groupKey = groupKey(computeNode.intBridge(), networkMode);
-        if (groupService.getGroup(computeNode.intBridge(), groupKey) == null) {
-            log.error("There's no group in compute node {}", computeNode.intBridge());
-            return;
-        }
-
-        if (isInsert) {
-            groupService.addBucketsToGroup(
-                    computeNode.intBridge(),
-                    groupKey,
-                    new GroupBuckets(bucketList),
-                    groupKey, appId);
-        } else {
-            groupService.removeBucketsFromGroup(
-                    computeNode.intBridge(),
-                    groupKey,
-                    new GroupBuckets(bucketList),
-                    groupKey, appId);
-        }
-    }
-
-
-
-    private List<GroupBucket> generateBucketsForSelectGroup(OpenstackNode computeNode,
-                                                         List<OpenstackNode> gatewayNodeList,
-                                                         NetworkMode networkMode) {
-        List<GroupBucket> bucketList = Lists.newArrayList();
-
-        switch (networkMode) {
-            case VXLAN:
-                gatewayNodeList.stream()
-                        .filter(node -> node.dataIp().isPresent())
-                        .forEach(node -> {
-                            TrafficTreatment tBuilder = DefaultTrafficTreatment.builder()
-                                    .extension(buildNiciraExtenstion(computeNode.intBridge(),
-                                            node.dataIp().get().getIp4Address()),
-                                            computeNode.intBridge())
-                                    .setOutput(getTunnelPort(computeNode.intBridge()))
-                                    .build();
-                            bucketList.add(createSelectGroupBucket(tBuilder));
-                        });
-                return bucketList;
-            case VLAN:
-                gatewayNodeList.stream()
-                        .filter(node -> node.vlanPort().isPresent())
-                        .forEach(node -> {
-                            TrafficTreatment tBuilder = DefaultTrafficTreatment.builder()
-                                    .setEthDst(MacAddress.valueOf(vlanPortMac(node)))
-                                    .setOutput(vlanPortNum(computeNode))
-                                    .build();
-                            bucketList.add(createSelectGroupBucket(tBuilder));
-                        });
-                return bucketList;
-            default:
-                final String error = String.format(
-                        ERR_UNSUPPORTED_NET_TYPE + "%s",
-                        networkMode.toString());
-                throw new IllegalStateException(error);
-        }
-    }
-
-    /**
-     * Builds Nicira extension for tagging remoteIp of vxlan.
-     *
-     * @param id device id of vxlan source device
-     * @param hostIp remote ip of vxlan destination device
-     * @return NiciraExtension Treatment
-     */
-    private ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
-        Driver driver = driverService.getDriver(id);
-        DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
-        ExtensionTreatmentResolver resolver = driverHandler.behaviour(ExtensionTreatmentResolver.class);
-
-        ExtensionTreatment extensionInstruction =
-                resolver.getExtensionInstruction(
-                        ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST.type());
-
-        try {
-            extensionInstruction.setPropertyValue(TUNNEL_DESTINATION, hostIp);
-        } catch (ExtensionPropertyException e) {
-            log.error("Error setting Nicira extension setting {}", e);
-        }
-
-        return extensionInstruction;
-    }
-
-    /**
-     * Returns port number of vxlan tunnel.
-     *
-     * @param deviceId target Device Id
-     * @return portNumber
-     */
-    private PortNumber getTunnelPort(DeviceId deviceId) {
-        Port port = deviceService.getPorts(deviceId).stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL))
-                .findAny().orElse(null);
-
-        if (port == null) {
-            log.error("No TunnelPort was created.");
-            return null;
-        }
-        return port.number();
-    }
-
-    private PortNumber vlanPortNum(OpenstackNode node) {
-        return deviceService.getPorts(node.intBridge()).stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(node.vlanPort().get()) &&
-                        p.isEnabled())
-                .map(Port::number).findFirst().get();
-
-    }
-    private String vlanPortMac(OpenstackNode node) {
-        return deviceService.getPorts(node.intBridge()).stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(node.vlanPort().get()) && p.isEnabled())
-                .findFirst().get().annotations().value(PORT_MAC);
-    }
-}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/Constants.java
similarity index 80%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/Constants.java
index 8bb75a1..9f812db 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/Constants.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
 
 /**
  * Provides constants used in OpenStack node services.
@@ -26,11 +26,6 @@
     public static final String INTEGRATION_BRIDGE = "br-int";
     public static final String ROUTER_BRIDGE = "br-router";
     public static final String DEFAULT_TUNNEL = "vxlan";
-    public static final String VLAN = "vlan";
     public static final String PATCH_INTG_BRIDGE = "patch-intg";
     public static final String PATCH_ROUT_BRIDGE = "patch-rout";
-
-    public static final int DEFAULT_OVSDB_PORT = 6640;
-    public static final int DEFAULT_OFPORT = 6653;
-    public static final String DEFAULT_OF_PROTO = "tcp";
 }
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/NodeState.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/NodeState.java
new file mode 100644
index 0000000..6d3a825
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/NodeState.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.openstacknode.api;
+
+/**
+ * Defines the initialization states of OpenStack node.
+ */
+public enum NodeState {
+
+    /**
+     * Indicates the node is newly added.
+     */
+    INIT {
+        @Override
+        public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+            handler.processInitState(osNode);
+        }
+
+        @Override
+        public NodeState nextState() {
+            return DEVICE_CREATED;
+        }
+    },
+    /**
+     * Indicates bridge devices are added according to the node state.
+     */
+    DEVICE_CREATED {
+        @Override
+        public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+            handler.processDeviceCreatedState(osNode);
+        }
+
+        @Override
+        public NodeState nextState() {
+            return PORT_CREATED;
+        }
+    },
+    /**
+     * Indicates required ports are added.
+     */
+    PORT_CREATED {
+        @Override
+        public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+            handler.processPortCreatedState(osNode);
+        }
+
+        @Override
+        public NodeState nextState() {
+            return COMPLETE;
+        }
+    },
+    /**
+     * Indicates node initialization is done.
+     */
+    COMPLETE {
+        @Override
+        public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+            handler.processCompleteState(osNode);
+        }
+
+        @Override
+        public NodeState nextState() {
+            return COMPLETE;
+        }
+
+    },
+    /**
+     * Indicates node is broken.
+     */
+    INCOMPLETE {
+        @Override
+        public void process(OpenstackNodeHandler handler, OpenstackNode osNode) {
+            handler.processIncompleteState(osNode);
+        }
+
+        @Override
+        public NodeState nextState() {
+            return INIT;
+        }
+    };
+
+    public abstract void process(OpenstackNodeHandler handler, OpenstackNode osNode);
+    public abstract NodeState nextState();
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
new file mode 100644
index 0000000..d380ec5
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNode.java
@@ -0,0 +1,238 @@
+/*
+ * 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.openstacknode.api;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.group.GroupKey;
+
+/**
+ * Representation of a node used in OpenstackNetworking service.
+ */
+public interface OpenstackNode {
+
+    /**
+     * List of valid virtual network modes.
+     */
+    enum NetworkMode {
+        VXLAN,
+        VLAN
+    }
+
+    /**
+     * List of valid node types.
+     */
+    enum NodeType {
+        COMPUTE,
+        GATEWAY
+    }
+
+    /**
+     * Returns hostname of the node.
+     *
+     * @return hostname
+     */
+    String hostname();
+
+    /**
+     * Returns the type of the node.
+     *
+     * @return node type
+     */
+    NodeType type();
+
+    /**
+     * Returns the OVSDB device ID of the node.
+     *
+     * @return ovsdb device id
+     */
+    DeviceId ovsdb();
+
+    /**
+     * Returns the device ID of the integration bridge at the node.
+     *
+     * @return device id
+     */
+    DeviceId intgBridge();
+
+    /**
+     * Returns the router bridge device ID.
+     *
+     * @return device id; null if the node type is compute
+     */
+    DeviceId routerBridge();
+
+    /**
+     * Returns the management network IP address of the node.
+     *
+     * @return ip address
+     */
+    IpAddress managementIp();
+
+    /**
+     * Returns the data network IP address used for tunneling.
+     *
+     * @return ip address; null if vxlan mode is not enabled
+     */
+    IpAddress dataIp();
+
+    /**
+     * Returns the name of the vlan interface.
+     *
+     * @return vlan interface name; null if vlan mode is not enabled
+     */
+    String vlanIntf();
+
+    /**
+     * Returns the initialization state of the node.
+     *
+     * @return node state
+     */
+    NodeState state();
+
+    /**
+     * Returns the gateway group ID of this node.
+     *
+     * @param mode network mode of the group
+     * @return gateway group identifier
+     */
+    GroupId gatewayGroupId(NetworkMode mode);
+
+    /**
+     * Returns the group key of this node.
+     *
+     * @param mode network mode of the group
+     * @return gateway group key
+     */
+    GroupKey gatewayGroupKey(NetworkMode mode);
+
+    /**
+     * Returns the tunnel port number.
+     *
+     * @return port number; null if tunnel port does not exist
+     */
+    PortNumber tunnelPortNum();
+
+    /**
+     * Returns the vlan port nubmer.
+     *
+     * @return port number; null if vlan port does not exist
+     */
+    PortNumber vlanPortNum();
+
+    /**
+     * Returns the patch port number of the integration bridge.
+     *
+     * @return port number; null if the node type is compute
+     */
+    PortNumber patchPortNum();
+
+    /**
+     * Returns the vlan port MAC address.
+     *
+     * @return mac address; null if vlan port does not exist
+     */
+    MacAddress vlanPortMac();
+
+    /**
+     * Returns new openstack node instance with given state.
+     *
+     * @param newState updated state
+     * @return updated openstack node
+     */
+    OpenstackNode updateState(NodeState newState);
+
+    /**
+     * Builder of new node entities.
+     */
+    interface Builder {
+
+        /**
+         * Builds an immutable openstack node instance.
+         *
+         * @return openstack node instance
+         */
+        OpenstackNode build();
+
+        /**
+         * Returns openstack node builder with supplied hostname.
+         *
+         * @param hostname hostname of the node
+         * @return opesntack node builder
+         */
+        Builder hostname(String hostname);
+
+        /**
+         * Returns openstack node builder with supplied type.
+         *
+         * @param type openstack node type
+         * @return openstack node builder
+         */
+        Builder type(NodeType type);
+
+        /**
+         * Returns openstack node builder with supplied integration bridge ID.
+         *
+         * @param intgBridge integration bridge device id
+         * @return openstack node builder
+         */
+        Builder intgBridge(DeviceId intgBridge);
+
+        /**
+         * Returns openstack node builder with supplied router bridge ID.
+         *
+         * @param routerBridge router bridge id
+         * @return openstack node builder
+         */
+        Builder routerBridge(DeviceId routerBridge);
+
+        /**
+         * Returns openstack node builder with supplied management IP address.
+         *
+         * @param managementIp management ip address
+         * @return openstack node builder
+         */
+        Builder managementIp(IpAddress managementIp);
+
+        /**
+         * Returns openstack node builder with supplied data network IP address.
+         *
+         * @param dataIp data network ip address
+         * @return openstack node builder
+         */
+        Builder dataIp(IpAddress dataIp);
+
+        /**
+         * Returns openstack node builder with supplied vlan interface.
+         *
+         * @param vlanIntf vlan interface name
+         * @return openstack node builder
+         */
+        Builder vlanIntf(String vlanIntf);
+
+        /**
+         * Returns openstack node builder with supplied node state.
+         *
+         * @param state node state
+         * @return openstack node builder
+         */
+        Builder state(NodeState state);
+    }
+}
+
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeAdminService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeAdminService.java
new file mode 100644
index 0000000..cfef1aa
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeAdminService.java
@@ -0,0 +1,44 @@
+/*
+ * 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.openstacknode.api;
+
+/**
+ * Service for administering inventory of opestackNode.
+ */
+public interface OpenstackNodeAdminService {
+
+    /**
+     * Creates a new node.
+     *
+     * @param osNode openstack node
+     */
+    void createNode(OpenstackNode osNode);
+
+    /**
+     * Updates the node.
+     *
+     * @param osNode openstack node
+     */
+    void updateNode(OpenstackNode osNode);
+
+    /**
+     * Removes the node.
+     *
+     * @param hostname openstack node hostname
+     * @return removed node; null if the node does not exist
+     */
+    OpenstackNode removeNode(String hostname);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeConfig.java
similarity index 71%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeConfig.java
index 0f264e9..774342a 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeConfig.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeConfig.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
 
 
 import com.fasterxml.jackson.databind.JsonNode;
@@ -22,13 +22,14 @@
 import org.onlab.packet.IpAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
-import org.onosproject.openstacknode.OpenstackNodeService.NodeType;
-import java.util.Set;
 import org.onosproject.net.config.Config;
+import org.onosproject.openstacknode.impl.DefaultOpenstackNode;
+
+import java.util.Set;
 
 import static org.onosproject.net.config.Config.FieldPresence.MANDATORY;
 import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 
 /**
  * Configuration object for OpensatckNode service.
@@ -44,22 +45,17 @@
 
     // GATEWAY node specific fields
     private static final String ROUTER_BRIDGE = "routerBridge";
-    private static final String UPLINK_PORT_NAME = "uplinkPort";
-    // TODO remove this when vRouter supports multiple switches
-    private static final String ROUTER_CONTROLLER = "routerController";
-    private static final String VLAN_PORT_NAME = "vlanPort";
+    private static final String VLAN_INTF_NAME = "vlanPort";
 
     @Override
     public boolean isValid() {
         boolean result = hasOnlyFields(NODES);
-
         if (object.get(NODES) == null || object.get(NODES).size() < 1) {
-            final String msg = "No node is present";
-            throw new IllegalArgumentException(msg);
+            return true;
         }
 
         for (JsonNode node : object.get(NODES)) {
-            if (get(node, DATA_IP) == null && get(node, VLAN_PORT_NAME) == null) {
+            if (get(node, DATA_IP) == null && get(node, VLAN_INTF_NAME) == null) {
                 final String msg = "There is neither tunnel interface nor vlan port";
                 throw new IllegalArgumentException(msg);
             }
@@ -71,26 +67,22 @@
                     DATA_IP,
                     INTEGRATION_BRIDGE,
                     ROUTER_BRIDGE,
-                    UPLINK_PORT_NAME,
-                    ROUTER_CONTROLLER,
-                    VLAN_PORT_NAME
+                    VLAN_INTF_NAME
             );
 
             result &= isString(osNode, HOST_NAME, MANDATORY);
             result &= isString(osNode, TYPE, MANDATORY);
             result &= isIpAddress(osNode, MANAGEMENT_IP, MANDATORY);
             result &= isString(osNode, INTEGRATION_BRIDGE, MANDATORY);
-            result &= isString(osNode, VLAN_PORT_NAME, OPTIONAL);
+            result &= isString(osNode, VLAN_INTF_NAME, OPTIONAL);
             result &= isIpAddress(osNode, DATA_IP, OPTIONAL);
 
             DeviceId.deviceId(osNode.get(INTEGRATION_BRIDGE).asText());
-            NodeType.valueOf(osNode.get(TYPE).asText());
+            OpenstackNode.NodeType.valueOf(osNode.get(TYPE).asText());
 
             if (osNode.get(TYPE).asText().equals(GATEWAY.name())) {
                 result &= isString(osNode, ROUTER_BRIDGE, MANDATORY);
                 DeviceId.deviceId(osNode.get(ROUTER_BRIDGE).asText());
-                result &= isString(osNode, UPLINK_PORT_NAME, MANDATORY);
-                result &= isIpAddress(osNode, ROUTER_CONTROLLER, MANDATORY);
             }
         }
         return result;
@@ -104,25 +96,22 @@
     public Set<OpenstackNode> openstackNodes() {
         Set<OpenstackNode> nodes = Sets.newHashSet();
         for (JsonNode node : object.get(NODES)) {
-            NodeType type = NodeType.valueOf(get(node, TYPE));
-            OpenstackNode.Builder nodeBuilder = OpenstackNode.builder()
-                    .integrationBridge(DeviceId.deviceId(get(node, INTEGRATION_BRIDGE)))
+            OpenstackNode.NodeType type = OpenstackNode.NodeType.valueOf(get(node, TYPE));
+            OpenstackNode.Builder nodeBuilder = DefaultOpenstackNode.builder()
+                    .intgBridge(DeviceId.deviceId(get(node, INTEGRATION_BRIDGE)))
                     .managementIp(IpAddress.valueOf(get(node, MANAGEMENT_IP)))
                     .type(type)
-                    .hostname(get(node, HOST_NAME));
+                    .hostname(get(node, HOST_NAME))
+                    .state(NodeState.INIT);
 
             if (get(node, DATA_IP) != null) {
                 nodeBuilder.dataIp(IpAddress.valueOf(get(node, DATA_IP)));
             }
-
-            if (get(node, VLAN_PORT_NAME) != null) {
-                nodeBuilder.vlanPort(get(node, VLAN_PORT_NAME));
+            if (get(node, VLAN_INTF_NAME) != null) {
+                nodeBuilder.vlanIntf(get(node, VLAN_INTF_NAME));
             }
-
             if (type.equals(GATEWAY)) {
-                nodeBuilder.routerBridge(DeviceId.deviceId(get(node, ROUTER_BRIDGE)))
-                        .uplink(get(node, UPLINK_PORT_NAME))
-                        .routerController(IpAddress.valueOf(get(node, ROUTER_CONTROLLER)));
+                nodeBuilder.routerBridge(DeviceId.deviceId(get(node, ROUTER_BRIDGE)));
             }
             nodes.add(nodeBuilder.build());
         }
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeEvent.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeEvent.java
new file mode 100644
index 0000000..68ac50e
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeEvent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.openstacknode.api;
+
+import org.onosproject.event.AbstractEvent;
+
+/**
+ * Describes OpenStack node init state event.
+ */
+public class OpenstackNodeEvent extends AbstractEvent<OpenstackNodeEvent.Type, OpenstackNode> {
+
+    public enum Type {
+
+        /**
+         * Signifies that new node is created.
+         */
+        OPENSTACK_NODE_CREATED,
+
+        /**
+         * Signifies that the node state is updated.
+         */
+        OPENSTACK_NODE_UPDATED,
+
+        /**
+         * Signifies that the node state is complete.
+         */
+        OPENSTACK_NODE_COMPLETE,
+
+        /**
+         * Signifies that the node state is removed.
+         */
+        OPENSTACK_NODE_REMOVED,
+
+        /**
+         * Signifies that the node state is changed to incomplete.
+         */
+        OPENSTACK_NODE_INCOMPLETE
+    }
+
+    /**
+     * Creates an event with the given type and node.
+     *
+     * @param type event type
+     * @param node openstack node
+     */
+    public OpenstackNodeEvent(Type type, OpenstackNode node) {
+        super(type, node);
+    }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeHandler.java
new file mode 100644
index 0000000..fa0bb33
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeHandler.java
@@ -0,0 +1,61 @@
+/*
+ * 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.openstacknode.api;
+
+/**
+ * Service handling openstack node state.
+ */
+public interface OpenstackNodeHandler {
+
+    /**
+     * Processes the given node for init state.
+     * It creates required bridges on OVS based on the node type.
+     *
+     * @param osNode openstack node
+     */
+    void processInitState(OpenstackNode osNode);
+
+    /**
+     * Processes the given node for device created state.
+     * It creates required ports on the bridges based on the node type.
+     *
+     * @param osNode openstack node
+     */
+    void processDeviceCreatedState(OpenstackNode osNode);
+
+    /**
+     * Processes the given node for port created state.
+     * It creates gateway groups on compute node.
+     *
+     * @param osNode openstack node
+     */
+    void processPortCreatedState(OpenstackNode osNode);
+
+    /**
+     * Processes the given node for complete state.
+     * It performs post-init jobs for the complete node.
+     *
+     * @param osNode openstack node
+     */
+    void processCompleteState(OpenstackNode osNode);
+
+    /**
+     * Processes the given node for incomplete state.
+     *
+     * @param osNode openstack node
+     */
+    void processIncompleteState(OpenstackNode osNode);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeListener.java
similarity index 83%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeListener.java
index df8cf0d..b462624 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeListener.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.
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
 
 import org.onosproject.event.EventListener;
 
 /**
- * Listener for OpenStack node events.
+ * Listener for OpenstackNode event.
  */
 public interface OpenstackNodeListener extends EventListener<OpenstackNodeEvent> {
 }
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java
new file mode 100644
index 0000000..592593a
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeService.java
@@ -0,0 +1,78 @@
+/*
+ * 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.openstacknode.api;
+
+import org.onosproject.event.ListenerService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.openstacknode.api.OpenstackNode.NodeType;
+
+import java.util.Set;
+
+/**
+ * Service for interfacing with the inventory of {@link OpenstackNode}.
+ */
+public interface OpenstackNodeService extends ListenerService<OpenstackNodeEvent, OpenstackNodeListener> {
+
+    String APP_ID = "org.onosproject.openstacknode";
+
+    /**
+     * Returns all registered nodes.
+     *
+     * @return set of openstack nodes
+     */
+    Set<OpenstackNode> nodes();
+
+    /**
+     * Returns all nodes with the specified type.
+     *
+     * @param type node type
+     * @return set of openstack nodes
+     */
+    Set<OpenstackNode> nodes(NodeType type);
+
+    /**
+     * Returns all nodes with complete state.
+     *
+     * @return set of openstack nodes
+     */
+    Set<OpenstackNode> completeNodes();
+
+    /**
+     * Returns all nodes with complete state and the specified type.
+     *
+     * @param type node type
+     * @return set of openstack nodes
+     */
+    Set<OpenstackNode> completeNodes(NodeType type);
+
+    /**
+     * Returns the node with the specified hostname.
+     *
+     * @param hostname hostname
+     * @return openstack node
+     */
+    OpenstackNode node(String hostname);
+
+    /**
+     * Returns the node with the specified device ID.
+     * The device ID can be any one of integration bridge, router bridge,
+     * or ovsdb device.
+     *
+     * @param deviceId device id
+     * @return openstack node
+     */
+    OpenstackNode node(DeviceId deviceId);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStore.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStore.java
new file mode 100644
index 0000000..3a0a1ca
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStore.java
@@ -0,0 +1,63 @@
+/*
+ * 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.openstacknode.api;
+
+import org.onosproject.store.Store;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of OpenstackNode; not intended for direct use.
+ */
+public interface OpenstackNodeStore extends Store<OpenstackNodeEvent, OpenstackNodeStoreDelegate> {
+
+    /**
+     * Creates a new node.
+     *
+     * @param osNode openstack node
+     */
+    void createNode(OpenstackNode osNode);
+
+    /**
+     * Updates the node.
+     *
+     * @param osNode openstack node
+     */
+    void updateNode(OpenstackNode osNode);
+
+    /**
+     * Removes the node.
+     *
+     * @param hostname openstack node hostname
+     * @return removed openstack node; null if no node mapped for the hostname
+     */
+    OpenstackNode removeNode(String hostname);
+
+    /**
+     * Returns all registered nodes.
+     *
+     * @return set of openstack nodes
+     */
+    Set<OpenstackNode> nodes();
+
+    /**
+     * Returns the node with the specified hostname.
+     *
+     * @param hostname hostname
+     * @return openstack node
+     */
+    OpenstackNode node(String hostname);
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStoreDelegate.java
similarity index 68%
copy from apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
copy to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStoreDelegate.java
index df8cf0d..34de865 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeListener.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/OpenstackNodeStoreDelegate.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.
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.onosproject.openstacknode;
+package org.onosproject.openstacknode.api;
 
-import org.onosproject.event.EventListener;
+import org.onosproject.store.StoreDelegate;
 
 /**
- * Listener for OpenStack node events.
+ * OpenstackNode store delegate.
  */
-public interface OpenstackNodeListener extends EventListener<OpenstackNodeEvent> {
+public interface OpenstackNodeStoreDelegate extends StoreDelegate<OpenstackNodeEvent> {
 }
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/package-info.java
similarity index 86%
copy from apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
copy to apps/openstacknode/src/main/java/org/onosproject/openstacknode/api/package-info.java
index c280f54..3bf6709 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/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.
@@ -17,4 +17,4 @@
 /**
  * Application for bootstrapping Compute/Gateway Node in OpenStack.
  */
-package org.onosproject.openstacknode;
\ No newline at end of file
+package org.onosproject.openstacknode.api;
\ No newline at end of file
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
index 4d54abd..a35ab1b 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeCheckCommand.java
@@ -25,14 +25,14 @@
 import org.onosproject.net.behaviour.BridgeConfig;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.device.PortDescription;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 
 import java.util.Optional;
 
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.openstacknode.Constants.*;
-import static org.onosproject.openstacknode.OpenstackNodeService.NodeType.GATEWAY;
+import static org.onosproject.openstacknode.api.Constants.*;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
 
 /**
  * Checks detailed node init state.
@@ -50,22 +50,17 @@
 
     @Override
     protected void execute() {
-        OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+        OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
         DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
 
-        OpenstackNode node = nodeService.nodes()
-                .stream()
-                .filter(n -> n.hostname().equals(hostname))
-                .findFirst()
-                .orElse(null);
-
-        if (node == null) {
-            print("Cannot find %s from registered nodes", hostname);
+        OpenstackNode osNode = osNodeService.node(hostname);
+        if (osNode == null) {
+            error("Cannot find %s from registered nodes", hostname);
             return;
         }
 
         print("[Integration Bridge Status]");
-        Device device = deviceService.getDevice(node.intBridge());
+        Device device = deviceService.getDevice(osNode.intgBridge());
         if (device != null) {
             print("%s %s=%s available=%s %s",
                     deviceService.isAvailable(device.id()) ? MSG_OK : MSG_NO,
@@ -73,41 +68,39 @@
                     device.id(),
                     deviceService.isAvailable(device.id()),
                     device.annotations());
-
-            node.dataIp().ifPresent(ip -> print(getPortState(deviceService, node.intBridge(), DEFAULT_TUNNEL)));
-            node.vlanPort().ifPresent(p -> print(getPortState(deviceService, node.intBridge(), p)));
+            if (osNode.dataIp() != null) {
+                print(getPortState(deviceService, osNode.intgBridge(), DEFAULT_TUNNEL));
+            }
+            if (osNode.vlanIntf() != null) {
+                print(getPortState(deviceService, osNode.intgBridge(), osNode.vlanIntf()));
+            }
         } else {
             print("%s %s=%s is not available",
                     MSG_NO,
                     INTEGRATION_BRIDGE,
-                    node.intBridge());
+                    osNode.intgBridge());
         }
 
-        if (node.type().equals(GATEWAY)) {
-            print(getPortState(deviceService, node.intBridge(), PATCH_INTG_BRIDGE));
+        if (osNode.type() == GATEWAY) {
+            print(getPortState(deviceService, osNode.intgBridge(), PATCH_INTG_BRIDGE));
 
             print("%n[Router Bridge Status]");
-            device = deviceService.getDevice(node.ovsdbId());
+            device = deviceService.getDevice(osNode.ovsdb());
             if (device == null || !device.is(BridgeConfig.class)) {
                 print("%s %s=%s is not available(unable to connect OVSDB)",
                       MSG_NO,
                       ROUTER_BRIDGE,
-                      node.intBridge());
+                      osNode.intgBridge());
             } else {
                 BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
                 boolean available = bridgeConfig.getBridges().stream()
-                        .filter(bridge -> bridge.name().equals(ROUTER_BRIDGE))
-                        .findAny()
-                        .isPresent();
-
+                        .anyMatch(bridge -> bridge.name().equals(ROUTER_BRIDGE));
                 print("%s %s=%s available=%s",
                       available ? MSG_OK : MSG_NO,
                       ROUTER_BRIDGE,
-                      node.routerBridge().get(),
+                      osNode.routerBridge(),
                       available);
-
-                print(getPortStateOvsdb(deviceService, node.ovsdbId(), PATCH_ROUT_BRIDGE));
-                print(getPortStateOvsdb(deviceService, node.ovsdbId(), node.uplink().get()));
+                print(getPortStateOvsdb(deviceService, osNode.ovsdb(), PATCH_ROUT_BRIDGE));
             }
         }
     }
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
index 8f4dbb1..85da391 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeInitCommand.java
@@ -19,10 +19,10 @@
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeService;
-
-import java.util.NoSuchElementException;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 
 /**
  * Initializes nodes for OpenStack node service.
@@ -37,21 +37,19 @@
 
     @Override
     protected void execute() {
-        OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+        OpenstackNodeService osNodeService =
+                AbstractShellCommand.get(OpenstackNodeService.class);
+        OpenstackNodeAdminService osNodeAdminService =
+                AbstractShellCommand.get(OpenstackNodeAdminService.class);
 
         for (String hostname : hostnames) {
-            OpenstackNode node;
-            try {
-                node = nodeService.nodes()
-                        .stream()
-                        .filter(n -> n.hostname().equals(hostname))
-                        .findFirst().get();
-            } catch (NoSuchElementException e) {
+            OpenstackNode osNode = osNodeService.node(hostname);
+            if (osNode == null) {
                 print("Unable to find %s", hostname);
                 continue;
             }
-
-            nodeService.addOrUpdateNode(node);
+            OpenstackNode updated = osNode.updateState(NodeState.INIT);
+            osNodeAdminService.updateNode(updated);
         }
     }
 }
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
index 65a77e6..ceed2a7 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/cli/OpenstackNodeListCommand.java
@@ -19,11 +19,13 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
 
+import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -37,43 +39,45 @@
 
     @Override
     protected void execute() {
-        OpenstackNodeService nodeService = AbstractShellCommand.get(OpenstackNodeService.class);
-        List<OpenstackNode> nodes = nodeService.nodes();
-        nodes.sort(OpenstackNode.OPENSTACK_NODE_COMPARATOR);
+        OpenstackNodeService osNodeService = AbstractShellCommand.get(OpenstackNodeService.class);
+        List<OpenstackNode> osNodes = Lists.newArrayList(osNodeService.nodes());
+        osNodes.sort(Comparator.comparing(OpenstackNode::hostname));
 
         if (outputJson()) {
-            print("%s", json(nodes));
+            print("%s", json(osNodes));
         } else {
             print(FORMAT, "Hostname", "Type", "Integration Bridge", "Router Bridge",
                     "Management IP", "Data IP", "VLAN Intf", "State");
-            for (OpenstackNode node : nodes) {
+            for (OpenstackNode osNode : osNodes) {
                 print(FORMAT,
-                        node.hostname(),
-                        node.type(),
-                        node.intBridge(),
-                        node.routerBridge().isPresent() ? node.routerBridge().get() : "",
-                        node.managementIp(),
-                        node.dataIp().isPresent() ? node.dataIp().get() : "",
-                        node.vlanPort().isPresent() ? node.vlanPort().get() : "",
-                        node.state());
+                        osNode.hostname(),
+                        osNode.type(),
+                        osNode.intgBridge(),
+                        osNode.routerBridge() != null ? osNode.routerBridge() : "",
+                        osNode.managementIp(),
+                        osNode.dataIp() != null ? osNode.dataIp() : "",
+                        osNode.vlanIntf() != null ? osNode.vlanIntf() : "",
+                        osNode.state());
             }
-            print("Total %s nodes", nodeService.nodes().size());
+            print("Total %s nodes", osNodeService.nodes().size());
         }
     }
 
-    private JsonNode json(List<OpenstackNode> nodes) {
+    private JsonNode json(List<OpenstackNode> osNodes) {
         ObjectMapper mapper = new ObjectMapper();
         ArrayNode result = mapper.createArrayNode();
-        for (OpenstackNode node : nodes) {
+        for (OpenstackNode osNode : osNodes) {
             result.add(mapper.createObjectNode()
-                    .put("hostname", node.hostname())
-                    .put("type", node.type().name())
-                    .put("intBridge", node.intBridge().toString())
-                    .put("routerBridge", node.routerBridge().toString())
-                    .put("managementIp", node.managementIp().toString())
-                    .put("dataIp", node.dataIp().toString())
-                    .put("vlanPort", node.vlanPort().toString())
-                    .put("state", node.state().name()));
+                    .put("hostname", osNode.hostname())
+                    .put("type", osNode.type().name())
+                    .put("integrationBridge", osNode.intgBridge().toString())
+                    .put("routerBridge", osNode.routerBridge().toString())
+                    .put("managementIp", osNode.managementIp().toString())
+                    .put("dataIp", osNode.dataIp().toString())
+                    .put("vlanIntf", osNode.vlanIntf())
+                    .put("tunnelPortNum", osNode.tunnelPortNum().toString())
+                    .put("vlanPortNum", osNode.vlanPortNum().toString())
+                    .put("state", osNode.state().name()));
         }
         return result;
     }
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
new file mode 100644
index 0000000..670d4e8
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNode.java
@@ -0,0 +1,355 @@
+/*
+ * 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.openstacknode.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onosproject.net.AnnotationKeys.PORT_MAC;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.openstacknode.api.Constants.DEFAULT_TUNNEL;
+import static org.onosproject.openstacknode.api.Constants.PATCH_INTG_BRIDGE;
+
+/**
+ * Representation of a openstack node.
+ */
+public final class DefaultOpenstackNode implements OpenstackNode {
+
+    private final String hostname;
+    private final NodeType type;
+    private final DeviceId intgBridge;
+    private final DeviceId routerBridge;
+    private final IpAddress managementIp;
+    private final IpAddress dataIp;
+    private final String vlanIntf;
+    private final NodeState state;
+
+    private DefaultOpenstackNode(String hostname,
+                                 NodeType type,
+                                 DeviceId intgBridge,
+                                 DeviceId routerBridge,
+                                 IpAddress managementIp,
+                                 IpAddress dataIp,
+                                 String vlanIntf,
+                                 NodeState state) {
+        this.hostname = hostname;
+        this.type = type;
+        this.intgBridge = intgBridge;
+        this.routerBridge = routerBridge;
+        this.managementIp = managementIp;
+        this.dataIp = dataIp;
+        this.vlanIntf = vlanIntf;
+        this.state = state;
+    }
+
+    @Override
+    public String hostname() {
+        return hostname;
+    }
+
+    @Override
+    public NodeType type() {
+        return type;
+    }
+
+    @Override
+    public DeviceId ovsdb() {
+        return DeviceId.deviceId("ovsdb:" + managementIp().toString());
+    }
+
+    @Override
+    public DeviceId intgBridge() {
+        return intgBridge;
+    }
+
+    @Override
+    public DeviceId routerBridge() {
+        return routerBridge;
+    }
+
+    @Override
+    public IpAddress managementIp() {
+        return managementIp;
+    }
+
+    @Override
+    public IpAddress dataIp() {
+        return dataIp;
+    }
+
+    @Override
+    public String vlanIntf() {
+        return vlanIntf;
+    }
+
+    @Override
+    public NodeState state() {
+        return state;
+    }
+
+    @Override
+    public GroupKey gatewayGroupKey(NetworkMode mode) {
+        return new DefaultGroupKey(intgBridge.toString().concat(mode.name()).getBytes());
+    }
+
+    @Override
+    public PortNumber tunnelPortNum() {
+        if (dataIp == null) {
+            return null;
+        }
+        DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+        Port port = deviceService.getPorts(intgBridge).stream()
+                .filter(p -> p.isEnabled() &&
+                        Objects.equals(p.annotations().value(PORT_NAME), DEFAULT_TUNNEL))
+                .findAny().orElse(null);
+        return port != null ? port.number() : null;
+    }
+
+    @Override
+    public PortNumber vlanPortNum() {
+        if (vlanIntf == null) {
+            return null;
+        }
+        DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+        Port port = deviceService.getPorts(intgBridge).stream()
+                .filter(p -> p.isEnabled() &&
+                        Objects.equals(p.annotations().value(PORT_NAME), vlanIntf))
+                .findAny().orElse(null);
+        return port != null ? port.number() : null;
+    }
+
+    @Override
+    public PortNumber patchPortNum() {
+        if (type == NodeType.COMPUTE) {
+            return null;
+        }
+        DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+        Port port = deviceService.getPorts(intgBridge).stream()
+                .filter(p -> p.isEnabled() &&
+                        Objects.equals(p.annotations().value(PORT_NAME), PATCH_INTG_BRIDGE))
+                .findAny().orElse(null);
+        return port != null ? port.number() : null;
+    }
+
+    @Override
+    public MacAddress vlanPortMac() {
+        if (vlanIntf == null) {
+            return null;
+        }
+        DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
+        Port port = deviceService.getPorts(intgBridge).stream()
+                .filter(p -> p.annotations().value(PORT_NAME).equals(vlanIntf))
+                .findAny().orElse(null);
+        return port != null ? MacAddress.valueOf(port.annotations().value(PORT_MAC)) : null;
+    }
+
+    @Override
+    public GroupId gatewayGroupId(NetworkMode mode) {
+        return new GroupId(intgBridge.toString().concat(mode.name()).hashCode());
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof DefaultOpenstackNode) {
+            DefaultOpenstackNode that = (DefaultOpenstackNode) obj;
+            if (Objects.equals(hostname, that.hostname) &&
+                    Objects.equals(type, that.type) &&
+                    Objects.equals(intgBridge, that.intgBridge) &&
+                    Objects.equals(routerBridge, that.routerBridge) &&
+                    Objects.equals(managementIp, that.managementIp) &&
+                    Objects.equals(dataIp, that.dataIp) &&
+                    Objects.equals(vlanIntf, that.vlanIntf)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(hostname,
+                type,
+                intgBridge,
+                routerBridge,
+                managementIp,
+                dataIp,
+                vlanIntf);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("hostname", hostname)
+                .add("type", type)
+                .add("integrationBridge", intgBridge)
+                .add("routerBridge", routerBridge)
+                .add("managementIp", managementIp)
+                .add("dataIp", dataIp)
+                .add("vlanIntf", vlanIntf)
+                .add("state", state)
+                .toString();
+    }
+
+    @Override
+    public OpenstackNode updateState(NodeState newState) {
+        return new Builder()
+                .type(type)
+                .hostname(hostname)
+                .intgBridge(intgBridge)
+                .routerBridge(routerBridge)
+                .managementIp(managementIp)
+                .dataIp(dataIp)
+                .vlanIntf(vlanIntf)
+                .state(newState)
+                .build();
+    }
+
+    /**
+     * Returns new builder instance.
+     *
+     * @return openstack node builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns new builder instance with the given node as a default value.
+     *
+     * @param osNode openstack node
+     * @return openstack node builder
+     */
+    public static Builder from(OpenstackNode osNode) {
+        return new Builder()
+                .hostname(osNode.hostname())
+                .intgBridge(osNode.intgBridge())
+                .routerBridge(osNode.routerBridge())
+                .managementIp(osNode.managementIp())
+                .dataIp(osNode.dataIp())
+                .vlanIntf(osNode.vlanIntf())
+                .state(osNode.state());
+    }
+
+    public static final class Builder implements OpenstackNode.Builder {
+
+        private String hostname;
+        private NodeType type;
+        private DeviceId intgBridge;
+        private DeviceId routerBridge;
+        private IpAddress managementIp;
+        private IpAddress dataIp;
+        private String vlanIntf;
+        private NodeState state;
+
+        private Builder() {
+        }
+
+        @Override
+        public DefaultOpenstackNode build() {
+            checkArgument(hostname != null, "Node hostname cannot be null");
+            checkArgument(type != null, "Node type cannot be null");
+            checkArgument(intgBridge != null, "Node integration bridge cannot be null");
+            checkArgument(managementIp != null, "Node management IP cannot be null");
+            checkArgument(state != null, "Node state cannot be null");
+
+            if (type == NodeType.GATEWAY && routerBridge == null) {
+                throw new IllegalArgumentException("Router bridge is required for gateway node");
+            }
+            if (dataIp == null && Strings.isNullOrEmpty(vlanIntf)) {
+                throw new IllegalArgumentException("Either data IP or VLAN interface is required");
+            }
+
+            return new DefaultOpenstackNode(hostname,
+                    type,
+                    intgBridge,
+                    routerBridge,
+                    managementIp,
+                    dataIp,
+                    vlanIntf,
+                    state);
+        }
+
+        @Override
+        public Builder hostname(String hostname) {
+            if (!Strings.isNullOrEmpty(hostname)) {
+                this.hostname = hostname;
+            }
+            return this;
+        }
+
+        @Override
+        public Builder type(NodeType type) {
+            this.type = type;
+            return this;
+        }
+
+        @Override
+        public Builder intgBridge(DeviceId intgBridge) {
+            this.intgBridge = intgBridge;
+            return this;
+        }
+
+        @Override
+        public Builder routerBridge(DeviceId routerBridge) {
+            this.routerBridge = routerBridge;
+            return this;
+        }
+
+        @Override
+        public Builder managementIp(IpAddress managementIp) {
+            this.managementIp = managementIp;
+            return this;
+        }
+
+        @Override
+        public Builder dataIp(IpAddress dataIp) {
+            this.dataIp = dataIp;
+            return this;
+        }
+
+        @Override
+        public Builder vlanIntf(String vlanIntf) {
+            this.vlanIntf = vlanIntf;
+            return this;
+        }
+
+        @Override
+        public Builder state(NodeState state) {
+            this.state = state;
+            return this;
+        }
+    }
+}
+
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java
new file mode 100644
index 0000000..c0d2c3c
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DefaultOpenstackNodeHandler.java
@@ -0,0 +1,805 @@
+/*
+ * 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.openstacknode.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.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.IpAddress;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode;
+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.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.behaviour.BridgeConfig;
+import org.onosproject.net.behaviour.BridgeDescription;
+import org.onosproject.net.behaviour.BridgeName;
+import org.onosproject.net.behaviour.ControllerInfo;
+import org.onosproject.net.behaviour.DefaultBridgeDescription;
+import org.onosproject.net.behaviour.DefaultPatchDescription;
+import org.onosproject.net.behaviour.DefaultTunnelDescription;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.behaviour.InterfaceConfig;
+import org.onosproject.net.behaviour.PatchDescription;
+import org.onosproject.net.behaviour.TunnelDescription;
+import org.onosproject.net.behaviour.TunnelEndPoints;
+import org.onosproject.net.behaviour.TunnelKeys;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.ExtensionPropertyException;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNode.NetworkMode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeHandler;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.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.packet.TpPort.tpPort;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
+import static org.onosproject.openstacknode.api.Constants.*;
+import static org.onosproject.openstacknode.api.Constants.PATCH_INTG_BRIDGE;
+import static org.onosproject.openstacknode.api.NodeState.*;
+import static org.onosproject.openstacknode.api.OpenstackNode.NetworkMode.VLAN;
+import static org.onosproject.openstacknode.api.OpenstackNode.NetworkMode.VXLAN;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.COMPUTE;
+import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
+import static org.onosproject.openstacknode.api.OpenstackNodeService.APP_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Service bootstraps openstack node based on its type.
+ */
+@Component(immediate = true)
+public class DefaultOpenstackNodeHandler implements OpenstackNodeHandler {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String OVSDB_PORT = "ovsdbPortNum";
+    private static final int DEFAULT_OVSDB_PORT = 6640;
+    private static final String DEFAULT_OF_PROTO = "tcp";
+    private static final int DEFAULT_OFPORT = 6653;
+    private static final int DPID_BEGIN = 3;
+
+    @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 DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceAdminService deviceAdminService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OvsdbController ovsdbController;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected GroupService groupService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService osNodeService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeAdminService osNodeAdminService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ComponentConfigService componentConfigService;
+
+    @Property(name = OVSDB_PORT, intValue = DEFAULT_OVSDB_PORT,
+            label = "OVSDB server listen port")
+    private int ovsdbPort = DEFAULT_OVSDB_PORT;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private final DeviceListener ovsdbListener = new InternalOvsdbListener();
+    private final DeviceListener bridgeListener = new InternalBridgeListener();
+    private final GroupListener groupListener = new InternalGroupListener();
+    private final OpenstackNodeListener osNodeListener = new InternalOpenstackNodeListener();
+
+    private ApplicationId appId;
+    private NodeId localNode;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.getAppId(APP_ID);
+        localNode = clusterService.getLocalNode().id();
+
+        componentConfigService.registerProperties(getClass());
+        leadershipService.runForLeadership(appId.name());
+        groupService.addListener(groupListener);
+        deviceService.addListener(ovsdbListener);
+        deviceService.addListener(bridgeListener);
+        osNodeService.addListener(osNodeListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osNodeService.removeListener(osNodeListener);
+        deviceService.removeListener(bridgeListener);
+        deviceService.removeListener(ovsdbListener);
+        groupService.removeListener(groupListener);
+        componentConfigService.unregisterProperties(getClass(), false);
+        leadershipService.withdraw(appId.name());
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Modified
+    protected void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context.getProperties();
+        int updatedOvsdbPort = Tools.getIntegerProperty(properties, OVSDB_PORT);
+        if (!Objects.equals(updatedOvsdbPort, ovsdbPort)) {
+            ovsdbPort = updatedOvsdbPort;
+        }
+
+        log.info("Modified");
+    }
+
+    @Override
+    public void processInitState(OpenstackNode osNode) {
+        if (!isOvsdbConnected(osNode)) {
+            ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
+            return;
+        }
+        if (!deviceService.isAvailable(osNode.intgBridge())) {
+            createBridge(osNode, INTEGRATION_BRIDGE, osNode.intgBridge());
+        }
+        if (osNode.type() == GATEWAY &&
+                !isBridgeCreated(osNode.ovsdb(), ROUTER_BRIDGE)) {
+            createBridge(osNode, ROUTER_BRIDGE, osNode.routerBridge());
+        }
+    }
+
+    @Override
+    public void processDeviceCreatedState(OpenstackNode osNode) {
+        if (!isOvsdbConnected(osNode)) {
+            ovsdbController.connect(osNode.managementIp(), tpPort(ovsdbPort));
+            return;
+        }
+        if (osNode.type() == GATEWAY && (
+                !isIntfEnabled(osNode, PATCH_INTG_BRIDGE) ||
+                        !isIntfCreated(osNode, PATCH_ROUT_BRIDGE)
+        )) {
+            createPatchInterface(osNode);
+        }
+        if (osNode.dataIp() != null &&
+                !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
+            createTunnelInterface(osNode);
+        }
+        if (osNode.vlanIntf() != null &&
+                !isIntfEnabled(osNode, osNode.vlanIntf())) {
+            addSystemInterface(osNode, INTEGRATION_BRIDGE, osNode.vlanIntf());
+        }
+    }
+
+    @Override
+    public void processPortCreatedState(OpenstackNode osNode) {
+        switch (osNode.type()) {
+            case COMPUTE:
+                if (osNode.dataIp() != null) {
+                    addOrUpdateGatewayGroup(osNode,
+                            osNodeService.completeNodes(GATEWAY),
+                            VXLAN);
+                }
+                if (osNode.vlanIntf() != null) {
+                    addOrUpdateGatewayGroup(osNode,
+                            osNodeService.completeNodes(GATEWAY),
+                            VLAN);
+                }
+                break;
+            case GATEWAY:
+                Set<OpenstackNode> gateways =
+                        Sets.newHashSet(osNodeService.completeNodes(GATEWAY));
+                gateways.add(osNode);
+                osNodeService.completeNodes(COMPUTE).forEach(n -> {
+                    if (n.dataIp() != null) {
+                        addOrUpdateGatewayGroup(n, gateways, VXLAN);
+                    }
+                    if (n.vlanIntf() != null) {
+                        addOrUpdateGatewayGroup(n, gateways, VLAN);
+                    }
+                });
+                break;
+            default:
+                break;
+        }
+    }
+
+    @Override
+    public void processCompleteState(OpenstackNode osNode) {
+        OvsdbClientService ovsdbClient = ovsdbController.getOvsdbClient(
+                new OvsdbNodeId(osNode.managementIp(), DEFAULT_OVSDB_PORT));
+        if (ovsdbClient != null && ovsdbClient.isConnected()) {
+            ovsdbClient.disconnect();
+        }
+    }
+
+    @Override
+    public void processIncompleteState(OpenstackNode osNode) {
+        if (osNode.type() == COMPUTE) {
+            if (osNode.dataIp() != null) {
+                groupService.removeGroup(osNode.intgBridge(), osNode.gatewayGroupKey(VXLAN), appId);
+            }
+            if (osNode.vlanIntf() != null) {
+                groupService.removeGroup(osNode.intgBridge(), osNode.gatewayGroupKey(VLAN), appId);
+            }
+        }
+        if (osNode.type() == GATEWAY) {
+            osNodeService.completeNodes(COMPUTE).forEach(n -> {
+                if (n.dataIp() != null) {
+                    addOrUpdateGatewayGroup(n,
+                            osNodeService.completeNodes(GATEWAY),
+                            VXLAN);
+                }
+                if (n.vlanIntf() != null) {
+                    addOrUpdateGatewayGroup(n,
+                            osNodeService.completeNodes(GATEWAY),
+                            VLAN);
+                }
+            });
+        }
+    }
+
+    private boolean isOvsdbConnected(OpenstackNode osNode) {
+        OvsdbNodeId ovsdb = new OvsdbNodeId(osNode.managementIp(), ovsdbPort);
+        OvsdbClientService client = ovsdbController.getOvsdbClient(ovsdb);
+        return deviceService.isAvailable(osNode.ovsdb()) &&
+                client != null &&
+                client.isConnected();
+    }
+
+    private void createBridge(OpenstackNode osNode, String bridgeName, DeviceId deviceId) {
+        Device device = deviceService.getDevice(osNode.ovsdb());
+        if (device == null || !device.is(BridgeConfig.class)) {
+            log.error("Failed to create integration bridge on {}", osNode.ovsdb());
+            return;
+        }
+
+        // TODO fix this when we use single ONOS cluster for both openstackNode and vRouter
+        Set<IpAddress> controllerIps;
+        if (bridgeName.equals(ROUTER_BRIDGE)) {
+            // TODO checks if empty controller does not break anything
+            controllerIps = ImmutableSet.of();
+        } else {
+            controllerIps = clusterService.getNodes().stream()
+                    .map(ControllerNode::ip)
+                    .collect(Collectors.toSet());
+        }
+
+        List<ControllerInfo> controllers = controllerIps.stream()
+                .map(ip -> new ControllerInfo(ip, DEFAULT_OFPORT, DEFAULT_OF_PROTO))
+                .collect(Collectors.toList());
+
+        String dpid = deviceId.toString().substring(DPID_BEGIN);
+        BridgeDescription bridgeDesc = DefaultBridgeDescription.builder()
+                .name(bridgeName)
+                .failMode(BridgeDescription.FailMode.SECURE)
+                .datapathId(dpid)
+                .disableInBand()
+                .controllers(controllers)
+                .build();
+
+        BridgeConfig bridgeConfig = device.as(BridgeConfig.class);
+        bridgeConfig.addBridge(bridgeDesc);
+    }
+
+    private void addSystemInterface(OpenstackNode osNode, String bridgeName, String intfName) {
+        Device device = deviceService.getDevice(osNode.ovsdb());
+        if (device == null || !device.is(BridgeConfig.class)) {
+            return;
+        }
+        BridgeConfig bridgeConfig =  device.as(BridgeConfig.class);
+        bridgeConfig.addPort(BridgeName.bridgeName(bridgeName), intfName);
+    }
+
+    private void createTunnelInterface(OpenstackNode osNode) {
+        if (isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
+            return;
+        }
+
+        Device device = deviceService.getDevice(osNode.ovsdb());
+        if (device == null || !device.is(InterfaceConfig.class)) {
+            log.error("Failed to create tunnel interface on {}", osNode.ovsdb());
+            return;
+        }
+
+        TunnelDescription tunnelDesc = DefaultTunnelDescription.builder()
+                .deviceId(INTEGRATION_BRIDGE)
+                .ifaceName(DEFAULT_TUNNEL)
+                .type(TunnelDescription.Type.VXLAN)
+                .remote(TunnelEndPoints.flowTunnelEndpoint())
+                .key(TunnelKeys.flowTunnelKey())
+                .build();
+
+        InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
+        ifaceConfig.addTunnelMode(DEFAULT_TUNNEL, tunnelDesc);
+    }
+
+    private void createPatchInterface(OpenstackNode osNode) {
+        checkArgument(osNode.type().equals(OpenstackNode.NodeType.GATEWAY));
+        if (isIntfEnabled(osNode, PATCH_INTG_BRIDGE) &&
+                isIntfCreated(osNode, PATCH_ROUT_BRIDGE)) {
+            return;
+        }
+
+        Device device = deviceService.getDevice(osNode.ovsdb());
+        if (device == null || !device.is(InterfaceConfig.class)) {
+            log.error("Failed to create patch interfaces on {}", osNode.hostname());
+            return;
+        }
+
+        PatchDescription patchIntg = DefaultPatchDescription.builder()
+                .deviceId(INTEGRATION_BRIDGE)
+                .ifaceName(PATCH_INTG_BRIDGE)
+                .peer(PATCH_ROUT_BRIDGE)
+                .build();
+
+        PatchDescription patchRout = DefaultPatchDescription.builder()
+                .deviceId(ROUTER_BRIDGE)
+                .ifaceName(PATCH_ROUT_BRIDGE)
+                .peer(PATCH_INTG_BRIDGE)
+                .build();
+
+        InterfaceConfig ifaceConfig = device.as(InterfaceConfig.class);
+        ifaceConfig.addPatchMode(PATCH_INTG_BRIDGE, patchIntg);
+        ifaceConfig.addPatchMode(PATCH_ROUT_BRIDGE, patchRout);
+    }
+
+    private void addOrUpdateGatewayGroup(OpenstackNode osNode,
+                                         Set<OpenstackNode> gatewayNodes,
+                                         NetworkMode mode) {
+        GroupBuckets buckets = gatewayGroupBuckets(osNode, gatewayNodes, mode);
+        if (groupService.getGroup(osNode.intgBridge(), osNode.gatewayGroupKey(mode)) == null) {
+            GroupDescription groupDescription = new DefaultGroupDescription(
+                    osNode.intgBridge(),
+                    GroupDescription.Type.SELECT,
+                    buckets,
+                    osNode.gatewayGroupKey(mode),
+                    osNode.gatewayGroupId(mode).id(),
+                    appId);
+            groupService.addGroup(groupDescription);
+            log.debug("Created gateway group for {}", osNode.hostname());
+        } else {
+            groupService.setBucketsForGroup(
+                    osNode.intgBridge(),
+                    osNode.gatewayGroupKey(mode),
+                    buckets,
+                    osNode.gatewayGroupKey(mode),
+                    appId);
+            log.debug("Updated gateway group for {}", osNode.hostname());
+        }
+    }
+
+    private GroupBuckets gatewayGroupBuckets(OpenstackNode osNode,
+                                             Set<OpenstackNode> gatewayNodes,
+                                             NetworkMode mode) {
+        List<GroupBucket> bucketList = Lists.newArrayList();
+        switch (mode) {
+            case VXLAN:
+                gatewayNodes.stream().filter(n -> n.dataIp() != null).forEach(n -> {
+                    TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                            .extension(tunnelDstTreatment(osNode.intgBridge(),
+                                    n.dataIp()),
+                                    osNode.intgBridge())
+                            .setOutput(osNode.tunnelPortNum())
+                            .build();
+                    bucketList.add(createSelectGroupBucket(treatment));
+                });
+                return new GroupBuckets(bucketList);
+            case VLAN:
+                gatewayNodes.stream().filter(n -> n.vlanIntf() != null).forEach(n -> {
+                    TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                            .setEthDst(n.vlanPortMac())
+                            .setOutput(osNode.vlanPortNum())
+                            .build();
+                    bucketList.add(createSelectGroupBucket(treatment));
+                });
+                return new GroupBuckets(bucketList);
+            default:
+                return null;
+        }
+    }
+
+    private ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, IpAddress remoteIp) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
+            log.error("The extension treatment is not supported");
+            return null;
+        }
+
+        ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+        ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+        try {
+            treatment.setPropertyValue("tunnelDst", remoteIp.getIp4Address());
+            return treatment;
+        } catch (ExtensionPropertyException e) {
+            log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
+            return null;
+        }
+    }
+
+    private boolean isBridgeCreated(DeviceId ovsdbId, String bridgeName) {
+        Device device = deviceService.getDevice(ovsdbId);
+        if (device == null || !deviceService.isAvailable(device.id()) ||
+                !device.is(BridgeConfig.class)) {
+            return false;
+        }
+        BridgeConfig bridgeConfig =  device.as(BridgeConfig.class);
+        return bridgeConfig.getBridges().stream()
+                .anyMatch(bridge -> bridge.name().equals(bridgeName));
+    }
+
+    private boolean isIntfEnabled(OpenstackNode osNode, String intf) {
+        if (!deviceService.isAvailable(osNode.intgBridge())) {
+            return false;
+        }
+        return deviceService.getPorts(osNode.intgBridge()).stream()
+                .anyMatch(port -> Objects.equals(
+                        port.annotations().value(PORT_NAME), intf) &&
+                        port.isEnabled());
+    }
+
+    private boolean isIntfCreated(OpenstackNode osNode, String intf) {
+        Device device = deviceService.getDevice(osNode.ovsdb());
+        if (device == null || !deviceService.isAvailable(osNode.ovsdb()) ||
+                !device.is(BridgeConfig.class)) {
+            return false;
+        }
+
+        BridgeConfig bridgeConfig =  device.as(BridgeConfig.class);
+        return bridgeConfig.getPorts().stream()
+                .anyMatch(port -> port.annotations().value(PORT_NAME).equals(intf));
+    }
+
+    private boolean isGroupCreated(OpenstackNode osNode) {
+        for (OpenstackNode gNode : osNodeService.completeNodes(GATEWAY)) {
+            if (!isGatewayBucketAdded(osNode, gNode)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean isGatewayBucketAdded(OpenstackNode cNode, OpenstackNode gNode) {
+        if (cNode.dataIp() != null) {
+            Group osGroup = groupService.getGroup(cNode.intgBridge(),
+                    cNode.gatewayGroupKey(VXLAN));
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .extension(tunnelDstTreatment(gNode.intgBridge(),
+                            gNode.dataIp()),
+                            cNode.intgBridge())
+                    .setOutput(cNode.tunnelPortNum())
+                    .build();
+            GroupBucket bucket = createSelectGroupBucket(treatment);
+            if (osGroup == null || osGroup.state() != Group.GroupState.ADDED ||
+                    !osGroup.buckets().buckets().contains(bucket)) {
+                return false;
+            }
+        }
+        if (cNode.vlanIntf() != null) {
+            Group osGroup = groupService.getGroup(cNode.intgBridge(),
+                    cNode.gatewayGroupKey(VLAN));
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setEthDst(gNode.vlanPortMac())
+                    .setOutput(cNode.vlanPortNum())
+                    .build();
+            GroupBucket bucket = createSelectGroupBucket(treatment);
+            if (osGroup == null || osGroup.state() != Group.GroupState.ADDED ||
+                    !osGroup.buckets().buckets().contains(bucket)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean isCurrentStateDone(OpenstackNode osNode) {
+        switch (osNode.state()) {
+            case INIT:
+                if (!deviceService.isAvailable(osNode.intgBridge())) {
+                    return false;
+                }
+                if (osNode.type() == GATEWAY &&
+                        !isBridgeCreated(osNode.ovsdb(), ROUTER_BRIDGE)) {
+                    return false;
+                }
+                return true;
+            case DEVICE_CREATED:
+                if (osNode.dataIp() != null &&
+                        !isIntfEnabled(osNode, DEFAULT_TUNNEL)) {
+                    return false;
+                }
+                if (osNode.vlanIntf() != null &&
+                        !isIntfEnabled(osNode, osNode.vlanIntf())) {
+                    return false;
+                }
+                if (osNode.type() == GATEWAY && (
+                        !isIntfEnabled(osNode, PATCH_INTG_BRIDGE) ||
+                        !isIntfCreated(osNode, PATCH_ROUT_BRIDGE))) {
+                    return false;
+                }
+                return true;
+            case PORT_CREATED:
+                if (osNode.type() == COMPUTE) {
+                    return isGroupCreated(osNode);
+                } else {
+                    for (OpenstackNode cNode : osNodeService.completeNodes(COMPUTE)) {
+                        if (!isGatewayBucketAdded(cNode, osNode)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+            case COMPLETE:
+                return false;
+            case INCOMPLETE:
+                // always return false
+                // run init CLI to re-trigger node bootstrap
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    private void setState(OpenstackNode osNode, NodeState newState) {
+        if (osNode.state() == newState) {
+            return;
+        }
+        OpenstackNode updated = osNode.updateState(newState);
+        osNodeAdminService.updateNode(updated);
+        log.info("Changed {} state: {}", osNode.hostname(), newState);
+    }
+
+    private void bootstrapNode(OpenstackNode osNode) {
+        if (isCurrentStateDone(osNode)) {
+            setState(osNode, osNode.state().nextState());
+        } else {
+            log.trace("Processing {} state for {}", osNode.state(), osNode.hostname());
+            osNode.state().process(this, osNode);
+        }
+    }
+
+    private class InternalOvsdbListener implements DeviceListener {
+
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNode, leader) &&
+                    event.subject().type() == Device.Type.CONTROLLER &&
+                    osNodeService.node(event.subject().id()) != null;
+        }
+
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            OpenstackNode osNode = osNodeService.node(device.id());
+
+            switch (event.type()) {
+                case DEVICE_AVAILABILITY_CHANGED:
+                case DEVICE_ADDED:
+                    eventExecutor.execute(() -> {
+                        if (deviceService.isAvailable(device.id())) {
+                            log.debug("OVSDB {} detected", device.id());
+                            bootstrapNode(osNode);
+                        } else if (osNode.state() == COMPLETE) {
+                            log.debug("Removing OVSDB {}", device.id());
+                            deviceAdminService.removeDevice(device.id());
+                        }
+                    });
+                    break;
+                case PORT_ADDED:
+                case PORT_REMOVED:
+                case DEVICE_REMOVED:
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+    }
+
+    private class InternalBridgeListener implements DeviceListener {
+
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNode, leader) &&
+                    event.subject().type() == Device.Type.SWITCH &&
+                    osNodeService.node(event.subject().id()) != null;
+        }
+
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            OpenstackNode osNode = osNodeService.node(device.id());
+
+            switch (event.type()) {
+                case DEVICE_AVAILABILITY_CHANGED:
+                case DEVICE_ADDED:
+                    eventExecutor.execute(() -> {
+                        if (deviceService.isAvailable(device.id())) {
+                            log.debug("Integration bridge created on {}", osNode.hostname());
+                            bootstrapNode(osNode);
+                        } else if (osNode.state() == COMPLETE) {
+                            log.warn("Device {} disconnected", device.id());
+                            setState(osNode, INCOMPLETE);
+                        }
+                    });
+                    break;
+                case PORT_ADDED:
+                    eventExecutor.execute(() -> {
+                        Port port = event.port();
+                        String portName = port.annotations().value(PORT_NAME);
+                        if (osNode.state() == DEVICE_CREATED && (
+                                Objects.equals(portName, DEFAULT_TUNNEL) ||
+                                Objects.equals(portName, osNode.vlanIntf()) ||
+                                Objects.equals(portName, PATCH_INTG_BRIDGE) ||
+                                Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
+                            // FIXME we never gets PATCH_ROUTE_BRIDGE port added events as of now
+                            log.debug("Interface {} added to {}", portName, event.subject().id());
+                            bootstrapNode(osNode);
+                        }
+                    });
+                    break;
+                case PORT_REMOVED:
+                    eventExecutor.execute(() -> {
+                        Port port = event.port();
+                        String portName = port.annotations().value(PORT_NAME);
+                        if (osNode.state() == COMPLETE && (
+                                Objects.equals(portName, DEFAULT_TUNNEL) ||
+                                Objects.equals(portName, osNode.vlanIntf()) ||
+                                Objects.equals(portName, PATCH_INTG_BRIDGE) ||
+                                Objects.equals(portName, PATCH_ROUT_BRIDGE))) {
+                            log.warn("Interface {} removed from {}", portName, event.subject().id());
+                            setState(osNode, INCOMPLETE);
+                        }
+                    });
+                    break;
+                case PORT_UPDATED:
+                case DEVICE_REMOVED:
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+    }
+
+    private class InternalGroupListener implements GroupListener {
+
+        @Override
+        public boolean isRelevant(GroupEvent event) {
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNode, leader);
+        }
+
+        @Override
+        public void event(GroupEvent event) {
+            switch (event.type()) {
+                case GROUP_ADDED:
+                    log.trace("Group added, ID:{} state:{}", event.subject().id(),
+                            event.subject().state());
+                    eventExecutor.execute(() -> {
+                        OpenstackNode osNode = osNodeByGroupId(event.subject().id());
+                        if (osNode != null && osNode.state() == PORT_CREATED) {
+                            setState(osNode, COMPLETE);
+                        }
+                    });
+                    break;
+                case GROUP_UPDATED:
+                    log.trace("Group updated, ID:{} state:{}", event.subject().id(),
+                            event.subject().state());
+                    eventExecutor.execute(() -> {
+                        osNodeService.nodes(GATEWAY).stream()
+                                .filter(osNode -> osNode.state() == PORT_CREATED)
+                                .forEach(osNode -> bootstrapNode(osNode));
+                    });
+                    break;
+                case GROUP_REMOVED:
+                    // TODO handle group removed
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        private OpenstackNode osNodeByGroupId(GroupId groupId) {
+            return osNodeService.nodes().stream()
+                    .filter(n -> n.gatewayGroupId(VXLAN).equals(groupId) ||
+                            n.gatewayGroupId(VLAN).equals(groupId))
+                    .findAny().orElse(null);
+        }
+    }
+
+    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNodeEvent event) {
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNode, leader);
+        }
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_NODE_CREATED:
+                case OPENSTACK_NODE_UPDATED:
+                    eventExecutor.execute(() -> {
+                        bootstrapNode(event.subject());
+                    });
+                    break;
+                case OPENSTACK_NODE_COMPLETE:
+                    break;
+                case OPENSTACK_NODE_REMOVED:
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
new file mode 100644
index 0000000..f248367
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/DistributedOpenstackNodeStore.java
@@ -0,0 +1,200 @@
+/*
+ * 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.openstacknode.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.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.openstacknode.api.NodeState;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeStore;
+import org.onosproject.openstacknode.api.OpenstackNodeStoreDelegate;
+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.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.openstacknode.api.NodeState.COMPLETE;
+import static org.onosproject.openstacknode.api.NodeState.INCOMPLETE;
+import static org.onosproject.openstacknode.api.OpenstackNodeEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of openstack node store using consistent map.
+ */
+@Service
+@Component(immediate = true)
+public class DistributedOpenstackNodeStore
+        extends AbstractStore<OpenstackNodeEvent, OpenstackNodeStoreDelegate>
+        implements OpenstackNodeStore {
+
+    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_OPENSTACK_NODE = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(OpenstackNode.class)
+            .register(DefaultOpenstackNode.class)
+            .register(OpenstackNode.NodeType.class)
+            .register(NodeState.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, OpenstackNode> osNodeMapListener =
+            new OpenstackNodeMapListener();
+    private ConsistentMap<String, OpenstackNode> osNodeStore;
+
+    @Activate
+    protected void activate() {
+        ApplicationId appId = coreService.registerApplication("org.onosproject.openstacknode");
+        osNodeStore = storageService.<String, OpenstackNode>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_OPENSTACK_NODE))
+                .withName("openstack-nodestore")
+                .withApplicationId(appId)
+                .build();
+        osNodeStore.addListener(osNodeMapListener);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osNodeStore.removeListener(osNodeMapListener);
+        eventExecutor.shutdown();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createNode(OpenstackNode osNode) {
+        osNodeStore.compute(osNode.hostname(), (hostname, existing) -> {
+            final String error = osNode.hostname() + ERR_DUPLICATE;
+            checkArgument(existing == null, error);
+            return osNode;
+        });
+    }
+
+    @Override
+    public void updateNode(OpenstackNode osNode) {
+        osNodeStore.compute(osNode.hostname(), (hostname, existing) -> {
+            final String error = osNode.hostname() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return osNode;
+        });
+    }
+
+    @Override
+    public OpenstackNode removeNode(String hostname) {
+        Versioned<OpenstackNode> osNode = osNodeStore.remove(hostname);
+        if (osNode == null) {
+            final String error = hostname + ERR_NOT_FOUND;
+            throw new IllegalArgumentException(error);
+        }
+        return osNode.value();
+    }
+
+    @Override
+    public Set<OpenstackNode> nodes() {
+        Set<OpenstackNode> osNodes = osNodeStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osNodes);
+    }
+
+    @Override
+    public OpenstackNode node(String hostname) {
+        Versioned<OpenstackNode> osNode = osNodeStore.get(hostname);
+        return osNode == null ? null : osNode.value();
+    }
+
+    private class OpenstackNodeMapListener implements MapEventListener<String, OpenstackNode> {
+
+        @Override
+        public void event(MapEvent<String, OpenstackNode> event) {
+            switch (event.type()) {
+                case INSERT:
+                    log.debug("OpenStack node created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNodeEvent(
+                                OPENSTACK_NODE_CREATED,
+                                event.newValue().value()
+                        ));
+                    });
+                    break;
+                case UPDATE:
+                    log.debug("OpenStack node updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNodeEvent(
+                                OPENSTACK_NODE_UPDATED,
+                                event.newValue().value()
+                        ));
+                        if (event.newValue().value().state() == COMPLETE) {
+                            notifyDelegate(new OpenstackNodeEvent(
+                                    OPENSTACK_NODE_COMPLETE,
+                                    event.newValue().value()
+                            ));
+                        } else if (event.newValue().value().state() == INCOMPLETE) {
+                            notifyDelegate(new OpenstackNodeEvent(
+                                    OPENSTACK_NODE_INCOMPLETE,
+                                    event.newValue().value()
+                            ));
+                        }
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("OpenStack node removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new OpenstackNodeEvent(
+                                OPENSTACK_NODE_REMOVED,
+                                event.oldValue().value()
+                        ));
+                    });
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java
new file mode 100644
index 0000000..65d565a
--- /dev/null
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/OpenstackNodeManager.java
@@ -0,0 +1,264 @@
+/*
+ * 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.openstacknode.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.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.event.ListenerRegistry;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.openstacknode.api.OpenstackNodeConfig;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeAdminService;
+import org.onosproject.openstacknode.api.OpenstackNodeEvent;
+import org.onosproject.openstacknode.api.OpenstackNodeListener;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+import org.onosproject.openstacknode.api.OpenstackNodeStore;
+import org.onosproject.openstacknode.api.OpenstackNodeStoreDelegate;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknode.api.NodeState.COMPLETE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Service administering the inventory of openstack nodes.
+ */
+@Service
+@Component(immediate = true)
+public class OpenstackNodeManager extends ListenerRegistry<OpenstackNodeEvent, OpenstackNodeListener>
+        implements OpenstackNodeService, OpenstackNodeAdminService {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String MSG_NODE = "OpenStack node %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_NODE = "OpenStack node cannot be null";
+    private static final String ERR_NULL_HOSTNAME = "OpenStack node hostname cannot be null";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeStore osNodeStore;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private final ConfigFactory configFactory =
+            new ConfigFactory<ApplicationId, OpenstackNodeConfig>(
+                    SubjectFactories.APP_SUBJECT_FACTORY, OpenstackNodeConfig.class, "openstacknode") {
+                @Override
+                public OpenstackNodeConfig createConfig() {
+                    return new OpenstackNodeConfig();
+                }
+            };
+
+    private final NetworkConfigListener configListener = new InternalConfigListener();
+    private final OpenstackNodeStoreDelegate delegate = new InternalNodeStoreDelegate();
+
+    private ApplicationId appId;
+    private NodeId localNode;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(APP_ID);
+        osNodeStore.setDelegate(delegate);
+
+        localNode = clusterService.getLocalNode().id();
+        leadershipService.runForLeadership(appId.name());
+
+        configRegistry.registerConfigFactory(configFactory);
+        configRegistry.addListener(configListener);
+
+        readConfiguration();
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        osNodeStore.unsetDelegate(delegate);
+        configRegistry.removeListener(configListener);
+        configRegistry.unregisterConfigFactory(configFactory);
+
+        leadershipService.withdraw(appId.name());
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createNode(OpenstackNode osNode) {
+        checkNotNull(osNode, ERR_NULL_NODE);
+        osNodeStore.createNode(osNode);
+        log.info(String.format(MSG_NODE, osNode.hostname(), MSG_CREATED));
+    }
+
+    @Override
+    public void updateNode(OpenstackNode osNode) {
+        checkNotNull(osNode, ERR_NULL_NODE);
+        osNodeStore.updateNode(osNode);
+        log.info(String.format(MSG_NODE, osNode.hostname(), MSG_UPDATED));
+    }
+
+    @Override
+    public OpenstackNode removeNode(String hostname) {
+        checkArgument(!Strings.isNullOrEmpty(hostname), ERR_NULL_HOSTNAME);
+        OpenstackNode osNode = osNodeStore.removeNode(hostname);
+        log.info(String.format(MSG_NODE, hostname, MSG_REMOVED));
+        return osNode;
+    }
+
+    @Override
+    public Set<OpenstackNode> nodes() {
+        return osNodeStore.nodes();
+    }
+
+    @Override
+    public Set<OpenstackNode> nodes(OpenstackNode.NodeType type) {
+        Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
+                .filter(osNode -> Objects.equals(osNode.type(), type))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osNodes);
+    }
+
+    @Override
+    public Set<OpenstackNode> completeNodes() {
+        Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
+                .filter(osNode -> Objects.equals(osNode.state(), COMPLETE))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osNodes);
+    }
+
+    @Override
+    public Set<OpenstackNode> completeNodes(OpenstackNode.NodeType type) {
+        Set<OpenstackNode> osNodes = osNodeStore.nodes().stream()
+                .filter(osNode -> osNode.type() == type &&
+                        Objects.equals(osNode.state(), COMPLETE))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(osNodes);
+    }
+
+    @Override
+    public OpenstackNode node(String hostname) {
+        return osNodeStore.node(hostname);
+    }
+
+    @Override
+    public OpenstackNode node(DeviceId deviceId) {
+        OpenstackNode result = osNodeStore.nodes().stream()
+                .filter(osNode -> Objects.equals(osNode.intgBridge(), deviceId) ||
+                        Objects.equals(osNode.ovsdb(), deviceId) ||
+                        Objects.equals(osNode.routerBridge(), deviceId))
+                .findFirst().orElse(null);
+        return result;
+    }
+
+    private class InternalNodeStoreDelegate implements OpenstackNodeStoreDelegate {
+
+        @Override
+        public void notify(OpenstackNodeEvent event) {
+            if (event != null) {
+                log.trace("send openstack node event {}", event);
+                process(event);
+            }
+        }
+    }
+
+    private void readConfiguration() {
+        OpenstackNodeConfig config = configRegistry.getConfig(appId, OpenstackNodeConfig.class);
+        if (config == null) {
+            log.debug("No configuration found");
+            return;
+        }
+
+        log.info("Read openstack node configurations...");
+        Set<String> hostnames = config.openstackNodes().stream()
+                .map(OpenstackNode::hostname)
+                .collect(Collectors.toSet());
+        nodes().stream().filter(osNode -> !hostnames.contains(osNode.hostname()))
+                .forEach(osNode -> removeNode(osNode.hostname()));
+
+        config.openstackNodes().forEach(osNode -> {
+            OpenstackNode existing = node(osNode.hostname());
+            if (existing == null) {
+                createNode(osNode);
+            } else if (!existing.equals(osNode)) {
+                updateNode(osNode);
+            }
+        });
+    }
+
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            NodeId leaderNodeId = leadershipService.getLeader(appId.name());
+            if (!Objects.equals(localNode, leaderNodeId)) {
+                // do not allow to proceed without leadership
+                return;
+            }
+
+            if (!event.configClass().equals(OpenstackNodeConfig.class)) {
+                return;
+            }
+
+            switch (event.type()) {
+                case CONFIG_ADDED:
+                case CONFIG_UPDATED:
+                    eventExecutor.execute(OpenstackNodeManager.this::readConfiguration);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/package-info.java
similarity index 86%
rename from apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
rename to apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/package-info.java
index c280f54..4982c44 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/package-info.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/impl/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.
@@ -17,4 +17,4 @@
 /**
  * Application for bootstrapping Compute/Gateway Node in OpenStack.
  */
-package org.onosproject.openstacknode;
\ No newline at end of file
+package org.onosproject.openstacknode.impl;
\ No newline at end of file