CORD-176, CORD-431 Switch to new VTN API

- Implemented VTN store and manager to keep network states
- Implemented the new VTN API for service network and port

Change-Id: Id8f5d0e609fa3deba782ff2265fc0d3175cbcb4c
diff --git a/pom.xml b/pom.xml
index 517b5a8..de815af 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,7 +43,6 @@
             org.onosproject.ovsdb-base,
             org.onosproject.drivers.ovsdb,
             org.onosproject.dhcp,
-            org.onosproject.xosclient,
             org.opencord.config
         </onos.app.requires>
         <web.context>/onos/cordvtn</web.context>
@@ -128,14 +127,24 @@
             <version>0.1.53</version>
         </dependency>
         <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-apps-xosclient</artifactId>
-            <version>${onos.version}</version>
-        </dependency>
-        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.scr.annotations</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.pacesys</groupId>
+            <artifactId>openstack4j-core</artifactId>
+            <version>2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>org.pacesys.openstack4j.connectors</groupId>
+            <artifactId>openstack4j-http-connector</artifactId>
+            <version>2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>org.pacesys.openstack4j.connectors</groupId>
+            <artifactId>openstack4j-httpclient</artifactId>
+            <version>2.11</version>
+        </dependency>
 
         <!-- TODO FIX ONOS ROOT POM to split thirdparty and onos dependencies -->
         <dependency>
@@ -216,8 +225,16 @@
                             ${project.groupId}.${project.artifactId}
                         </Bundle-SymbolicName>
                         <Import-Package>
+                            !org.apache.http.*,
+                            !com.fasterxml.jackson.dataformat.*,
+                            !javax.annotation,
                             *,org.glassfish.jersey.servlet
                         </Import-Package>
+                        <Embed-Dependency>
+                            openstack4j-core,
+                            openstack4j-http-connector,
+                            openstack4j-httpclient
+                        </Embed-Dependency>
                         <Web-ContextPath>${web.context}</Web-ContextPath>
                     </instructions>
                 </configuration>
diff --git a/src/main/java/org/opencord/cordvtn/api/CordVtnAdminService.java b/src/main/java/org/opencord/cordvtn/api/CordVtnAdminService.java
new file mode 100644
index 0000000..afb2a1e
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/CordVtnAdminService.java
@@ -0,0 +1,131 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+/**
+ * Service for administering the inventory of virtual network and service network.
+ */
+public interface CordVtnAdminService extends CordVtnService {
+
+    /**
+     * Creates vtn port with given service port information.
+     *
+     * @param servicePort the new service port
+     */
+    void createVtnPort(ServicePort servicePort);
+
+    /**
+     * Updates vtn port with given service port information.
+     *
+     * @param servicePort the updated service port
+     */
+    void updateVtnPort(ServicePort servicePort);
+
+    /**
+     * Removes vtn port with given port id.
+     *
+     * @param portId port id
+     */
+    void removeVtnPort(PortId portId);
+
+    /**
+     * Creates vtn network with given service network information.
+     *
+     * @param serviceNet the new service network
+     */
+    void createVtnNetwork(ServiceNetwork serviceNet);
+
+    /**
+     * Updates the vtn network with given service network information.
+     *
+     * @param serviceNet the updated service network
+     */
+    void updateVtnNetwork(ServiceNetwork serviceNet);
+
+    /**
+     * Removes the vtn network.
+     *
+     * @param netId network id
+     */
+    void removeVtnNetwork(NetworkId netId);
+
+    /**
+     * Creates a port.
+     *
+     * @param port port
+     */
+    void createPort(Port port);
+
+    /**
+     * Updates the port.
+     *
+     * @param port the updated port
+     */
+    void updatePort(Port port);
+
+    /**
+     * Removes the port with the given port id.
+     *
+     * @param portId port id
+     */
+    void removePort(PortId portId);
+
+    /**
+     * Creates a network.
+     *
+     * @param network network
+     */
+    void createNetwork(Network network);
+
+    /**
+     * Updates the network.
+     *
+     * @param network the updated network
+     */
+    void updateNetwork(Network network);
+
+    /**
+     * Removes the network with the given network id.
+     *
+     * @param netId network id
+     */
+    void removeNetwork(NetworkId netId);
+
+    /**
+     * Creates a subnet.
+     *
+     * @param subnet subnet id
+     */
+    void createSubnet(Subnet subnet);
+
+    /**
+     * Updates the subnet.
+     *
+     * @param subnet the updated subnet
+     */
+    void updateSubnet(Subnet subnet);
+
+    /**
+     * Removes the subnet with the given subnet id.
+     *
+     * @param subnetId subnet id
+     */
+    void removeSubnet(SubnetId subnetId);
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java b/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
index 41e869d..d0a9562 100644
--- a/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
+++ b/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
@@ -25,8 +25,6 @@
 import org.onlab.packet.TpPort;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.net.config.Config;
-import org.onosproject.xosclient.api.OpenStackAccess;
-import org.onosproject.xosclient.api.XosAccess;
 import org.slf4j.Logger;
 
 import java.util.Map;
@@ -236,28 +234,5 @@
         return publicGateways;
     }
 
-    /**
-     * Returns XOS access information.
-     *
-     * @return XOS access, or null
-     */
-    public XosAccess xosAccess() {
-        JsonNode jsonNode = object.get(XOS);
-        return new XosAccess(getConfig(jsonNode, ENDPOINT),
-                             getConfig(jsonNode, USER),
-                             getConfig(jsonNode, PASSWORD));
-    }
-
-    /**
-     * Returns OpenStack API access information.
-     *
-     * @return openstack access
-     */
-    public OpenStackAccess openstackAccess() {
-        JsonNode jsonNode = object.get(OPENSTACK);
-        return new OpenStackAccess(jsonNode.path(ENDPOINT).asText(),
-                                   jsonNode.path(TENANT).asText(),
-                                   jsonNode.path(USER).asText(),
-                                   jsonNode.path(PASSWORD).asText());
-    }
+    // TODO add methods to get XOS and OpenStack API access
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/CordVtnService.java b/src/main/java/org/opencord/cordvtn/api/CordVtnService.java
new file mode 100644
index 0000000..5b0849e
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/CordVtnService.java
@@ -0,0 +1,149 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import org.onosproject.event.ListenerService;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
+import java.util.Set;
+
+/**
+ * Service for interacting with the inventory of VTN network and port.
+ */
+public interface CordVtnService
+        extends ListenerService<VtnNetworkEvent, VtnNetworkListener> {
+
+    /**
+     * Returns the service port with the given port id.
+     *
+     * @param portId port id
+     * @return service port
+     */
+    VtnPort getVtnPort(PortId portId);
+
+    /**
+     * Returns the vtn port with the given port id. It returns the VTN port with
+     * the default settings if no service port exists for the port.
+     *
+     * @param portId port id
+     * @return vtn port for the port, of the default vtn port if no service port
+     * exists for the port
+     */
+    VtnPort getVtnPortOrDefault(PortId portId);
+
+    /**
+     * Returns the VTN port with the given port name.
+     *
+     * @param portName port name
+     * @return vtn port
+     */
+    VtnPort getVtnPort(String portName);
+
+    /**
+     * Returns all service ports.
+     *
+     * @return set of service ports
+     */
+    Set<VtnPort> getVtnPorts();
+
+    /**
+     * Returns the service network with the given network id.
+     *
+     * @param netId network id
+     * @return service network
+     */
+    VtnNetwork getVtnNetwork(NetworkId netId);
+
+    /**
+     * Returns the vtn network with the given network id. It returns the VTN
+     * network with default settings if no service network exists for the network.
+     *
+     * @param netId network id
+     * @return vtn network for the network id, or the default vtn network if no
+     * service network is created for the network
+     */
+    VtnNetwork getVtnNetworkOrDefault(NetworkId netId);
+
+    /**
+     * Returns all service networks.
+     *
+     * @return set of service networks
+     */
+    Set<VtnNetwork> getVtnNetworks();
+
+    /**
+     * Returns the port with the given port id.
+     *
+     * @param portId port id
+     * @return port
+     */
+    Port getPort(PortId portId);
+
+    /**
+     * Returns all ports.
+     *
+     * @return set of ports
+     */
+    Set<Port> getPorts();
+
+    /**
+     * Returns the network with the given network id.
+     *
+     * @param netId network id
+     * @return network
+     */
+    Network getNetwork(NetworkId netId);
+
+    /**
+     * Returns all networks.
+     *
+     * @return set of networks
+     */
+    Set<Network> getNetworks();
+
+    /**
+     * Returns the subnet with the given subnet id.
+     *
+     * @param subnetId subnet id
+     * @return subnet
+     */
+    Subnet getSubnet(SubnetId subnetId);
+
+    /**
+     * Returns all subnets.
+     *
+     * @return set of subnets
+     */
+    Set<Subnet> getSubnets();
+
+    /**
+     * Returns instance attached to the given port.
+     *
+     * @param portId port identifier
+     * @return instance, or null if no instance for the port
+     */
+    Instance getInstance(PortId portId);
+
+    /**
+     * Returns instances in the given network.
+     *
+     * @param netId network identifier
+     * @return set of instances, empty set if no instances in the network
+     */
+    Set<Instance> getInstances(NetworkId netId);
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/CordVtnStore.java b/src/main/java/org/opencord/cordvtn/api/CordVtnStore.java
index d6d7a9b..bfa84f7 100644
--- a/src/main/java/org/opencord/cordvtn/api/CordVtnStore.java
+++ b/src/main/java/org/opencord/cordvtn/api/CordVtnStore.java
@@ -15,77 +15,195 @@
  */
 package org.opencord.cordvtn.api;
 
+import org.onosproject.store.Store;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+
 import java.util.Set;
 
 /**
- * Manages VTN service networks and ports.
+ * Manages inventory of virtual and vtn networks; not intended for direct use.
  */
-public interface CordVtnStore {
+public interface CordVtnStore extends Store<VtnNetworkEvent, CordVtnStoreDelegate> {
 
     /**
-     * Creates service network.
+     * Creates vtn network.
      *
-     * @param serviceNet the new service network
+     * @param serviceNet the new vtn network
      */
-    void createServiceNetwork(ServiceNetwork serviceNet);
+    void createVtnNetwork(VtnNetwork serviceNet);
 
     /**
-     * Updates the service network.
+     * Updates the vtn network.
      *
-     * @param serviceNet the updated service network
+     * @param serviceNet the updated vtn network
      */
-    void updateServiceNetwork(ServiceNetwork serviceNet);
+    void updateVtnNetwork(VtnNetwork serviceNet);
 
     /**
-     * Returns the service network with the given network id.
+     * Returns the vtn network with the given network id.
      *
      * @param netId network id
-     * @return service network
+     * @return vtn network
      */
-    ServiceNetwork getServiceNetwork(NetworkId netId);
+    VtnNetwork getVtnNetwork(NetworkId netId);
 
     /**
-     * Returns all service networks.
+     * Returns all vtn networks.
      *
-     * @return set of service networks
+     * @return set of vtn networks
      */
-    Set<ServiceNetwork> getServiceNetworks();
+    Set<VtnNetwork> getVtnNetworks();
 
     /**
-     * Removes the service network.
+     * Removes the vtn network.
      *
      * @param netId network id
      */
-    void removeServiceNetwork(NetworkId netId);
+    void removeVtnNetwork(NetworkId netId);
 
     /**
-     * Creates service port.
+     * Creates vtn port.
      *
-     * @param servicePort the new service port
+     * @param servicePort the new vtn port
      */
-    void createServicePort(ServicePort servicePort);
+    void createVtnPort(VtnPort servicePort);
 
     /**
-     * Returns the service port with the given port id.
+     * Updates the vtn port.
+     *
+     * @param servicePort vtn port
+     */
+    void updateVtnPort(VtnPort servicePort);
+
+    /**
+     * Returns the vtn port with the given port id.
      *
      * @param portId port id
-     * @return service port
+     * @return vtn port
      */
-    ServicePort getServicePort(PortId portId);
+    VtnPort getVtnPort(PortId portId);
 
     /**
-     * Returns all service ports.
+     * Returns all vtn ports.
      *
-     * @return set of service ports
+     * @return set of vtn ports
      */
-    Set<ServicePort> getServicePorts();
+    Set<VtnPort> getVtnPorts();
 
     /**
-     * Removes service port.
+     * Removes vtn port.
      *
      * @param portId port id
      */
-    void removeServicePort(PortId portId);
+    void removeVtnPort(PortId portId);
 
-    // TODO add apis for the virtual network and port
+    /**
+     * Creates a network.
+     *
+     * @param net network
+     */
+    void createNetwork(Network net);
+
+    /**
+     * Updates the network.
+     *
+     * @param net the updated network
+     */
+    void updateNetwork(Network net);
+
+    /**
+     * Returns the network with the given network id.
+     *
+     * @param netId network id
+     * @return network
+     */
+    Network getNetwork(NetworkId netId);
+
+    /**
+     * Returns all networks.
+     *
+     * @return set of networks
+     */
+    Set<Network> getNetworks();
+
+    /**
+     * Removes the network with the given network id.
+     *
+     * @param netId network id
+     */
+    void removeNetwork(NetworkId netId);
+
+    /**
+     * Creates a port.
+     *
+     * @param port port
+     */
+    void createPort(Port port);
+
+    /**
+     * Updates the port.
+     *
+     * @param port the updated port
+     */
+    void updatePort(Port port);
+
+    /**
+     * Returns the port with the given port id.
+     *
+     * @param portId port id
+     * @return port
+     */
+    Port getPort(PortId portId);
+
+    /**
+     * Returns all ports.
+     *
+     * @return set of ports
+     */
+    Set<Port> getPorts();
+
+    /**
+     * Removes the port with the given port id.
+     *
+     * @param portId port id
+     */
+    void removePort(PortId portId);
+
+    /**
+     * Creates a subnet.
+     *
+     * @param subnet subnet id
+     */
+    void createSubnet(Subnet subnet);
+
+    /**
+     * Updates the subnet.
+     *
+     * @param subnet the updated subnet
+     */
+    void updateSubnet(Subnet subnet);
+
+    /**
+     * Returns the subnet with the given subnet id.
+     *
+     * @param subnetId subnet id
+     * @return subnet
+     */
+    Subnet getSubnet(SubnetId subnetId);
+
+    /**
+     * Returns all subnets.
+     *
+     * @return set of subnets
+     */
+    Set<Subnet> getSubnets();
+
+    /**
+     * Removes the subnet with the given subnet id.
+     *
+     * @param subnetId subnet id
+     */
+    void removeSubnet(SubnetId subnetId);
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/CordVtnStoreDelegate.java b/src/main/java/org/opencord/cordvtn/api/CordVtnStoreDelegate.java
new file mode 100644
index 0000000..eb43f92
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/CordVtnStoreDelegate.java
@@ -0,0 +1,24 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import org.onosproject.store.StoreDelegate;
+
+/**
+ * VTN store delegate abstraction.
+ */
+public interface CordVtnStoreDelegate extends StoreDelegate<VtnNetworkEvent> {
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/Dependency.java b/src/main/java/org/opencord/cordvtn/api/Dependency.java
new file mode 100644
index 0000000..bbe7ef5
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/Dependency.java
@@ -0,0 +1,168 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a dependency between two networks, subscriber and provider.
+ */
+public final class Dependency {
+
+    public enum Type {
+        BIDIRECTIONAL,
+        UNIDIRECTIONAL
+    }
+
+    private final VtnNetwork subscriber;
+    private final VtnNetwork provider;
+    private final Type type;
+
+    private Dependency(VtnNetwork subscriber, VtnNetwork provider, Type type) {
+        this.subscriber = subscriber;
+        this.provider = provider;
+        this.type = type;
+    }
+
+    /**
+     * Returns subscriber network.
+     *
+     * @return vtn network
+     */
+    public VtnNetwork subscriber() {
+        return subscriber;
+    }
+
+    /**
+     * Returns provider network.
+     *
+     * @return vtn network
+     */
+    public VtnNetwork provider() {
+        return provider;
+    }
+
+    /**
+     * Returns direct access type between subscriber and provider networks.
+     *
+     * @return type
+     */
+    public Type type() {
+        return type;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof Dependency) {
+            Dependency that = (Dependency) obj;
+            if (Objects.equals(subscriber, that.subscriber) &&
+                    Objects.equals(provider, that.provider) &&
+                    Objects.equals(type, that.type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(subscriber, provider, type);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("subscriber", subscriber.id())
+                .add("provider", provider.id())
+                .add("type", type)
+                .toString();
+    }
+
+    /**
+     * Returns new dependency builder instance.
+     *
+     * @return dependency
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of the dependency entities.
+     */
+    public static final class Builder {
+        private VtnNetwork subscriber;
+        private VtnNetwork provider;
+        private Type type;
+
+        private Builder() {
+        }
+
+        /**
+         * Builds an immutable dependency.
+         *
+         * @return dependency instance
+         */
+        public Dependency build() {
+            checkNotNull(subscriber);
+            checkNotNull(provider);
+            checkNotNull(type);
+
+            return new Dependency(subscriber, provider, type);
+        }
+
+        /**
+         * Returns dependency with the supplied subscriber.
+         *
+         * @param subscriber subscriber network
+         * @return dependency builder
+         */
+        public Builder subscriber(VtnNetwork subscriber) {
+            this.subscriber = subscriber;
+            return this;
+        }
+
+        /**
+         * Returns dependency with the supplied provider.
+         *
+         * @param provider provider network
+         * @return dependency builder
+         */
+        public Builder provider(VtnNetwork provider) {
+            this.provider = provider;
+            return this;
+        }
+
+        /**
+         * Returns dependency with the supplied type.
+         *
+         * @param type type
+         * @return dependency builder
+         */
+        public Builder type(Type type) {
+            this.type = type;
+            return this;
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/DependencyService.java b/src/main/java/org/opencord/cordvtn/api/DependencyService.java
index 48905da..08ad58d 100644
--- a/src/main/java/org/opencord/cordvtn/api/DependencyService.java
+++ b/src/main/java/org/opencord/cordvtn/api/DependencyService.java
@@ -15,7 +15,7 @@
  */
 package org.opencord.cordvtn.api;
 
-import org.onosproject.xosclient.api.VtnServiceId;
+import org.opencord.cordvtn.api.Dependency.Type;
 
 /**
  * Provides dependency services.
@@ -25,17 +25,17 @@
     /**
      * Creates dependencies for a given tenant service.
      *
-     * @param tServiceId id of the service which has a dependency
-     * @param pServiceId id of the service which provide dependency
-     * @param isBidirectional true to enable bidirectional connectivity between two services
+     * @param subscriber subscriber network id
+     * @param provider   provider network id
+     * @param type       bidirectional access type
      */
-    void createDependency(VtnServiceId tServiceId, VtnServiceId pServiceId, boolean isBidirectional);
+    void createDependency(NetworkId subscriber, NetworkId provider, Type type);
 
     /**
      * Removes all dependencies from a given tenant service.
      *
-     * @param tServiceId id of the service which has a dependency
-     * @param pServiceId id of the service which provide dependency
+     * @param subscriber subscriber network id
+     * @param provider   provider network id
      */
-    void removeDependency(VtnServiceId tServiceId, VtnServiceId pServiceId);
+    void removeDependency(NetworkId subscriber, NetworkId provider);
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/Instance.java b/src/main/java/org/opencord/cordvtn/api/Instance.java
index 23841b6..d81440e 100644
--- a/src/main/java/org/opencord/cordvtn/api/Instance.java
+++ b/src/main/java/org/opencord/cordvtn/api/Instance.java
@@ -21,21 +21,19 @@
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.PortNumber;
-import org.onosproject.xosclient.api.VtnPortId;
-import org.onosproject.xosclient.api.VtnServiceApi.ServiceType;
-import org.onosproject.xosclient.api.VtnServiceId;
+import org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
- * Provides methods to help to handle network service instance.
+ * Provides methods to help to handle network network instance.
  */
 public final class Instance {
 
-    public static final String SERVICE_ID = "serviceId";
-    public static final String SERVICE_TYPE = "serviceType";
-    public static final String PORT_ID = "vtnPortId";
+    public static final String NETWORK_ID = "networkId";
+    public static final String NETWORK_TYPE = "networkType";
+    public static final String PORT_ID = "portId";
     public static final String CREATE_TIME = "createTime";
     public static final String NESTED_INSTANCE = "nestedInstance";
     public static final String TRUE = "true";
@@ -68,8 +66,8 @@
      */
     public static Instance of(Host host) {
         checkNotNull(host);
-        checkArgument(!Strings.isNullOrEmpty(host.annotations().value(SERVICE_ID)));
-        checkArgument(!Strings.isNullOrEmpty(host.annotations().value(SERVICE_TYPE)));
+        checkArgument(!Strings.isNullOrEmpty(host.annotations().value(NETWORK_ID)));
+        checkArgument(!Strings.isNullOrEmpty(host.annotations().value(NETWORK_TYPE)));
         checkArgument(!Strings.isNullOrEmpty(host.annotations().value(PORT_ID)));
         checkArgument(!Strings.isNullOrEmpty(host.annotations().value(CREATE_TIME)));
 
@@ -77,33 +75,33 @@
     }
 
     /**
-     * Returns service ID of a given host.
+     * Returns network ID of a given host.
      *
-     * @return vtn service id
+     * @return network id
      */
-    public VtnServiceId serviceId() {
-        String serviceId = host.annotations().value(SERVICE_ID);
-        return VtnServiceId.of(serviceId);
+    public NetworkId netId() {
+        String netId = host.annotations().value(NETWORK_ID);
+        return NetworkId.of(netId);
     }
 
     /**
-     * Returns service type of a given host.
+     * Returns network type of a given host.
      *
-     * @return vtn service type
+     * @return network type
      */
-    public ServiceType serviceType() {
-        String serviceType = host.annotations().value(SERVICE_TYPE);
-        return ServiceType.valueOf(serviceType);
+    public ServiceNetworkType netType() {
+        String netType = host.annotations().value(NETWORK_TYPE);
+        return ServiceNetworkType.valueOf(netType);
     }
 
     /**
      * Returns port ID of a given host.
      *
-     * @return vtn port id
+     * @return port id
      */
-    public VtnPortId portId() {
+    public PortId portId() {
         String portId = host.annotations().value(PORT_ID);
-        return VtnPortId.of(portId);
+        return PortId.of(portId);
     }
 
     /**
diff --git a/src/main/java/org/opencord/cordvtn/api/InstanceHandler.java b/src/main/java/org/opencord/cordvtn/api/InstanceHandler.java
index ea44857..a2364fd 100644
--- a/src/main/java/org/opencord/cordvtn/api/InstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/api/InstanceHandler.java
@@ -28,6 +28,13 @@
     void instanceDetected(Instance instance);
 
     /**
+     * Handles updated instance.
+     *
+     * @param instance instance
+     */
+    void instanceUpdated(Instance instance);
+
+    /**
      * Handles removed instance.
      *
      * @param instance instance
diff --git a/src/main/java/org/opencord/cordvtn/api/InstanceService.java b/src/main/java/org/opencord/cordvtn/api/InstanceService.java
index a8e0f77..add968f 100644
--- a/src/main/java/org/opencord/cordvtn/api/InstanceService.java
+++ b/src/main/java/org/opencord/cordvtn/api/InstanceService.java
@@ -25,7 +25,8 @@
 public interface InstanceService {
 
     /**
-     * Adds a service instance on a given connect point.
+     * Adds a service instance on a given connect point. Or updates if the
+     * instance already exists.
      *
      * @param connectPoint connect point of the instance
      */
diff --git a/src/main/java/org/opencord/cordvtn/api/ProviderNetwork.java b/src/main/java/org/opencord/cordvtn/api/ProviderNetwork.java
new file mode 100644
index 0000000..9caabbd
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/ProviderNetwork.java
@@ -0,0 +1,97 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import com.google.common.base.MoreObjects;
+import org.opencord.cordvtn.api.Dependency.Type;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a provider network.
+ */
+public final class ProviderNetwork {
+
+    private final NetworkId id;
+    private final Type type;
+
+    private ProviderNetwork(NetworkId id, Type type) {
+        this.id = id;
+        this.type = type;
+    }
+
+    /**
+     * Returns network id.
+     *
+     * @return network id
+     */
+    public NetworkId id() {
+        return id;
+    }
+
+    /**
+     * Returns the direct access type with this provider network.
+     *
+     * @return direct access type
+     */
+    public Type type() {
+        return type;
+    }
+
+    /**
+     * Returns immutable provider network with the supplied network id and type.
+     *
+     * @param id   network id
+     * @param type direct access type
+     * @return provider network
+     */
+    public static ProviderNetwork of(NetworkId id, Type type) {
+        checkNotNull(id);
+        checkNotNull(type);
+        return new ProviderNetwork(id, type);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof ProviderNetwork) {
+            ProviderNetwork that = (ProviderNetwork) obj;
+            if (Objects.equals(id, that.id) &&
+                    Objects.equals(type, that.type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, type);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id)
+                .add("type", type)
+                .toString();
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/SegmentId.java b/src/main/java/org/opencord/cordvtn/api/SegmentId.java
new file mode 100644
index 0000000..e4101b3
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/SegmentId.java
@@ -0,0 +1,43 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import org.onlab.util.Identifier;
+
+/**
+ * Representation of the network segmentation identifier.
+ */
+public final class SegmentId extends Identifier<Long> {
+
+    /**
+     * Default constructor.
+     *
+     * @param id long segmentation identifier
+     */
+    private SegmentId(Long id) {
+        super(id);
+    }
+
+    /**
+     * Returns the segmentation identifier with the supplied value.
+     *
+     * @param id long segmentation identifier
+     * @return segmentation identifier
+     */
+    public static SegmentId of(Long id) {
+        return new SegmentId(id);
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/ServiceNetwork.java b/src/main/java/org/opencord/cordvtn/api/ServiceNetwork.java
index bc639da..6da6833 100644
--- a/src/main/java/org/opencord/cordvtn/api/ServiceNetwork.java
+++ b/src/main/java/org/opencord/cordvtn/api/ServiceNetwork.java
@@ -16,18 +16,20 @@
 package org.opencord.cordvtn.api;
 
 import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
+import com.google.common.collect.ImmutableSet;
 
-import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * Representation of a service network.
  */
-public final class ServiceNetwork {
+public class ServiceNetwork {
+
+    private static final String ERR_ID = "Service network ID cannot be null";
+    private static final String ERR_TYPE = "Service network type cannot be null";
 
     public enum ServiceNetworkType {
         PRIVATE,
@@ -38,21 +40,16 @@
         ACCESS_AGENT
     }
 
-    public enum DirectAccessType {
-        BIDIRECTIONAL,
-        UNIDIRECTIONAL
-    }
+    protected final NetworkId id;
+    protected final ServiceNetworkType type;
+    protected final Set<ProviderNetwork> providers;
 
-    private final NetworkId id;
-    private final ServiceNetworkType type;
-    private final Map<NetworkId, DirectAccessType> providers;
-
-    private ServiceNetwork(NetworkId id,
-                           ServiceNetworkType type,
-                           Map<NetworkId, DirectAccessType> providers) {
-        this.id = id;
-        this.type = type;
-        this.providers = providers;
+    public ServiceNetwork(NetworkId id,
+                          ServiceNetworkType type,
+                          Set<ProviderNetwork> providers) {
+        this.id = checkNotNull(id, ERR_ID);
+        this.type = checkNotNull(type, ERR_TYPE);
+        this.providers = providers == null ? ImmutableSet.of() : providers;
     }
 
     /**
@@ -78,10 +75,21 @@
      *
      * @return provider networks
      */
-    public Map<NetworkId, DirectAccessType> providers() {
+    public Set<ProviderNetwork> providers() {
         return providers;
     }
 
+    /**
+     * Returns if the given network is the provider of this network or not.
+     *
+     * @param netId network id
+     * @return true if the given network is the provider of this network
+     */
+    public boolean isProvider(NetworkId netId) {
+        return providers.stream().filter(p -> Objects.equals(p.id(), netId))
+                .findAny().isPresent();
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -112,86 +120,4 @@
                 .add("providers", providers)
                 .toString();
     }
-
-    /**
-     * Returns new service network builder instance.
-     *
-     * @return service network builder
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
-     * Builder of the service network entities.
-     */
-    public static final class Builder {
-        private NetworkId id;
-        private ServiceNetworkType type;
-        private Map<NetworkId, DirectAccessType> providers = Maps.newHashMap();
-
-        private Builder() {
-        }
-
-        /**
-         * Builds an immutable service network.
-         *
-         * @return service network instance
-         */
-        public ServiceNetwork build() {
-            checkNotNull(id, "Service network id cannot be null");
-            checkNotNull(type, "Service network type cannot be null");
-            providers = providers == null ? ImmutableMap.of() : providers;
-
-            return new ServiceNetwork(id, type, providers);
-        }
-
-        /**
-         * Returns service network builder with the supplied network ID.
-         *
-         * @param id network id
-         * @return service network builder
-         */
-        public Builder id(NetworkId id) {
-            this.id = id;
-            return this;
-        }
-
-        /**
-         * Returns service network builder with the supplied service network type.
-         *
-         * @param type service network type
-         * @return service network builder
-         */
-        public Builder type(ServiceNetworkType type) {
-            this.type = type;
-            return this;
-        }
-
-        /**
-         * Returns service network builder with the supplied provider service networks.
-         *
-         * @param providers provider service networks
-         * @return service network builder
-         */
-        public Builder providers(Map<NetworkId, DirectAccessType> providers) {
-            this.providers = providers;
-            return this;
-        }
-
-        /**
-         * Returns service network builder with the given additional provider network.
-         *
-         * @param id provider network id
-         * @param type direct access type to the provider network
-         * @return service network builder
-         */
-        public Builder addProvider(NetworkId id, DirectAccessType type) {
-            checkNotNull(id, "Provider network ID cannot be null");
-            checkNotNull(type, "Provider network type cannot be null");
-
-            this.providers.put(id, type);
-            return this;
-        }
-    }
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/ServicePort.java b/src/main/java/org/opencord/cordvtn/api/ServicePort.java
index 61b0e52..3ed18ee 100644
--- a/src/main/java/org/opencord/cordvtn/api/ServicePort.java
+++ b/src/main/java/org/opencord/cordvtn/api/ServicePort.java
@@ -17,7 +17,6 @@
 
 import com.google.common.base.MoreObjects;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
 import org.onlab.packet.VlanId;
 
 import java.util.Objects;
@@ -29,18 +28,20 @@
 /**
  * Representation of a service port.
  */
-public final class ServicePort {
+public class ServicePort {
 
-    private final PortId id;
-    private final VlanId vlanId;
-    private final Set<AddressPair> addressPairs;
+    private static final String ERR_ID = "Service port ID cannot be null";
 
-    private ServicePort(PortId id,
-                        VlanId vlanId,
-                        Set<AddressPair> addressPairs) {
-        this.id = id;
+    protected final PortId id;
+    protected final VlanId vlanId;
+    protected final Set<AddressPair> addressPairs;
+
+    public ServicePort(PortId id,
+                       VlanId vlanId,
+                       Set<AddressPair> addressPairs) {
+        this.id = checkNotNull(id, ERR_ID);
         this.vlanId = vlanId;
-        this.addressPairs = addressPairs;
+        this.addressPairs = addressPairs == null ? ImmutableSet.of() : addressPairs;
     }
 
     /**
@@ -100,83 +101,4 @@
                 .add("addressPairs", addressPairs)
                 .toString();
     }
-
-    /**
-     * Returns new service port builder instance.
-     *
-     * @return service port builder
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
-     * Builder of the service port entities.
-     */
-    public static final class Builder {
-        private PortId id;
-        private VlanId vlanId;
-        private Set<AddressPair> addressPairs = Sets.newHashSet();
-
-        private Builder() {
-        }
-
-        /**
-         * Builds an immutable service port.
-         *
-         * @return service port instance
-         */
-        public ServicePort build() {
-            checkNotNull(id, "ServicePort port id cannot be null");
-            addressPairs = addressPairs == null ? ImmutableSet.of() : addressPairs;
-
-            return new ServicePort(id, vlanId, addressPairs);
-        }
-
-        /**
-         * Returns service port builder with the supplied port port id.
-         *
-         * @param id port id
-         * @return service port builder
-         */
-        public Builder id(PortId id) {
-            this.id = id;
-            return this;
-        }
-
-        /**
-         * Returns service port builder with the supplied VLAN ID.
-         *
-         * @param vlanId vlan id
-         * @return service port builder
-         */
-        public Builder vlanId(VlanId vlanId) {
-            this.vlanId = vlanId;
-            return this;
-        }
-
-        /**
-         * Returns service port builder with the supplied address pairs.
-         *
-         * @param addressPairs set of address pairs
-         * @return service port builder
-         */
-        public Builder addressPairs(Set<AddressPair> addressPairs) {
-            this.addressPairs = addressPairs;
-            return this;
-        }
-
-        /**
-         * Returns service port builder with the given additional address pair.
-         *
-         * @param addressPair address pair to add
-         * @return service port builder
-         */
-        public Builder addAddressPair(AddressPair addressPair) {
-            checkNotNull(addressPair, "ServicePort address pair cannot be null");
-
-            this.addressPairs.add(addressPair);
-            return this;
-        }
-    }
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/SubnetId.java b/src/main/java/org/opencord/cordvtn/api/SubnetId.java
new file mode 100644
index 0000000..e98ebd8
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/SubnetId.java
@@ -0,0 +1,43 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import org.onlab.util.Identifier;
+
+/**
+ * Representation of the subnet identifier.
+ */
+public final class SubnetId extends Identifier<String> {
+
+    /**
+     * Default constructor.
+     *
+     * @param id string subnet identifier
+     */
+    private SubnetId(String id) {
+        super(id);
+    }
+
+    /**
+     * Returns the subnet identifier with the supplied value.
+     *
+     * @param id string subnet identifier
+     * @return subnet identifier
+     */
+    public static SubnetId of(String id) {
+        return new SubnetId(id);
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/VtnNetwork.java b/src/main/java/org/opencord/cordvtn/api/VtnNetwork.java
new file mode 100644
index 0000000..644a939
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/VtnNetwork.java
@@ -0,0 +1,322 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Subnet;
+
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.PRIVATE;
+
+/**
+ * Representation of the network containing all network information consumed by
+ * VTN service.
+ */
+public final class VtnNetwork extends ServiceNetwork {
+
+    private static final String ERR_SEGMENT_ID_MISSING = "VTN network segment ID is missing";
+    private static final String ERR_GATEWAY_IP_MISSING = "VTN subnet gateway IP is missing";
+
+    private final SegmentId segmentId;
+    private final IpPrefix subnet;
+    private final IpAddress serviceIp;
+
+    private VtnNetwork(NetworkId id,
+                       SegmentId segmentId,
+                       IpPrefix subnet,
+                       IpAddress serviceIp,
+                       ServiceNetworkType type,
+                       Set<ProviderNetwork> providers) {
+        super(id, type, providers);
+        this.segmentId = segmentId;
+        this.subnet = subnet;
+        this.serviceIp = serviceIp;
+    }
+
+    /**
+     * Returns the network ID.
+     *
+     * @return network id
+     */
+    public NetworkId id() {
+        return id;
+    }
+
+    /**
+     * Returns the segment ID of this network.
+     *
+     * @return segment id
+     */
+    public SegmentId segmentId() {
+        return segmentId;
+    }
+
+    /**
+     * Returns the subnet used in this network.
+     *
+     * @return subnet
+     */
+    public IpPrefix subnet() {
+        return subnet;
+    }
+
+    /**
+     * Returns the service IP address of this network.
+     *
+     * @return ip address
+     */
+    public IpAddress serviceIp() {
+        return serviceIp;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof VtnNetwork) {
+            VtnNetwork that = (VtnNetwork) obj;
+            if (Objects.equals(id, that.id) &&
+                    Objects.equals(segmentId, that.segmentId) &&
+                    Objects.equals(subnet, that.subnet) &&
+                    Objects.equals(serviceIp, that.serviceIp) &&
+                    Objects.equals(type, that.type) &&
+                    Objects.equals(providers, that.providers)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, segmentId, subnet, serviceIp, type, providers);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id)
+                .add("segmentId", segmentId)
+                .add("subnet", subnet)
+                .add("serviceIp", serviceIp)
+                .add("type", type)
+                .add("providers", providers)
+                .toString();
+    }
+
+    /**
+     * Returns immutable VTN network with the supplied Neutron network, subnet,
+     * and additional service network information.
+     *
+     * @param network    neutron network
+     * @param subnet     neutron subnet
+     * @param serviceNet service network
+     * @return vtn network
+     */
+    public static VtnNetwork of(Network network, Subnet subnet, ServiceNetwork serviceNet) {
+        validateNeutronNetwork(network, subnet);
+        if (serviceNet != null) {
+            checkArgument(Objects.equals(network.getId(), serviceNet.id().id()));
+        }
+
+        return builder().id(NetworkId.of(network.getId()))
+                .segmentId(SegmentId.of(Long.valueOf(network.getProviderSegID())))
+                .subnet(IpPrefix.valueOf(subnet.getCidr()))
+                .serviceIp(IpAddress.valueOf(subnet.getGateway()))
+                .type(serviceNet == null ? PRIVATE : serviceNet.type())
+                .providers(serviceNet == null ? ImmutableSet.of() : serviceNet.providers())
+                .build();
+    }
+
+    private static void validateNeutronNetwork(Network network, Subnet subnet) {
+        checkNotNull(network);
+        checkNotNull(subnet);
+        checkArgument(Objects.equals(network.getId(), subnet.getNetworkId()));
+        checkArgument(!Strings.isNullOrEmpty(network.getProviderSegID()), ERR_SEGMENT_ID_MISSING);
+        checkArgument(!Strings.isNullOrEmpty(subnet.getGateway()), ERR_GATEWAY_IP_MISSING);
+    }
+
+    /**
+     * Returns new vtn network builder instance.
+     *
+     * @return vtn network builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns new vtn network builder instance with copy of the given vtn network.
+     *
+     * @param vtnNet vtn network
+     * @return vtn network builder
+     */
+    public static Builder builder(VtnNetwork vtnNet) {
+        return new Builder()
+                .id(vtnNet.id())
+                .segmentId(vtnNet.segmentId())
+                .subnet(vtnNet.subnet())
+                .serviceIp(vtnNet.serviceIp())
+                .type(vtnNet.type())
+                .providers(vtnNet.providers());
+    }
+
+    /**
+     * Builder of the vtn network entities.
+     */
+    public static final class Builder {
+        private NetworkId id;
+        private SegmentId segmentId;
+        private IpPrefix subnet;
+        private IpAddress serviceIp;
+        private ServiceNetworkType type;
+        private Set<ProviderNetwork> providers = ImmutableSet.of();
+
+        private Builder() {
+        }
+
+        /**
+         * Builds an immutable vtn network.
+         *
+         * @return vtn network instance
+         */
+        public VtnNetwork build() {
+            checkNotNull(id, "VTN network id cannot be null");
+            checkNotNull(segmentId, "VTN network segment id cannot be null");
+            checkNotNull(subnet, "VTN network subnet cannot be null");
+            checkNotNull(serviceIp, "VTN network service IP cannot be null");
+            checkNotNull(type, "VTN network type cannot be null");
+            providers = providers == null ? ImmutableSet.of() : providers;
+
+            return new VtnNetwork(id, segmentId, subnet, serviceIp, type, providers);
+        }
+
+        /**
+         * Returns vtn network builder with the supplied network ID.
+         *
+         * @param id network id
+         * @return vtn network builder
+         */
+        public Builder id(NetworkId id) {
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * Returns vtn network builder with the supplied segment ID.
+         *
+         * @param segmentId segment id
+         * @return vtn network builder
+         */
+        public Builder segmentId(SegmentId segmentId) {
+            this.segmentId = segmentId;
+            return this;
+        }
+
+        /**
+         * Returns vtn network builder with the supplied subnet.
+         *
+         * @param subnet subnet
+         * @return vtn network builder
+         */
+        public Builder subnet(IpPrefix subnet) {
+            this.subnet = subnet;
+            return this;
+        }
+
+        /**
+         * Returns vtn network service IP address.
+         *
+         * @param serviceIp service ip address
+         * @return vtn network builder
+         */
+        public Builder serviceIp(IpAddress serviceIp) {
+            this.serviceIp = serviceIp;
+            return this;
+        }
+
+        /**
+         * Returns vtn network builder with the supplied service network type.
+         *
+         * @param type service network type
+         * @return vtn network builder
+         */
+        public Builder type(ServiceNetworkType type) {
+            this.type = type;
+            return this;
+        }
+
+        /**
+         * Returns vtn network builder with the supplied provider service networks.
+         *
+         * @param providers provider service networks
+         * @return vtn network builder
+         */
+        public Builder providers(Set<ProviderNetwork> providers) {
+            this.providers = providers;
+            return this;
+        }
+
+        /**
+         * Returns vtn network builder with the given additional provider network.
+         *
+         * @param providerId provider network id
+         * @param type       direct access type to the provider network
+         * @return vtn network builder
+         */
+        public Builder addProvider(NetworkId providerId, Dependency.Type type) {
+            checkNotNull(providerId, "Provider network ID cannot be null");
+            checkNotNull(type, "Provider network type cannot be null");
+
+            Set<ProviderNetwork> updated = Sets.newHashSet(this.providers);
+            updated.add(ProviderNetwork.of(providerId, type));
+            this.providers = ImmutableSet.copyOf(updated);
+            return this;
+        }
+
+        /**
+         * Returns vtn network builder without the given provider network.
+         *
+         * @param providerId provider network id
+         * @return vtn network builder
+         */
+        public Builder delProvider(NetworkId providerId) {
+            checkNotNull(providerId, "Provider network ID cannot be null");
+
+            ProviderNetwork provider = this.providers.stream()
+                    .filter(p -> Objects.equals(p.id(), providerId))
+                    .findAny().orElse(null);
+            if (provider != null) {
+                Set<ProviderNetwork> updated = Sets.newHashSet(this.providers);
+                updated.remove(provider);
+                this.providers = ImmutableSet.copyOf(updated);
+            }
+            return this;
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/VtnNetworkEvent.java b/src/main/java/org/opencord/cordvtn/api/VtnNetworkEvent.java
new file mode 100644
index 0000000..c1d6fef
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/VtnNetworkEvent.java
@@ -0,0 +1,112 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import org.joda.time.LocalDateTime;
+import org.onosproject.event.AbstractEvent;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Describes vtn network event.
+ */
+public class VtnNetworkEvent extends AbstractEvent<VtnNetworkEvent.Type, VtnNetwork> {
+
+    private final VtnPort vtnPort;
+
+    /**
+     * Type of vtn network event.
+     */
+    public enum Type {
+        /**
+         * Signifies that a new vtn network has been created.
+         */
+        VTN_NETWORK_CREATED,
+
+        /**
+         * Signifies that some vtn network attributes have changed.
+         */
+        VTN_NETWORK_UPDATED,
+
+        /**
+         * Signifies that a vtn network has been removed.
+         */
+        VTN_NETWORK_REMOVED,
+
+        /**
+         * Signifies that a new vtn port has been created.
+         */
+        VTN_PORT_CREATED,
+
+        /**
+         * Signifies that some vtn port attributes have changed.
+         */
+        VTN_PORT_UPDATED,
+
+        /**
+         * Signifies that a vtn port has been removed.
+         */
+        VTN_PORT_REMOVED
+    }
+
+    /**
+     * Creates an event of a given type and for the specified vtn network and
+     * the current time.
+     *
+     * @param type   vtn network event type
+     * @param vtnNet vtn network subject
+     */
+    public VtnNetworkEvent(Type type, VtnNetwork vtnNet) {
+        super(type, vtnNet);
+        this.vtnPort = null;
+    }
+
+    /**
+     * Creates an event of a given type and for the specified vtn network,
+     * port and the current time.
+     *
+     * @param type    vtn network event type
+     * @param vtnNet  vtn network subject
+     * @param vtnPort optional vtn port subject
+     */
+    public VtnNetworkEvent(Type type, VtnNetwork vtnNet, VtnPort vtnPort) {
+        super(type, vtnNet);
+        this.vtnPort = vtnPort;
+    }
+
+    /**
+     * Returns the vtn port subject.
+     * It returns valid value only with the vtn port events.
+     *
+     * @return vtn port or null if the event is not vtn port specific
+     */
+    public VtnPort vtnPort() {
+        return vtnPort;
+    }
+
+    @Override
+    public String toString() {
+        if (vtnPort == null) {
+            return super.toString();
+        }
+        return toStringHelper(this)
+                .add("time", new LocalDateTime(time()))
+                .add("type", type())
+                .add("vtnNet", subject())
+                .add("vtnPort", vtnPort)
+                .toString();
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/VtnNetworkListener.java b/src/main/java/org/opencord/cordvtn/api/VtnNetworkListener.java
new file mode 100644
index 0000000..be78ef0
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/VtnNetworkListener.java
@@ -0,0 +1,24 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import org.onosproject.event.EventListener;
+
+/**
+ * Listener for vtn network event.
+ */
+public interface VtnNetworkListener extends EventListener<VtnNetworkEvent> {
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/VtnPort.java b/src/main/java/org/opencord/cordvtn/api/VtnPort.java
new file mode 100644
index 0000000..a7d8385
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/VtnPort.java
@@ -0,0 +1,296 @@
+/*
+ * 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.opencord.cordvtn.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.openstack4j.model.network.Port;
+
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of the port containing all port information consumed by VTN service.
+ */
+public final class VtnPort extends ServicePort {
+
+    private static final String ERR_IP_MISSING = "VTN port IP adderess is missing";
+
+    private final NetworkId netId;
+    private final MacAddress mac;
+    private final IpAddress ip;
+
+    private VtnPort(PortId id,
+                    NetworkId netId,
+                    MacAddress mac,
+                    IpAddress ip,
+                    VlanId vlanId,
+                    Set<AddressPair> addressPairs) {
+        super(id, vlanId, addressPairs);
+        this.netId = netId;
+        this.mac = mac;
+        this.ip = ip;
+    }
+
+    /**
+     * Returns the network ID of this port.
+     *
+     * @return network id
+     */
+    public NetworkId netId() {
+        return netId;
+    }
+
+    /**
+     * Returns the MAC address of this port.
+     *
+     * @return mac address
+     */
+    public MacAddress mac() {
+        return mac;
+    }
+
+    /**
+     * Returns the IP address of this port.
+     *
+     * @return ip address
+     */
+    public IpAddress ip() {
+        return ip;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof VtnPort) {
+            VtnPort that = (VtnPort) obj;
+            if (Objects.equals(id, that.id) &&
+                    Objects.equals(netId, that.netId) &&
+                    Objects.equals(mac, that.mac) &&
+                    Objects.equals(ip, that.ip) &&
+                    Objects.equals(vlanId, that.vlanId) &&
+                    Objects.equals(addressPairs, that.addressPairs)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, netId, mac, ip, vlanId, addressPairs);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id)
+                .add("netId", netId)
+                .add("mac", mac)
+                .add("ip", ip)
+                .add("vlanId", vlanId)
+                .add("addressPairs", addressPairs)
+                .toString();
+    }
+
+    /**
+     * Returns the immutable VTN port with the supplied Neutron port with additional
+     * vtn port information.
+     *
+     * @param port        neutron port
+     * @param servicePort vtn port
+     * @return vtn port
+     */
+    public static VtnPort of(Port port, ServicePort servicePort) {
+        validateNeutronPort(port);
+        if (servicePort != null) {
+            checkArgument(Objects.equals(port.getId(), servicePort.id().id()));
+        }
+
+        return builder().id(PortId.of(port.getId()))
+                .netId(NetworkId.of(port.getNetworkId()))
+                .mac(MacAddress.valueOf(port.getMacAddress()))
+                .ip(IpAddress.valueOf(port.getFixedIps().iterator().next().getIpAddress()))
+                .vlanId(servicePort == null ? null : servicePort.vlanId().orElse(null))
+                .addressPairs(servicePort == null ? ImmutableSet.of() : servicePort.addressPairs())
+                .build();
+    }
+
+    private static void validateNeutronPort(Port port) {
+        checkNotNull(port);
+        checkArgument(port.getFixedIps().size() > 0, ERR_IP_MISSING);
+    }
+
+    /**
+     * Returns the immutable VTN port with the supplied VTN port with additional
+     * vtn port information.
+     *
+     * @param vtnPort     vtn port
+     * @param servicePort vtn port
+     * @return vtn port
+     */
+    public static VtnPort of(VtnPort vtnPort, ServicePort servicePort) {
+        return builder(vtnPort)
+                .vlanId(servicePort.vlanId().orElse(null))
+                .addressPairs(servicePort.addressPairs())
+                .build();
+    }
+
+    /**
+     * Returns new vtn port builder instance.
+     *
+     * @return vtn port builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns new vtn port builder instance with copy of the supplied vtn port.
+     *
+     * @param vtnPort vtn port
+     * @return vtn port builder
+     */
+    public static Builder builder(VtnPort vtnPort) {
+        return new Builder().id(vtnPort.id())
+                .netId(vtnPort.netId())
+                .mac(vtnPort.mac())
+                .ip(vtnPort.ip())
+                .vlanId(vtnPort.vlanId().orElse(null))
+                .addressPairs(vtnPort.addressPairs());
+    }
+
+    /**
+     * Builder of the vtn port entities.
+     */
+    public static final class Builder {
+        private PortId id;
+        private NetworkId netId;
+        private MacAddress mac;
+        private IpAddress ip;
+        private VlanId vlanId;
+        private Set<AddressPair> addressPairs = ImmutableSet.of();
+
+        private Builder() {
+        }
+
+        /**
+         * Builds an immutable vtn port.
+         *
+         * @return vtn port instance
+         */
+        public VtnPort build() {
+            checkNotNull(id, "VtnPort port id cannot be null");
+            checkNotNull(netId, "VtnPort network id cannot be null");
+            checkNotNull(mac, "VtnPort mac address cannot be null");
+            checkNotNull(ip, "VtnPort ip address cannot be null");
+            addressPairs = addressPairs == null ? ImmutableSet.of() : addressPairs;
+
+            return new VtnPort(id, netId, mac, ip, vlanId, addressPairs);
+        }
+
+        /**
+         * Returns vtn port builder with the supplied port id.
+         *
+         * @param id port id
+         * @return vtn port builder
+         */
+        public Builder id(PortId id) {
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * Returns vtn port builder with the supplied network id.
+         *
+         * @param netId network id
+         * @return vtn port builder
+         */
+        public Builder netId(NetworkId netId) {
+            this.netId = netId;
+            return this;
+        }
+
+        /**
+         * Returns vtn port builder with the supplied mac address.
+         *
+         * @param mac mac address
+         * @return vtn port builder
+         */
+        public Builder mac(MacAddress mac) {
+            this.mac = mac;
+            return this;
+        }
+
+        /**
+         * Returns vtn port builder with the supplied ip address.
+         *
+         * @param ip ip address
+         * @return vtn port builder
+         */
+        public Builder ip(IpAddress ip) {
+            this.ip = ip;
+            return this;
+        }
+
+        /**
+         * Returns vtn port builder with the supplied VLAN ID.
+         *
+         * @param vlanId vlan id
+         * @return vtn port builder
+         */
+        public Builder vlanId(VlanId vlanId) {
+            this.vlanId = vlanId;
+            return this;
+        }
+
+        /**
+         * Returns vtn port builder with the supplied address pairs.
+         *
+         * @param addressPairs set of address pairs
+         * @return vtn port builder
+         */
+        public Builder addressPairs(Set<AddressPair> addressPairs) {
+            this.addressPairs = addressPairs;
+            return this;
+        }
+
+        /**
+         * Returns vtn port builder with the given additional address pair.
+         *
+         * @param addressPair address pair to add
+         * @return vtn port builder
+         */
+        public Builder addAddressPair(AddressPair addressPair) {
+            checkNotNull(addressPair, "VtnPort address pair cannot be null");
+
+            Set<AddressPair> updated = Sets.newHashSet(this.addressPairs);
+            updated.add(addressPair);
+            this.addressPairs = ImmutableSet.copyOf(updated);
+            return this;
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java b/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
index 52f9a7e..37df2d0 100644
--- a/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
+++ b/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
@@ -17,16 +17,21 @@
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Sets;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
+import org.opencord.cordvtn.api.Dependency;
 import org.opencord.cordvtn.api.NetworkId;
+import org.opencord.cordvtn.api.ProviderNetwork;
 import org.opencord.cordvtn.api.ServiceNetwork;
-import org.opencord.cordvtn.api.ServiceNetwork.DirectAccessType;
+import org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType;
+
+import java.util.Set;
 
 import static java.lang.Boolean.FALSE;
 import static java.lang.Boolean.TRUE;
-import static org.opencord.cordvtn.api.ServiceNetwork.DirectAccessType.BIDIRECTIONAL;
-import static org.opencord.cordvtn.api.ServiceNetwork.DirectAccessType.UNIDIRECTIONAL;
+import static org.opencord.cordvtn.api.Dependency.Type.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.Dependency.Type.UNIDIRECTIONAL;
 import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.valueOf;
 
 /**
@@ -46,10 +51,10 @@
                 .put(TYPE, snet.type().name().toLowerCase());
 
         ArrayNode providers = context.mapper().createArrayNode();
-        snet.providers().entrySet().forEach(provider -> {
+        snet.providers().forEach(provider -> {
             ObjectNode providerJson = context.mapper().createObjectNode()
-                    .put(ID, provider.getKey().id())
-                    .put(BIDIRECT, provider.getValue() == BIDIRECTIONAL ? TRUE : FALSE);
+                    .put(ID, provider.id().id())
+                    .put(BIDIRECT, provider.type() == BIDIRECTIONAL ? TRUE : FALSE);
             providers.add(providerJson);
         });
 
@@ -63,18 +68,18 @@
             return null;
         }
 
-        ServiceNetwork.Builder snetBuilder = ServiceNetwork.builder()
-                .id(NetworkId.of(json.get(ID).asText()))
-                .type(valueOf(json.get(TYPE).asText().toUpperCase()));
-
+        NetworkId netId = NetworkId.of(json.get(ID).asText());
+        ServiceNetworkType netType = valueOf(json.get(TYPE).asText().toUpperCase());
+        Set<ProviderNetwork> providers = Sets.newHashSet();
         if (json.get(PROVIDER_NETWORKS) != null) {
             json.get(PROVIDER_NETWORKS).forEach(provider -> {
                 NetworkId providerId = NetworkId.of(provider.get(ID).asText());
-                DirectAccessType type = provider.get(BIDIRECT).asBoolean() ?
+                Dependency.Type type = provider.get(BIDIRECT).asBoolean() ?
                         BIDIRECTIONAL : UNIDIRECTIONAL;
-                snetBuilder.addProvider(providerId, type);
+                providers.add(ProviderNetwork.of(providerId, type));
             });
         }
-        return snetBuilder.build();
+
+        return new ServiceNetwork(netId, netType, providers);
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java b/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
index 9c4343f..91681d0 100644
--- a/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
+++ b/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
@@ -17,6 +17,7 @@
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Sets;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
@@ -26,6 +27,8 @@
 import org.opencord.cordvtn.api.PortId;
 import org.opencord.cordvtn.api.ServicePort;
 
+import java.util.Set;
+
 /**
  * Service port JSON codec.
  */
@@ -62,21 +65,24 @@
             return null;
         }
 
-        ServicePort.Builder sportBuilder = ServicePort.builder()
-                .id(PortId.of(json.get(ID).asText()));
-
+        PortId portId = PortId.of(json.get(ID).asText());
+        VlanId vlanId = null;
         if (json.get(VLAN_ID) != null) {
-            sportBuilder.vlanId(VlanId.vlanId(json.get(VLAN_ID).asText()));
+            try {
+                vlanId = VlanId.vlanId(json.get(VLAN_ID).asText());
+            } catch (Exception ignore) {
+            }
         }
 
+        Set<AddressPair> addressPairs = Sets.newHashSet();
         if (json.get(FLOATING_ADDRESS_PAIRS) != null) {
             json.get(FLOATING_ADDRESS_PAIRS).forEach(pair -> {
                 AddressPair addrPair = AddressPair.of(
                         IpAddress.valueOf(pair.get(IP_ADDRESS).asText()),
                         MacAddress.valueOf(pair.get(MAC_ADDRESS).asText()));
-                sportBuilder.addAddressPair(addrPair);
+                addressPairs.add(addrPair);
             });
         }
-        return sportBuilder.build();
+        return new ServicePort(portId, vlanId, addressPairs);
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/AbstractInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/AbstractInstanceHandler.java
deleted file mode 100644
index ff58107..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/AbstractInstanceHandler.java
+++ /dev/null
@@ -1,201 +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.opencord.cordvtn.impl;
-
-import org.onlab.osgi.DefaultServiceDirectory;
-import org.onlab.osgi.ServiceDirectory;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.Host;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
-import org.onosproject.xosclient.api.OpenStackAccess;
-import org.onosproject.xosclient.api.VtnPort;
-import org.onosproject.xosclient.api.VtnPortApi;
-import org.onosproject.xosclient.api.VtnPortId;
-import org.onosproject.xosclient.api.VtnService;
-import org.onosproject.xosclient.api.VtnServiceApi;
-import org.onosproject.xosclient.api.VtnServiceApi.ServiceType;
-import org.onosproject.xosclient.api.VtnServiceId;
-import org.onosproject.xosclient.api.XosAccess;
-import org.onosproject.xosclient.api.XosClientService;
-import org.opencord.cordvtn.api.Constants;
-import org.opencord.cordvtn.api.CordVtnConfig;
-import org.opencord.cordvtn.api.Instance;
-import org.opencord.cordvtn.api.InstanceHandler;
-import org.slf4j.Logger;
-
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
-
-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.opencord.cordvtn.api.Constants.ERROR_OPENSTACK_ACCESS;
-import static org.opencord.cordvtn.api.Constants.ERROR_XOS_ACCESS;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Provides default virtual network connectivity for service instances.
- */
-public abstract class AbstractInstanceHandler implements InstanceHandler {
-
-    protected final Logger log = getLogger(getClass());
-
-    protected CoreService coreService;
-    protected MastershipService mastershipService;
-    protected NetworkConfigRegistry configRegistry;
-    protected HostService hostService;
-    protected XosClientService xosClient;
-
-    protected ApplicationId appId;
-    protected Optional<ServiceType> serviceType = Optional.empty();
-    protected NetworkConfigListener configListener = new InternalConfigListener();
-    protected HostListener hostListener = new InternalHostListener();
-
-    private XosAccess xosAccess = null;
-    private OpenStackAccess osAccess = null;
-    private final ExecutorService eventExecutor = newSingleThreadExecutor(
-            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
-
-    protected void activate() {
-        ServiceDirectory services = new DefaultServiceDirectory();
-        coreService = services.get(CoreService.class);
-        configRegistry = services.get(NetworkConfigRegistry.class);
-        mastershipService = services.get(MastershipService.class);
-        hostService = services.get(HostService.class);
-        xosClient = services.get(XosClientService.class);
-
-        appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
-        hostService.addListener(hostListener);
-        configRegistry.addListener(configListener);
-
-        log.info("Started");
-    }
-
-    protected void deactivate() {
-        hostService.removeListener(hostListener);
-        configRegistry.removeListener(configListener);
-        eventExecutor.shutdown();
-
-        log.info("Stopped");
-    }
-
-    protected VtnService getVtnService(VtnServiceId serviceId) {
-        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
-        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
-
-        // TODO remove openstack access when XOS provides all information
-        VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
-        VtnService service = serviceApi.service(serviceId, osAccess);
-        if (service == null) {
-            log.warn("Failed to get VtnService for {}", serviceId);
-        }
-        return service;
-    }
-
-    protected VtnPort getVtnPort(Instance instance) {
-        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
-        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
-
-        VtnPortId vtnPortId = instance.portId();
-        VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
-        VtnPort vtnPort = portApi.vtnPort(vtnPortId, osAccess);
-        if (vtnPort == null) {
-            log.warn("Failed to get port information of {}", instance);
-            return null;
-        }
-        return vtnPort;
-    }
-
-    protected Set<Instance> getInstances(VtnServiceId serviceId) {
-        return StreamSupport.stream(hostService.getHosts().spliterator(), false)
-                .filter(host -> Objects.equals(
-                        serviceId.id(),
-                        host.annotations().value(Instance.SERVICE_ID)))
-                .map(Instance::of)
-                .collect(Collectors.toSet());
-    }
-
-    protected void readConfiguration() {
-        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
-        if (config == null) {
-            log.debug("No configuration found");
-            return;
-        }
-        osAccess = config.openstackAccess();
-        xosAccess = config.xosAccess();
-    }
-
-    private class InternalHostListener implements HostListener {
-
-        @Override
-        public void event(HostEvent event) {
-            Host host = event.subject();
-            if (!mastershipService.isLocalMaster(host.location().deviceId())) {
-                // do not allow to proceed without mastership
-                return;
-            }
-
-            Instance instance = Instance.of(host);
-            if (serviceType.isPresent() &&
-                    !serviceType.get().equals(instance.serviceType())) {
-                // not my service instance, do nothing
-                return;
-            }
-
-            switch (event.type()) {
-                case HOST_UPDATED:
-                case HOST_ADDED:
-                    eventExecutor.execute(() -> instanceDetected(instance));
-                    break;
-                case HOST_REMOVED:
-                    eventExecutor.execute(() -> instanceRemoved(instance));
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private class InternalConfigListener implements NetworkConfigListener {
-
-        @Override
-        public void event(NetworkConfigEvent event) {
-            if (!event.configClass().equals(CordVtnConfig.class)) {
-                return;
-            }
-
-            switch (event.type()) {
-                case CONFIG_ADDED:
-                case CONFIG_UPDATED:
-                    readConfiguration();
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
index d1dfdd2..15269dd 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
@@ -15,8 +15,8 @@
  */
 package org.opencord.cordvtn.impl;
 
+import com.google.common.collect.ImmutableSet;
 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;
@@ -30,8 +30,10 @@
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
 import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.xosclient.api.VtnService;
 import org.opencord.cordvtn.api.CordVtnConfig;
 import org.opencord.cordvtn.api.Instance;
 import org.onosproject.net.Host;
@@ -43,6 +45,8 @@
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketPriority;
 import org.onosproject.net.packet.PacketService;
+import org.opencord.cordvtn.api.VtnNetwork;
+import org.opencord.cordvtn.impl.handler.AbstractInstanceHandler;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
@@ -51,9 +55,7 @@
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.PRIVATE;
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.ACCESS_AGENT;
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.MANAGEMENT;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -69,14 +71,21 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnNodeManager nodeManager;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
     private final PacketProcessor packetProcessor = new InternalPacketProcessor();
     private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
 
     private MacAddress privateGatewayMac = MacAddress.NONE;
+    private NetworkConfigListener configListener = new InternalConfigListener();
 
     @Activate
     protected void activate() {
         super.activate();
+        configRegistry.addListener(configListener);
+        readConfiguration();
+
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
         requestPacket();
     }
@@ -84,6 +93,7 @@
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
+        configRegistry.removeListener(configListener);
         super.deactivate();
     }
 
@@ -158,7 +168,6 @@
             return;
         }
 
-        log.trace("Send ARP reply for {} with {}", targetIp, replyMac);
         Ethernet ethReply = ARP.buildArpReply(
                 targetIp,
                 replyMac,
@@ -213,7 +222,7 @@
                 .findFirst().orElse(null);
 
         if (host == null ||
-                !Instance.of(host).serviceType().equals(MANAGEMENT) ||
+                !Instance.of(host).netType().name().contains("MANAGEMENT") ||
                 hostMgmtPort == null) {
             context.block();
             return;
@@ -341,20 +350,17 @@
     public void instanceDetected(Instance instance) {
         // TODO remove this when XOS provides access agent information
         // and handle it the same way wit the other instances
-        if (instance.serviceType() == ACCESS_AGENT) {
+        if (instance.netType() == ACCESS_AGENT) {
             return;
         }
 
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            return;
-        }
-
-        if (service.networkType().equals(PRIVATE)) {
-            log.trace("Added IP:{} MAC:{}", service.serviceIp(), privateGatewayMac);
-            addGateway(service.serviceIp(), privateGatewayMac);
+        VtnNetwork vtnNet = getVtnNetwork(instance);
+        if (vtnNet.type() != PUBLIC && vtnNet.type() != MANAGEMENT_HOST &&
+                vtnNet.type() != MANAGEMENT_LOCAL) {
+            log.trace("Added IP:{} MAC:{}", vtnNet.serviceIp(), privateGatewayMac);
+            addGateway(vtnNet.serviceIp(), privateGatewayMac);
             // send gratuitous ARP for the existing VMs when ONOS is restarted
-            sendGratuitousArp(service.serviceIp(), Sets.newHashSet(instance));
+            sendGratuitousArp(vtnNet.serviceIp(), ImmutableSet.of(instance));
         }
     }
 
@@ -362,24 +368,19 @@
     public void instanceRemoved(Instance instance) {
         // TODO remove this when XOS provides access agent information
         // and handle it the same way wit the other instances
-        if (instance.serviceType() == ACCESS_AGENT) {
+        if (instance.netType() == ACCESS_AGENT) {
             return;
         }
 
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            return;
-        }
-
+        VtnNetwork vtnNet = getVtnNetwork(instance);
         // remove this network gateway from proxy ARP if no instance presents
-        if (service.networkType().equals(PRIVATE) &&
-                getInstances(service.id()).isEmpty()) {
-            removeGateway(service.serviceIp());
+        if (vtnNet.type() == PRIVATE &&
+                getInstances(vtnNet.id()).isEmpty()) {
+            removeGateway(vtnNet.serviceIp());
         }
     }
 
-    @Override
-    protected void readConfiguration() {
+    private void readConfiguration() {
         CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
         if (config == null) {
             log.debug("No configuration found");
@@ -395,7 +396,24 @@
                       entry.getKey(), entry.getValue());
         });
         // TODO send gratuitous arp in case the MAC is changed
+    }
 
-        super.readConfiguration();
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (!event.configClass().equals(CordVtnConfig.class)) {
+                return;
+            }
+
+            switch (event.type()) {
+                case CONFIG_ADDED:
+                case CONFIG_UPDATED:
+                    readConfiguration();
+                    break;
+                default:
+                    break;
+            }
+        }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
new file mode 100644
index 0000000..d645b8f
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
@@ -0,0 +1,458 @@
+/*
+ * 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.opencord.cordvtn.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostService;
+import org.opencord.cordvtn.api.CordVtnAdminService;
+import org.opencord.cordvtn.api.CordVtnService;
+import org.opencord.cordvtn.api.CordVtnStore;
+import org.opencord.cordvtn.api.CordVtnStoreDelegate;
+import org.opencord.cordvtn.api.Instance;
+import org.opencord.cordvtn.api.NetworkId;
+import org.opencord.cordvtn.api.PortId;
+import org.opencord.cordvtn.api.ServiceNetwork;
+import org.opencord.cordvtn.api.ServicePort;
+import org.opencord.cordvtn.api.SubnetId;
+import org.opencord.cordvtn.api.VtnNetwork;
+import org.opencord.cordvtn.api.VtnNetworkEvent;
+import org.opencord.cordvtn.api.VtnNetworkListener;
+import org.opencord.cordvtn.api.VtnPort;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Subnet;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.opencord.cordvtn.api.Instance.NETWORK_ID;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing VTN networks.
+ */
+@Component(immediate = true)
+@Service
+public class CordVtnManager extends ListenerRegistry<VtnNetworkEvent, VtnNetworkListener>
+        implements CordVtnAdminService, CordVtnService {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String MSG_SERVICE_NET  = "VTN network %s %s";
+    private static final String MSG_SERVICE_PORT = "VTN port %s %s";
+    private static final String MSG_NET  = "Network %s %s";
+    private static final String MSG_PORT = "Port %s %s";
+    private static final String MSG_SUBNET = "Subnet %s %s";
+
+    private static final String CREATED = "created";
+    private static final String UPDATED = "updated";
+    private static final String REMOVED = "removed";
+
+    private static final String ERR_NULL_SERVICE_PORT = "Service port cannot be null";
+    private static final String ERR_NULL_SERVICE_NET  = "Service network cannot be null";
+    private static final String ERR_NULL_PORT = "Port cannot be null";
+    private static final String ERR_NULL_NET  = "Network cannot be null";
+    private static final String ERR_NULL_SUBNET  = "Subnet cannot be null";
+    private static final String ERR_NULL_PORT_ID = "Port ID cannot be null";
+    private static final String ERR_NULL_NET_ID  = "Network ID cannot be null";
+    private static final String ERR_NULL_SUBNET_ID = "Subnet ID cannot be null";
+
+    private static final String ERR_SYNC = "VTN store is out of sync: ";
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_IN_USE_INSTANCE = "There are instances still in use on the network %s";
+    private static final String ERR_IN_USE_NETWORK = "There are subscribers still in use on the network %s";
+    private static final String ERR_IN_USE_PORT = "There are ports still in use on the network %s";
+    private static final String ERR_SUBNET_DUPLICATE = "Subnet already exists for network %s";
+
+    private static final String PORT = "port ";
+    private static final String NETWORK  = "network ";
+    private static final String SUBNET  = "subnet for ";
+    private static final String PROVIDER = "provider ";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnStore store;
+
+    private CordVtnStoreDelegate delegate = new InternalCordVtnStoreDelegate();
+
+    @Activate
+    protected void activate() {
+        store.setDelegate(delegate);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        store.unsetDelegate(delegate);
+        log.info("Stopped");
+    }
+
+    @Override
+    public void createVtnNetwork(ServiceNetwork serviceNet) {
+        checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
+        synchronized (this) {
+            Network network = store.getNetwork(serviceNet.id());
+            if (network == null) {
+                final String error = ERR_SYNC + NETWORK + serviceNet.id() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+
+            Subnet subnet = getSubnet(serviceNet.id());
+            if (subnet == null) {
+                final String error = ERR_SYNC + SUBNET + serviceNet.id() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+
+            // TODO check VTN network instead of network
+            serviceNet.providers().stream().forEach(provider -> {
+                if (store.getNetwork(provider.id()) == null) {
+                    final String error = ERR_SYNC + PROVIDER + provider.id() + ERR_NOT_FOUND;
+                    throw new IllegalStateException(error);
+                }
+            });
+
+            store.createVtnNetwork(VtnNetwork.of(network, subnet, serviceNet));
+            log.info(String.format(MSG_SERVICE_NET, CREATED, serviceNet.id()));
+        }
+    }
+
+    @Override
+    public void updateVtnNetwork(ServiceNetwork serviceNet) {
+        checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
+        synchronized (this) {
+            VtnNetwork existing = store.getVtnNetwork(serviceNet.id());
+            if (existing == null) {
+                final String error = ERR_SYNC + NETWORK + serviceNet.id() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+            // only providers update is allowed
+            VtnNetwork updated = VtnNetwork.builder(existing)
+                    .providers(serviceNet.providers())
+                    .build();
+            store.updateVtnNetwork(updated);
+            log.info(String.format(MSG_SERVICE_NET, UPDATED, serviceNet.id()));
+        }
+    }
+
+    @Override
+    public void removeVtnNetwork(NetworkId netId) {
+        checkNotNull(netId, ERR_NULL_NET_ID);
+        // TODO check if the network still exists?
+        store.removeVtnNetwork(netId);
+        log.info(String.format(MSG_SERVICE_NET, REMOVED, netId));
+    }
+
+    @Override
+    public void createVtnPort(ServicePort servicePort) {
+        checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
+        synchronized (this) {
+            Port port = store.getPort(servicePort.id());
+            if (port == null) {
+                final String error = ERR_SYNC + PORT + servicePort.id() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+            store.createVtnPort(VtnPort.of(port, servicePort));
+            log.info(String.format(MSG_SERVICE_PORT, CREATED, servicePort.id()));
+        }
+    }
+
+    @Override
+    public void updateVtnPort(ServicePort servicePort) {
+        checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
+        synchronized (this) {
+            VtnPort vtnPort = store.getVtnPort(servicePort.id());
+            if (vtnPort == null) {
+                final String error = ERR_SYNC + PORT + servicePort.id() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+            store.updateVtnPort(VtnPort.of(vtnPort, servicePort));
+            log.info(String.format(MSG_SERVICE_PORT, UPDATED, servicePort.id()));
+        }
+    }
+
+    @Override
+    public void removeVtnPort(PortId portId) {
+        checkNotNull(portId, ERR_NULL_PORT_ID);
+        store.removeVtnPort(portId);
+        log.info(String.format(MSG_SERVICE_PORT, REMOVED, portId));
+    }
+
+    @Override
+    public void createNetwork(Network network) {
+        checkNotNull(network, ERR_NULL_NET);
+        store.createNetwork(network);
+        log.info(String.format(MSG_NET, CREATED, network.getId()));
+    }
+
+    @Override
+    public void updateNetwork(Network network) {
+        checkNotNull(network, ERR_NULL_NET);
+        store.updateNetwork(network);
+        log.info(String.format(MSG_NET, UPDATED, network.getId()));
+    }
+
+    @Override
+    public void removeNetwork(NetworkId netId) {
+        checkNotNull(netId, ERR_NULL_NET_ID);
+        // FIXME Neutron removes network anyway even if there's an exception here
+        store.removeNetwork(netId);
+        log.info(String.format(MSG_NET, REMOVED, netId));
+    }
+
+    @Override
+    public void createPort(Port port) {
+        checkNotNull(port, ERR_NULL_PORT);
+        synchronized (this) {
+            if (store.getNetwork(NetworkId.of(port.getNetworkId())) == null) {
+                final String error = ERR_SYNC + port.getNetworkId() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+            store.createPort(port);
+            log.info(String.format(MSG_PORT, CREATED, port.getId()));
+        }
+    }
+
+    @Override
+    public void updatePort(Port port) {
+        checkNotNull(port, ERR_NULL_PORT);
+        synchronized (this) {
+            if (store.getNetwork(NetworkId.of(port.getNetworkId())) == null) {
+                final String error = ERR_SYNC + port.getNetworkId() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+            store.updatePort(port);
+            log.info(String.format(MSG_PORT, UPDATED, port.getId()));
+        }
+    }
+
+    @Override
+    public void removePort(PortId portId) {
+        checkNotNull(portId, ERR_NULL_PORT_ID);
+        synchronized (this) {
+            if (getInstance(portId) != null) {
+                final String error = String.format(ERR_IN_USE_PORT, portId);
+                throw new IllegalStateException(error);
+            }
+            removeVtnPort(portId);
+            store.removePort(portId);
+            log.info(String.format(MSG_PORT, REMOVED, portId));
+        }
+    }
+
+    @Override
+    public void createSubnet(Subnet subnet) {
+        checkNotNull(subnet, ERR_NULL_SUBNET);
+        synchronized (this) {
+            if (store.getNetwork(NetworkId.of(subnet.getNetworkId())) == null) {
+                final String error = ERR_SYNC + subnet.getNetworkId() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+
+            if (getSubnet(NetworkId.of(subnet.getNetworkId())) != null) {
+                // CORD does not allow multiple subnets for a network
+                final String error = String.format(ERR_SUBNET_DUPLICATE, subnet.getNetworkId());
+                throw new IllegalStateException(error);
+            }
+            store.createSubnet(subnet);
+            log.info(String.format(MSG_SUBNET, CREATED, subnet.getId()));
+        }
+    }
+
+    @Override
+    public void updateSubnet(Subnet subnet) {
+        checkNotNull(subnet, ERR_NULL_SUBNET);
+        synchronized (this) {
+            if (store.getNetwork(NetworkId.of(subnet.getNetworkId())) == null) {
+                final String error = ERR_SYNC + subnet.getNetworkId() + ERR_NOT_FOUND;
+                throw new IllegalStateException(error);
+            }
+            store.updateSubnet(subnet);
+            log.info(String.format(MSG_SUBNET, UPDATED, subnet.getId()));
+        }
+    }
+
+    @Override
+    public void removeSubnet(SubnetId subnetId) {
+        checkNotNull(subnetId, ERR_NULL_SUBNET_ID);
+        // FIXME Neutron removes network anyway even if there's an exception here
+        synchronized (this) {
+            removeVtnNetwork(NetworkId.of(store.getSubnet(subnetId).getNetworkId()));
+            store.removeSubnet(subnetId);
+            log.info(String.format(MSG_SUBNET, REMOVED, subnetId));
+        }
+    }
+
+    @Override
+    public VtnNetwork getVtnNetwork(NetworkId netId) {
+        checkNotNull(netId, ERR_NULL_NET_ID);
+        return store.getVtnNetwork(netId);
+    }
+
+    @Override
+    public VtnNetwork getVtnNetworkOrDefault(NetworkId netId) {
+        checkNotNull(netId, ERR_NULL_NET_ID);
+
+        // return default VTN network if the network and subnet exist
+        VtnNetwork vtnNet = store.getVtnNetwork(netId);
+        return vtnNet == null ? getDefaultVtnNetwork(netId) : vtnNet;
+    }
+
+    @Override
+    public Set<VtnNetwork> getVtnNetworks() {
+        return store.getVtnNetworks();
+    }
+
+    @Override
+    public VtnPort getVtnPort(PortId portId) {
+        checkNotNull(portId, ERR_NULL_PORT_ID);
+        return store.getVtnPort(portId);
+    }
+
+    @Override
+    public VtnPort getVtnPortOrDefault(PortId portId) {
+        checkNotNull(portId, ERR_NULL_PORT_ID);
+
+        // return default VTN port if the port exists
+        VtnPort vtnPort = store.getVtnPort(portId);
+        return vtnPort == null ? getDefaultPort(portId) : vtnPort;
+    }
+
+    @Override
+    public VtnPort getVtnPort(String portName) {
+        Optional<Port> port = store.getPorts()
+                .stream()
+                .filter(p -> p.getId().contains(portName.substring(3)))
+                .findFirst();
+        if (!port.isPresent()) {
+            return null;
+        }
+        return getVtnPortOrDefault(PortId.of(port.get().getId()));
+    }
+
+    @Override
+    public Set<VtnPort> getVtnPorts() {
+        return store.getVtnPorts();
+    }
+
+    @Override
+    public Network getNetwork(NetworkId netId) {
+        checkNotNull(netId, ERR_NULL_NET_ID);
+        return store.getNetwork(netId);
+    }
+
+    @Override
+    public Set<Network> getNetworks() {
+        return store.getNetworks();
+    }
+
+    @Override
+    public Port getPort(PortId portId) {
+        checkNotNull(portId, ERR_NULL_PORT_ID);
+        return store.getPort(portId);
+    }
+
+    @Override
+    public Set<Port> getPorts() {
+        return store.getPorts();
+    }
+
+    @Override
+    public Subnet getSubnet(SubnetId subnetId) {
+        checkNotNull(subnetId, ERR_NULL_SUBNET_ID);
+        return store.getSubnet(subnetId);
+    }
+
+    @Override
+    public Set<Subnet> getSubnets() {
+        return store.getSubnets();
+    }
+
+    @Override
+    public Instance getInstance(PortId portId) {
+        VtnPort vtnPort = getVtnPortOrDefault(portId);
+        if (vtnPort == null) {
+            final String error = "Failed to build VTN port for " + portId.id();
+            throw new IllegalStateException(error);
+        }
+
+        Host host = hostService.getHost(HostId.hostId(vtnPort.mac()));
+        if (host == null) {
+            return null;
+        }
+        return Instance.of(host);
+    }
+
+    @Override
+    public Set<Instance> getInstances(NetworkId netId) {
+        return Tools.stream(hostService.getHosts())
+                .filter(host -> Objects.equals(
+                        host.annotations().value(NETWORK_ID),
+                        netId.id()))
+                .map(Instance::of)
+                .collect(Collectors.toSet());
+    }
+
+    private VtnNetwork getDefaultVtnNetwork(NetworkId netId) {
+        Network network = getNetwork(netId);
+        Subnet subnet = getSubnet(netId);
+        if (network == null || subnet == null) {
+            return null;
+        }
+        return VtnNetwork.of(network, subnet, null);
+    }
+
+    private VtnPort getDefaultPort(PortId portId) {
+        Port port = getPort(portId);
+        if (port == null) {
+            return null;
+        }
+        return VtnPort.of(port, null);
+    }
+
+    private Subnet getSubnet(NetworkId netId) {
+        // TODO fix networking-onos to send Network UPDATE when subnet created
+        Optional<Subnet> subnet = getSubnets().stream()
+                .filter(s -> Objects.equals(s.getNetworkId(), netId.id()))
+                .findFirst();
+        return subnet.orElse(null);
+    }
+
+    private class InternalCordVtnStoreDelegate implements CordVtnStoreDelegate {
+
+        @Override
+        public void notify(VtnNetworkEvent event) {
+            if (event != null) {
+                log.trace("send service network event {}", event);
+                process(event);
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
index 82bb0a9..ff43f93 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
@@ -34,6 +34,8 @@
 import org.onosproject.net.behaviour.InterfaceConfig;
 import org.onosproject.net.behaviour.TunnelEndPoints;
 import org.onosproject.net.behaviour.TunnelKeys;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.basics.SubjectFactories;
 import org.opencord.cordvtn.api.ConnectionHandler;
 import org.opencord.cordvtn.api.CordVtnConfig;
 import org.opencord.cordvtn.api.CordVtnNode;
@@ -147,6 +149,16 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnPipeline pipeline;
 
+    private static final Class<CordVtnConfig> CONFIG_CLASS = CordVtnConfig.class;
+    private final ConfigFactory configFactory =
+            new ConfigFactory<ApplicationId, CordVtnConfig>(
+                    SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "cordvtn") {
+                @Override
+                public CordVtnConfig createConfig() {
+                    return new CordVtnConfig();
+                }
+            };
+
     private final ExecutorService eventExecutor =
             newSingleThreadExecutor(groupedThreads("onos/cordvtn-node", "event-handler", log));
 
@@ -213,8 +225,9 @@
 
     @Activate
     protected void activate() {
-        appId = coreService.getAppId(CORDVTN_APP_ID);
+        appId = coreService.registerApplication(CORDVTN_APP_ID);
 
+        configRegistry.registerConfigFactory(configFactory);
         localNodeId = clusterService.getLocalNode().id();
         leadershipService.runForLeadership(appId.name());
 
@@ -238,6 +251,7 @@
         nodeStore.removeListener(nodeStoreListener);
 
         leadershipService.withdraw(appId.name());
+        configRegistry.unregisterConfigFactory(configFactory);
         eventExecutor.shutdown();
 
         log.info("Stopped");
diff --git a/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java b/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
index c22982b..bb1692c 100644
--- a/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
@@ -15,9 +15,10 @@
  */
 package org.opencord.cordvtn.impl;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 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;
@@ -27,8 +28,20 @@
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
-import org.onosproject.xosclient.api.VtnServiceApi;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+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.opencord.cordvtn.api.CordVtnAdminService;
 import org.opencord.cordvtn.api.CordVtnNode;
+import org.opencord.cordvtn.api.Dependency;
+import org.opencord.cordvtn.api.Dependency.Type;
 import org.opencord.cordvtn.api.DependencyService;
 import org.opencord.cordvtn.api.Instance;
 import org.onosproject.core.DefaultGroupId;
@@ -50,8 +63,14 @@
 import org.onosproject.net.group.GroupDescription;
 import org.onosproject.net.group.GroupKey;
 import org.onosproject.net.group.GroupService;
-import org.onosproject.xosclient.api.VtnService;
-import org.onosproject.xosclient.api.VtnServiceId;
+import org.opencord.cordvtn.api.NetworkId;
+import org.opencord.cordvtn.api.ProviderNetwork;
+import org.opencord.cordvtn.api.SegmentId;
+import org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType;
+import org.opencord.cordvtn.api.VtnNetwork;
+import org.opencord.cordvtn.api.VtnNetworkEvent;
+import org.opencord.cordvtn.api.VtnNetworkListener;
+import org.opencord.cordvtn.impl.handler.AbstractInstanceHandler;
 import org.slf4j.Logger;
 
 import java.util.List;
@@ -60,6 +79,8 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+import static org.opencord.cordvtn.api.Dependency.Type.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.ACCESS_AGENT;
 import static org.opencord.cordvtn.impl.CordVtnPipeline.*;
 import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -73,116 +94,175 @@
 
     protected final Logger log = getLogger(getClass());
 
+    private static final String ERR_NET_FAIL = "Failed to get VTN network ";
+    private static final String MSG_CREATE = "Created dependency %s";
+    private static final String MSG_REMOVE = "Removed dependency %s";
+    private static final String ADDED = "Added ";
+    private static final String REMOVED = "Removed ";
+
+    private static final KryoNamespace SERIALIZER_DEPENDENCY = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(VtnNetwork.class)
+            .register(NetworkId.class)
+            .register(SegmentId.class)
+            .register(ServiceNetworkType.class)
+            .register(ProviderNetwork.class)
+            .register(Dependency.class)
+            .register(Type.class)
+            .build();
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected GroupService groupService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected LeadershipService leadershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnPipeline pipeline;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnNodeManager nodeManager;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnAdminService vtnService;
+
+    private final VtnNetworkListener vtnNetListener = new InternalVtnNetListener();
+    private final MapEventListener<NetworkId, Set<Dependency>> dependencyListener =
+            new DependencyMapListener();
+
+    private ConsistentMap<NetworkId, Set<Dependency>> dependencyStore;
+    private NodeId localNodeId;
+
     @Activate
     protected void activate() {
         super.activate();
+
+        dependencyStore = storageService.<NetworkId, Set<Dependency>>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_DEPENDENCY))
+                .withName("cordvtn-dependencymap")
+                .withApplicationId(appId)
+                .build();
+        dependencyStore.addListener(dependencyListener);
+
+        localNodeId = clusterService.getLocalNode().id();
+        leadershipService.runForLeadership(appId.name());
+        vtnService.addListener(vtnNetListener);
     }
 
     @Deactivate
     protected void deactivate() {
         super.deactivate();
+        dependencyStore.removeListener(dependencyListener);
+        vtnService.removeListener(vtnNetListener);
+        leadershipService.withdraw(appId.name());
     }
 
     @Override
-    public void createDependency(VtnServiceId tServiceId, VtnServiceId pServiceId,
-                                        boolean isBidirectional) {
-        VtnService tService = getVtnService(tServiceId);
-        VtnService pService = getVtnService(pServiceId);
-
-        if (tService == null || pService == null) {
-            log.error("Failed to create dependency between {} and {}",
-                      tServiceId, pServiceId);
+    public void createDependency(NetworkId subNetId, NetworkId proNetId, Type type) {
+        // FIXME this is not safe
+        VtnNetwork existing = vtnService.getVtnNetworkOrDefault(subNetId);
+        if (existing == null) {
+            log.warn("Failed to create dependency between {} and {}", subNetId, proNetId);
             return;
         }
-
-        log.info("Created dependency between {} and {}", tService.name(), pService.name());
-        serviceDependencyRules(tService, pService, isBidirectional, true);
+        vtnService.createVtnNetwork(existing);
+        VtnNetwork updated = VtnNetwork.builder(existing)
+                .addProvider(proNetId, type)
+                .build();
+        vtnService.updateVtnNetwork(updated);
     }
 
     @Override
-    public void removeDependency(VtnServiceId tServiceId, VtnServiceId pServiceId) {
-        VtnService tService = getVtnService(tServiceId);
-        VtnService pService = getVtnService(pServiceId);
-
-        if (tService == null || pService == null) {
-            log.error("Failed to remove dependency between {} and {}",
-                      tServiceId, pServiceId);
+    public void removeDependency(NetworkId subNetId, NetworkId proNetId) {
+        // FIXME this is not safe
+        VtnNetwork subNet = vtnService.getVtnNetwork(subNetId);
+        if (subNet == null) {
+            log.warn("No dependency exists between {} and {}", subNetId, proNetId);
             return;
         }
-
-        log.info("Removed dependency between {} and {}", tService.name(), pService.name());
-        serviceDependencyRules(tService, pService, true, false);
+        VtnNetwork updated = VtnNetwork.builder(subNet)
+                .delProvider(proNetId)
+                .build();
+        vtnService.updateVtnNetwork(updated);
     }
 
     @Override
     public void instanceDetected(Instance instance) {
         // TODO remove this when XOS provides access agent information
         // and handle it the same way wit the other instances
-        if (instance.serviceType() == VtnServiceApi.ServiceType.ACCESS_AGENT) {
+        if (instance.netType() == ACCESS_AGENT) {
             return;
         }
 
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            return;
+        VtnNetwork vtnNet = vtnService.getVtnNetworkOrDefault(instance.netId());
+        if (vtnNet == null) {
+            final String error = ERR_NET_FAIL + instance.netId();
+            throw new IllegalStateException(error);
         }
 
-        // TODO get bidirectional information from XOS once XOS supports
-        service.tenantServices().stream().forEach(
-                tServiceId -> createDependency(tServiceId, service.id(), true));
-        service.providerServices().stream().forEach(
-                pServiceId -> createDependency(service.id(), pServiceId, true));
-
-        updateProviderServiceInstances(service);
+        if (!vtnNet.providers().isEmpty()) {
+            updateSubscriberInstances(vtnNet, instance, true);
+        }
+        // TODO check if subscribers on this network
+        updateProviderInstances(vtnNet);
     }
 
     @Override
     public void instanceRemoved(Instance instance) {
         // TODO remove this when XOS provides access agent information
         // and handle it the same way wit the other instances
-        if (instance.serviceType() == VtnServiceApi.ServiceType.ACCESS_AGENT) {
+        if (instance.netType() == ACCESS_AGENT) {
             return;
         }
 
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            return;
+        VtnNetwork vtnNet = vtnService.getVtnNetworkOrDefault(instance.netId());
+        if (vtnNet == null) {
+            final String error = ERR_NET_FAIL + instance.netId();
+            throw new IllegalStateException(error);
         }
 
-        if (!service.providerServices().isEmpty()) {
-            removeInstanceFromTenantService(instance, service);
+        if (!vtnNet.providers().isEmpty()) {
+            updateSubscriberInstances(vtnNet, instance, false);
         }
-        if (!service.tenantServices().isEmpty()) {
-            updateProviderServiceInstances(service);
-        }
+        // TODO check if subscribers on this network
+        updateProviderInstances(vtnNet);
     }
 
-    private void updateProviderServiceInstances(VtnService service) {
-        GroupKey groupKey = getGroupKey(service.id());
+    private void dependencyCreated(Dependency dependency) {
+        populateDependencyRules(dependency.subscriber(), dependency.provider(),
+                                dependency.type(), true);
+        log.info(String.format(MSG_CREATE, dependency));
+    }
 
+    private void dependencyRemoved(Dependency dependency) {
+        populateDependencyRules(dependency.subscriber(), dependency.provider(),
+                                dependency.type(), false);
+        log.info(String.format(MSG_REMOVE, dependency));
+    }
+
+    private void updateProviderInstances(VtnNetwork provider) {
         Set<DeviceId> devices = nodeManager.completeNodes().stream()
                 .map(CordVtnNode::integrationBridgeId)
                 .collect(Collectors.toSet());
 
+        GroupKey groupKey = getGroupKey(provider.id());
         for (DeviceId deviceId : devices) {
             Group group = groupService.getGroup(deviceId, groupKey);
             if (group == null) {
-                log.trace("No group exists for service {} in {}", service.id(), deviceId);
                 continue;
             }
 
             List<GroupBucket> oldBuckets = group.buckets().buckets();
-            List<GroupBucket> newBuckets = getServiceGroupBuckets(
-                    deviceId, service.vni(), getInstances(service.id())).buckets();
+            List<GroupBucket> newBuckets = getProviderGroupBuckets(
+                    deviceId,
+                    provider.segmentId().id(),
+                    getInstances(provider.id())).buckets();
 
             if (oldBuckets.equals(newBuckets)) {
                 continue;
@@ -196,7 +276,8 @@
                         groupKey,
                         new GroupBuckets(bucketsToRemove),
                         groupKey, appId);
-                log.debug("Removes instances from {} : {}", service.name(), bucketsToRemove);
+                log.debug("Removed buckets from provider({}) group on {}: {}",
+                          provider.id(), deviceId, bucketsToRemove);
             }
 
             List<GroupBucket> bucketsToAdd = Lists.newArrayList(newBuckets);
@@ -207,57 +288,67 @@
                         groupKey,
                         new GroupBuckets(bucketsToAdd),
                         groupKey, appId);
-                log.debug("Adds instances to {} : {}", service.name(), bucketsToRemove);
+                log.debug("Added buckets to provider({}) group on {}: {}",
+                          provider.id(), deviceId, bucketsToAdd);
             }
         }
     }
 
-    private void removeInstanceFromTenantService(Instance instance, VtnService service) {
-        service.providerServices().stream().forEach(pServiceId -> {
-            Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
-            Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
+    private void updateSubscriberInstances(VtnNetwork subscriber, Instance instance,
+                                           boolean isDetected) {
+        DeviceId deviceId = instance.deviceId();
+        final String isAdded = isDetected ? ADDED : REMOVED;
+        subscriber.providers().stream().forEach(provider -> {
+            populateInPortRule(
+                    ImmutableMap.of(deviceId, ImmutableSet.of(instance.portNumber())),
+                    ImmutableMap.of(deviceId, getGroupId(provider.id(), deviceId)),
+                    isDetected);
 
-            inPorts.put(instance.deviceId(), Sets.newHashSet(instance.portNumber()));
-            outGroups.put(instance.deviceId(), getGroupId(pServiceId, instance.deviceId()));
-
-            inServiceRule(inPorts, outGroups, false);
+            log.info(isAdded + "subscriber instance({}) for provider({})",
+                     instance.host().id(), provider.id());
         });
     }
 
-    private void serviceDependencyRules(VtnService tService, VtnService pService,
-                                       boolean isBidirectional, boolean install) {
-        Map<DeviceId, GroupId> outGroups = Maps.newHashMap();
-        Map<DeviceId, Set<PortNumber>> inPorts = Maps.newHashMap();
+    private void populateDependencyRules(VtnNetwork subscriber,
+                                         VtnNetwork provider,
+                                         Type type, boolean install) {
+        Map<DeviceId, GroupId> providerGroups = Maps.newHashMap();
+        Map<DeviceId, Set<PortNumber>> subscriberPorts = Maps.newHashMap();
 
         nodeManager.completeNodes().stream().forEach(node -> {
             DeviceId deviceId = node.integrationBridgeId();
-            GroupId groupId = createServiceGroup(deviceId, pService);
-            outGroups.put(deviceId, groupId);
+            GroupId groupId = getProviderGroup(provider, deviceId);
+            providerGroups.put(deviceId, groupId);
 
-            Set<PortNumber> tServiceInstances = getInstances(tService.id())
+            Set<PortNumber> ports = getInstances(subscriber.id())
                     .stream()
                     .filter(instance -> instance.deviceId().equals(deviceId))
                     .map(Instance::portNumber)
                     .collect(Collectors.toSet());
-            inPorts.put(deviceId, tServiceInstances);
+            subscriberPorts.put(deviceId, ports);
         });
 
-        Ip4Prefix srcRange = tService.subnet().getIp4Prefix();
-        Ip4Prefix dstRange = pService.subnet().getIp4Prefix();
+        Ip4Prefix subscriberIp = subscriber.subnet().getIp4Prefix();
+        Ip4Prefix providerIp = provider.subnet().getIp4Prefix();
 
-        indirectAccessRule(srcRange, pService.serviceIp().getIp4Address(), outGroups, install);
-        directAccessRule(srcRange, dstRange, install);
-        if (isBidirectional) {
-            directAccessRule(dstRange, srcRange, install);
+        populateInPortRule(subscriberPorts, providerGroups, install);
+        populateIndirectAccessRule(
+                subscriberIp,
+                provider.serviceIp().getIp4Address(),
+                providerGroups,
+                install);
+        populateDirectAccessRule(subscriberIp, providerIp, install);
+        if (type == BIDIRECTIONAL) {
+            populateDirectAccessRule(providerIp, subscriberIp, install);
         }
-        inServiceRule(inPorts, outGroups, install);
     }
 
-    private void indirectAccessRule(Ip4Prefix srcRange, Ip4Address serviceIp,
-                                    Map<DeviceId, GroupId> outGroups, boolean install) {
+    private void populateIndirectAccessRule(Ip4Prefix srcIp, Ip4Address serviceIp,
+                                            Map<DeviceId, GroupId> outGroups,
+                                            boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(srcRange)
+                .matchIPSrc(srcIp)
                 .matchIPDst(serviceIp.toIpPrefix())
                 .build();
 
@@ -280,11 +371,11 @@
         }
     }
 
-    private void directAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
+    private void populateDirectAccessRule(Ip4Prefix srcIp, Ip4Prefix dstIp, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(srcRange)
-                .matchIPDst(dstRange)
+                .matchIPSrc(srcIp)
+                .matchIPDst(dstIp)
                 .build();
 
         TrafficTreatment treatment = DefaultTrafficTreatment.builder()
@@ -307,13 +398,14 @@
         });
     }
 
-    private void inServiceRule(Map<DeviceId, Set<PortNumber>> inPorts,
-                               Map<DeviceId, GroupId> outGroups, boolean install) {
-        for (Map.Entry<DeviceId, Set<PortNumber>> entry : inPorts.entrySet()) {
+    private void populateInPortRule(Map<DeviceId, Set<PortNumber>> subscriberPorts,
+                                    Map<DeviceId, GroupId> providerGroups,
+                                    boolean install) {
+        for (Map.Entry<DeviceId, Set<PortNumber>> entry : subscriberPorts.entrySet()) {
             Set<PortNumber> ports = entry.getValue();
             DeviceId deviceId = entry.getKey();
 
-            GroupId groupId = outGroups.get(deviceId);
+            GroupId groupId = providerGroups.get(deviceId);
             if (groupId == null) {
                 continue;
             }
@@ -342,26 +434,25 @@
         }
     }
 
-    private GroupId getGroupId(VtnServiceId serviceId, DeviceId deviceId) {
-        return new DefaultGroupId(Objects.hash(serviceId, deviceId));
+    private GroupId getGroupId(NetworkId netId, DeviceId deviceId) {
+        return new DefaultGroupId(Objects.hash(netId, deviceId));
     }
 
-    private GroupKey getGroupKey(VtnServiceId serviceId) {
-        return new DefaultGroupKey(serviceId.id().getBytes());
+    private GroupKey getGroupKey(NetworkId netId) {
+        return new DefaultGroupKey(netId.id().getBytes());
     }
 
-    private GroupId createServiceGroup(DeviceId deviceId, VtnService service) {
-        GroupKey groupKey = getGroupKey(service.id());
+    private GroupId getProviderGroup(VtnNetwork provider, DeviceId deviceId) {
+        GroupKey groupKey = getGroupKey(provider.id());
         Group group = groupService.getGroup(deviceId, groupKey);
-        GroupId groupId = getGroupId(service.id(), deviceId);
+        GroupId groupId = getGroupId(provider.id(), deviceId);
 
         if (group != null) {
-            log.debug("Group {} is already exist in {}", service.id(), deviceId);
             return groupId;
         }
 
-        GroupBuckets buckets = getServiceGroupBuckets(
-                deviceId, service.vni(), getInstances(service.id()));
+        GroupBuckets buckets = getProviderGroupBuckets(
+                deviceId, provider.segmentId().id(), getInstances(provider.id()));
         GroupDescription groupDescription = new DefaultGroupDescription(
                 deviceId,
                 GroupDescription.Type.SELECT,
@@ -374,26 +465,135 @@
         return groupId;
     }
 
-    private GroupBuckets getServiceGroupBuckets(DeviceId deviceId, long tunnelId,
-                                                Set<Instance> instances) {
+    private void removeProviderGroup(NetworkId netId) {
+        GroupKey groupKey = getGroupKey(netId);
+        nodeManager.completeNodes().stream()
+                .forEach(node -> {
+                    DeviceId deviceId = node.integrationBridgeId();
+                    Group group = groupService.getGroup(deviceId, groupKey);
+                    if (group != null) {
+                        groupService.removeGroup(deviceId, groupKey, appId);
+                    }
+        });
+        log.debug("Removed group for network {}", netId);
+    }
+
+    private GroupBuckets getProviderGroupBuckets(DeviceId deviceId,
+                                                 long tunnelId,
+                                                 Set<Instance> instances) {
         List<GroupBucket> buckets = Lists.newArrayList();
         instances.stream().forEach(instance -> {
             Ip4Address tunnelIp = nodeManager.dataIp(instance.deviceId()).getIp4Address();
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
 
             if (deviceId.equals(instance.deviceId())) {
-                tBuilder.setEthDst(instance.mac())
-                        .setOutput(instance.portNumber());
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setEthDst(instance.mac())
+                        .setOutput(instance.portNumber())
+                        .build();
+                buckets.add(createSelectGroupBucket(treatment));
             } else {
                 ExtensionTreatment tunnelDst =
                         pipeline.tunnelDstTreatment(deviceId, tunnelIp);
-                tBuilder.setEthDst(instance.mac())
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setEthDst(instance.mac())
                         .extension(tunnelDst, deviceId)
                         .setTunnelId(tunnelId)
-                        .setOutput(nodeManager.tunnelPort(instance.deviceId()));
+                        .setOutput(nodeManager.tunnelPort(instance.deviceId()))
+                        .build();
+                buckets.add(createSelectGroupBucket(treatment));
             }
-            buckets.add(createSelectGroupBucket(tBuilder.build()));
         });
         return new GroupBuckets(buckets);
     }
+
+    private class InternalVtnNetListener implements VtnNetworkListener {
+
+        @Override
+        public void event(VtnNetworkEvent event) {
+            NodeId leader = leadershipService.getLeader(appId.name());
+            if (!Objects.equals(localNodeId, leader)) {
+                // do not allow to proceed without leadership
+                return;
+            }
+
+            switch (event.type()) {
+                case VTN_NETWORK_CREATED:
+                case VTN_NETWORK_UPDATED:
+                    log.debug("Processing dependency for {}", event.subject());
+                    eventExecutor.execute(() -> updateDependency(event.subject()));
+                    break;
+                case VTN_NETWORK_REMOVED:
+                    log.debug("Removing dependency for {}", event.subject());
+                    NetworkId netId = event.subject().id();
+                    eventExecutor.execute(() -> dependencyStore.remove(netId));
+                    break;
+                case VTN_PORT_CREATED:
+                case VTN_PORT_UPDATED:
+                case VTN_PORT_REMOVED:
+                default:
+                    // do nothing for the other events
+                    break;
+            }
+        }
+
+        private void updateDependency(VtnNetwork subscriber) {
+            Set<Dependency> dependencies = subscriber.providers().stream()
+                    .map(provider -> Dependency.builder()
+                            .subscriber(subscriber)
+                            .provider(vtnService.getVtnNetworkOrDefault(provider.id()))
+                            .type(provider.type())
+                            .build())
+                    .collect(Collectors.toSet());
+            dependencyStore.put(subscriber.id(), dependencies);
+        }
+    }
+
+    private class DependencyMapListener implements MapEventListener<NetworkId, Set<Dependency>> {
+
+        @Override
+        public void event(MapEvent<NetworkId, Set<Dependency>> event) {
+            NodeId leader = leadershipService.getLeader(appId.name());
+            if (!Objects.equals(localNodeId, leader)) {
+                // do not allow to proceed without leadership
+                return;
+            }
+
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("Subscriber {} updated", event.key());
+                    eventExecutor.execute(() -> dependencyUpdated(
+                            event.oldValue().value(),
+                            event.newValue().value()
+                    ));
+                    break;
+                case INSERT:
+                    log.debug("Subscriber {} inserted", event.key());
+                    eventExecutor.execute(() -> dependencyUpdated(
+                            ImmutableSet.of(),
+                            event.newValue().value()
+                    ));
+                    break;
+                case REMOVE:
+                    log.debug("Subscriber {} removed", event.key());
+                    eventExecutor.execute(() -> dependencyUpdated(
+                            event.oldValue().value(),
+                            ImmutableSet.of()
+                    ));
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+
+        private void dependencyUpdated(Set<Dependency> oldDeps, Set<Dependency> newDeps) {
+            oldDeps.stream().filter(oldDep -> !newDeps.contains(oldDep))
+                    .forEach(DependencyManager.this::dependencyRemoved);
+
+            newDeps.stream().filter(newDep -> !oldDeps.contains(newDep))
+                    .forEach(DependencyManager.this::dependencyCreated);
+
+            // TODO remove any group if no subscriber exists
+        }
+    }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/DistributedCordVtnStore.java b/src/main/java/org/opencord/cordvtn/impl/DistributedCordVtnStore.java
index 11b1e83..3520ba6 100644
--- a/src/main/java/org/opencord/cordvtn/impl/DistributedCordVtnStore.java
+++ b/src/main/java/org/opencord/cordvtn/impl/DistributedCordVtnStore.java
@@ -15,96 +15,441 @@
  */
 package org.opencord.cordvtn.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.onosproject.store.AbstractStore;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Versioned;
 import org.opencord.cordvtn.api.CordVtnStore;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.opencord.cordvtn.api.AddressPair;
+import org.opencord.cordvtn.api.CordVtnStoreDelegate;
+import org.opencord.cordvtn.api.Dependency;
 import org.opencord.cordvtn.api.NetworkId;
 import org.opencord.cordvtn.api.PortId;
-import org.opencord.cordvtn.api.ServiceNetwork;
-import org.opencord.cordvtn.api.ServicePort;
+import org.opencord.cordvtn.api.ProviderNetwork;
+import org.opencord.cordvtn.api.SegmentId;
+import org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType;
+import org.opencord.cordvtn.api.SubnetId;
+import org.opencord.cordvtn.api.VtnNetwork;
+import org.opencord.cordvtn.api.VtnNetworkEvent;
+import org.opencord.cordvtn.api.VtnPort;
+import org.openstack4j.model.network.IPVersionType;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.model.network.NetworkType;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.State;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronAllowedAddressPair;
+import org.openstack4j.openstack.networking.domain.NeutronExtraDhcpOptCreate;
+import org.openstack4j.openstack.networking.domain.NeutronHostRoute;
+import org.openstack4j.openstack.networking.domain.NeutronIP;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
+import org.openstack4j.openstack.networking.domain.NeutronPool;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
+import org.openstack4j.openstack.networking.domain.NeutronSubnet;
 import org.slf4j.Logger;
 
 import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.opencord.cordvtn.api.Constants.CORDVTN_APP_ID;
+import static org.opencord.cordvtn.api.VtnNetworkEvent.Type.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Implementation of the cordvtn service.
+ * Manages the inventory of VTN networks using a {@code ConsistentMap}.
  */
 @Component(immediate = true)
 @Service
-public class DistributedCordVtnStore implements CordVtnStore {
+public class DistributedCordVtnStore extends AbstractStore<VtnNetworkEvent, CordVtnStoreDelegate>
+        implements CordVtnStore {
+
     protected final Logger log = getLogger(getClass());
 
-    private static final String MSG_SERVICE_NET  = "Service network %s %s";
-    private static final String MSG_SERVICE_PORT = "Service port %s %s";
-    private static final String CREATED = "created";
-    private static final String UPDATED = "updated";
-    private static final String REMOVED = "removed";
+    private static final String ERR_SYNC = "VTN store is out of sync: ";
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_DUPLICATE = " already exists with different properties";
+
+    private static final KryoNamespace SERIALIZER_SERVICE = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(VtnNetwork.class)
+            .register(NetworkId.class)
+            .register(SegmentId.class)
+            .register(ServiceNetworkType.class)
+            .register(ProviderNetwork.class)
+            .register(Dependency.Type.class)
+            .register(VtnPort.class)
+            .register(PortId.class)
+            .register(AddressPair.class)
+            .build();
+
+    // Use Neutron data model until we need our own abstraction of virtual networks
+    private static final KryoNamespace SERIALIZER_NEUTRON = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(Network.class)
+            .register(NetworkId.class)
+            .register(NeutronNetwork.class)
+            .register(State.class)
+            .register(NetworkType.class)
+            .register(Port.class)
+            .register(PortId.class)
+            .register(NeutronPort.class)
+            .register(NeutronIP.class)
+            .register(NeutronAllowedAddressPair.class)
+            .register(NeutronExtraDhcpOptCreate.class)
+            .register(Subnet.class)
+            .register(SubnetId.class)
+            .register(NeutronSubnet.class)
+            .register(NeutronPool.class)
+            .register(NeutronHostRoute.class)
+            .register(IPVersionType.class)
+            .build();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+    private final MapEventListener<PortId, VtnPort> vtnPortListener = new VtnPortMapListener();
+    private final MapEventListener<NetworkId, VtnNetwork> vtnNetworkListener = new VtnNetworkMapListener();
+    private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    private ConsistentMap<NetworkId, VtnNetwork> vtnNetworkStore;
+    private ConsistentMap<PortId, VtnPort> vtnPortStore;
+    private ConsistentMap<NetworkId, Network> networkStore;
+    private ConsistentMap<SubnetId, Subnet> subnetStore;
+    private ConsistentMap<PortId, Port> portStore;
 
     @Activate
     protected void activate() {
+        ApplicationId appId = coreService.registerApplication(CORDVTN_APP_ID);
+
+        vtnNetworkStore = storageService.<NetworkId, VtnNetwork>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_SERVICE))
+                .withName("cordvtn-vtnnetstore")
+                .withApplicationId(appId)
+                .build();
+        vtnNetworkStore.addListener(vtnNetworkListener);
+
+        vtnPortStore = storageService.<PortId, VtnPort>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_SERVICE))
+                .withName("cordvtn-vtnportstore")
+                .withApplicationId(appId)
+                .build();
+        vtnPortStore.addListener(vtnPortListener);
+
+        networkStore = storageService.<NetworkId, Network>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON))
+                .withName("cordvtn-networkstore")
+                .withApplicationId(appId)
+                .build();
+
+        portStore = storageService.<PortId, Port>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON))
+                .withName("cordvtn-portstore")
+                .withApplicationId(appId)
+                .build();
+
+        subnetStore = storageService.<SubnetId, Subnet>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_NEUTRON))
+                .withName("cordvtn-subnetstore")
+                .withApplicationId(appId)
+                .build();
+
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
+        vtnNetworkStore.removeListener(vtnNetworkListener);
+        vtnPortStore.removeListener(vtnPortListener);
+
         log.info("Stopped");
     }
 
     @Override
-    public void createServiceNetwork(ServiceNetwork serviceNet) {
-        // TODO implement
-        log.info(String.format(MSG_SERVICE_NET, CREATED, serviceNet));
+    public void createVtnNetwork(VtnNetwork vtnNet) {
+        vtnNetworkStore.compute(vtnNet.id(), (id, existing) -> {
+            final String error = ERR_SYNC + vtnNet.id().id() + ERR_DUPLICATE;
+            checkArgument(existing == null || existing.equals(vtnNet), error);
+            return vtnNet;
+        });
     }
 
     @Override
-    public void updateServiceNetwork(ServiceNetwork serviceNet) {
-        // TODO implement
-        log.info(String.format(MSG_SERVICE_NET, UPDATED, serviceNet));
+    public void updateVtnNetwork(VtnNetwork vtnNet) {
+        vtnNetworkStore.compute(vtnNet.id(), (id, existing) -> {
+            final String error = ERR_SYNC + vtnNet.id().id() + ERR_NOT_FOUND;
+            checkArgument(existing != null, ERR_SYNC + error);
+            return vtnNet;
+        });
     }
 
     @Override
-    public ServiceNetwork getServiceNetwork(NetworkId netId) {
-        // TODO implement
-        return null;
+    public void removeVtnNetwork(NetworkId netId) {
+        synchronized (this) {
+            // remove any dependencies that this network involved in
+            vtnNetworkStore.computeIfPresent(netId, (id, existing) ->
+                    VtnNetwork.builder(existing)
+                            .providers(ImmutableSet.of()).build()
+            );
+            getSubscribers(netId).stream().forEach(subs ->
+                vtnNetworkStore.computeIfPresent(subs.id(), (id, existing) ->
+                    VtnNetwork.builder(existing)
+                            .delProvider(netId).build())
+            );
+            vtnNetworkStore.remove(netId);
+        }
     }
 
     @Override
-    public Set<ServiceNetwork> getServiceNetworks() {
-        // TODO implement
-        return null;
+    public VtnNetwork getVtnNetwork(NetworkId netId) {
+        Versioned<VtnNetwork> versioned = vtnNetworkStore.get(netId);
+        return versioned == null ? null : versioned.value();
     }
 
     @Override
-    public void removeServiceNetwork(NetworkId netId) {
-        // TODO implement
-        log.info(String.format(MSG_SERVICE_NET, REMOVED, netId));
+    public Set<VtnNetwork> getVtnNetworks() {
+        return vtnNetworkStore.values().stream().map(Versioned::value)
+                .collect(Collectors.toSet());
     }
 
     @Override
-    public void createServicePort(ServicePort servicePort) {
-        // TODO implement
-        log.info(String.format(MSG_SERVICE_PORT, CREATED, servicePort));
+    public void createVtnPort(VtnPort vtnPort) {
+        vtnPortStore.compute(vtnPort.id(), (id, existing) -> {
+            final String error = ERR_SYNC + vtnPort.id().id() + ERR_DUPLICATE;
+            checkArgument(existing == null || existing.equals(vtnPort), error);
+            return vtnPort;
+        });
     }
 
     @Override
-    public ServicePort getServicePort(PortId portId) {
-        // TODO implement
-        return null;
+    public void updateVtnPort(VtnPort vtnPort) {
+        vtnPortStore.compute(vtnPort.id(), (id, existing) -> {
+            final String error = ERR_SYNC + vtnPort.id().id() + ERR_NOT_FOUND;
+            checkArgument(existing != null, ERR_SYNC + error);
+            return vtnPort;
+        });
     }
 
     @Override
-    public Set<ServicePort> getServicePorts() {
-        // TODO implement
-        return null;
+    public void removeVtnPort(PortId portId) {
+        vtnPortStore.remove(portId);
     }
 
     @Override
-    public void removeServicePort(PortId portId) {
-        // TODO implement
-        log.info(String.format(MSG_SERVICE_PORT, REMOVED, portId));
+    public VtnPort getVtnPort(PortId portId) {
+        Versioned<VtnPort> versioned = vtnPortStore.get(portId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<VtnPort> getVtnPorts() {
+        return vtnPortStore.values().stream().map(Versioned::value)
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public void createNetwork(Network net) {
+        networkStore.compute(NetworkId.of(net.getId()), (id, existing) -> {
+            final String error = ERR_SYNC + net.getId() + ERR_DUPLICATE;
+            checkArgument(existing == null || net.equals(existing), error);
+            return net;
+        });
+    }
+
+    @Override
+    public void updateNetwork(Network net) {
+        networkStore.compute(NetworkId.of(net.getId()), (id, existing) -> {
+            final String error = ERR_SYNC + net.getId() + ERR_NOT_FOUND;
+            checkArgument(existing != null, ERR_SYNC + error);
+            return net;
+        });
+    }
+
+    @Override
+    public void removeNetwork(NetworkId netId) {
+        networkStore.remove(netId);
+    }
+
+    @Override
+    public Network getNetwork(NetworkId netId) {
+        Versioned<Network> versioned = networkStore.get(netId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<Network> getNetworks() {
+        return networkStore.values().stream().map(Versioned::value)
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public void createPort(Port port) {
+        portStore.compute(PortId.of(port.getId()), (id, existing) -> {
+            final String error = ERR_SYNC + port.getId() + ERR_DUPLICATE;
+            checkArgument(existing == null || port.equals(existing), error);
+            return port;
+        });
+    }
+
+    @Override
+    public void updatePort(Port port) {
+        portStore.compute(PortId.of(port.getId()), (id, existing) -> {
+            final String error = ERR_SYNC + port.getId() + ERR_NOT_FOUND;
+            checkArgument(existing != null, ERR_SYNC + error);
+            return port;
+        });
+    }
+
+    @Override
+    public void removePort(PortId portId) {
+        portStore.remove(portId);
+    }
+
+    @Override
+    public Port getPort(PortId portId) {
+        Versioned<Port> versioned = portStore.get(portId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<Port> getPorts() {
+        return portStore.values().stream().map(Versioned::value)
+                .collect(Collectors.toSet());
+    }
+
+    @Override
+    public void createSubnet(Subnet subnet) {
+        subnetStore.compute(SubnetId.of(subnet.getId()), (id, existing) -> {
+            final String error = ERR_SYNC + subnet.getId() + ERR_DUPLICATE;
+            checkArgument(existing == null || subnet.equals(existing), error);
+            return subnet;
+        });
+    }
+
+    @Override
+    public void updateSubnet(Subnet subnet) {
+        subnetStore.compute(SubnetId.of(subnet.getId()), (id, existing) -> {
+            final String error = ERR_SYNC + subnet.getId() + ERR_NOT_FOUND;
+            checkArgument(existing != null, ERR_SYNC + error);
+            return subnet;
+        });
+    }
+
+    @Override
+    public void removeSubnet(SubnetId subnetId) {
+        subnetStore.remove(subnetId);
+    }
+
+    @Override
+    public Subnet getSubnet(SubnetId subnetId) {
+        Versioned<Subnet> versioned = subnetStore.get(subnetId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<Subnet> getSubnets() {
+        return subnetStore.values().stream().map(Versioned::value)
+                .collect(Collectors.toSet());
+    }
+
+    private Set<VtnNetwork> getSubscribers(NetworkId netId) {
+        return getVtnNetworks().stream().filter(net -> net.isProvider(netId))
+                .collect(Collectors.toSet());
+    }
+
+    private class VtnNetworkMapListener implements MapEventListener<NetworkId, VtnNetwork> {
+
+        @Override
+        public void event(MapEvent<NetworkId, VtnNetwork> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("VTN network updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new VtnNetworkEvent(
+                                VTN_NETWORK_UPDATED,
+                                event.newValue().value()));
+                    });
+                    break;
+                case INSERT:
+                    log.debug("VTN network created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new VtnNetworkEvent(
+                                VTN_NETWORK_CREATED,
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("VTN network removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new VtnNetworkEvent(
+                                VTN_NETWORK_REMOVED,
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+    }
+
+    private class VtnPortMapListener implements MapEventListener<PortId, VtnPort> {
+
+        @Override
+        public void event(MapEvent<PortId, VtnPort> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("VTN port updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new VtnNetworkEvent(
+                                VTN_PORT_UPDATED,
+                                getVtnNetwork(event.newValue().value().netId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case INSERT:
+                    log.debug("VTN port created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new VtnNetworkEvent(
+                                VTN_PORT_CREATED,
+                                getVtnNetwork(event.newValue().value().netId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("VTN port removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new VtnNetworkEvent(
+                                VTN_PORT_REMOVED,
+                                getVtnNetwork(event.oldValue().value().netId()),
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
index b02fb52..ba35d9b 100644
--- a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
@@ -25,9 +25,12 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
 import org.opencord.cordconfig.CordConfigService;
 import org.opencord.cordconfig.access.AccessAgentData;
-import org.opencord.cordvtn.api.CordVtnConfig;
+import org.opencord.cordvtn.api.CordVtnService;
 import org.opencord.cordvtn.api.Instance;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
@@ -39,11 +42,6 @@
 import org.onosproject.net.HostId;
 import org.onosproject.net.HostLocation;
 import org.onosproject.net.Port;
-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.DeviceService;
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
@@ -53,29 +51,27 @@
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
-import org.onosproject.xosclient.api.OpenStackAccess;
-import org.onosproject.xosclient.api.VtnPort;
-import org.onosproject.xosclient.api.VtnPortApi;
-import org.onosproject.xosclient.api.VtnService;
-import org.onosproject.xosclient.api.VtnServiceApi;
-import org.onosproject.xosclient.api.VtnServiceId;
-import org.onosproject.xosclient.api.XosAccess;
-import org.onosproject.xosclient.api.XosClientService;
 import org.opencord.cordvtn.api.InstanceService;
+import org.opencord.cordvtn.api.PortId;
+import org.opencord.cordvtn.api.VtnNetwork;
+import org.opencord.cordvtn.api.VtnNetworkEvent;
+import org.opencord.cordvtn.api.VtnNetworkListener;
+import org.opencord.cordvtn.api.VtnPort;
 import org.slf4j.Logger;
 
 import java.util.Date;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.ExecutorService;
 
-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.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.ACCESS_AGENT;
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.MANAGEMENT;
 import static org.opencord.cordvtn.api.Constants.*;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.ACCESS_AGENT;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.MANAGEMENT_HOST;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.MANAGEMENT_LOCAL;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -87,14 +83,13 @@
         InstanceService {
 
     protected final Logger log = getLogger(getClass());
+    private static final String ERR_VTN_NETWORK = "Faild to get VTN network for %s";
+    private static final String ERR_VTN_PORT = "Faild to get VTN port for %s";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostProviderRegistry hostProviderRegistry;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -104,33 +99,28 @@
     protected HostService hostService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DhcpService dhcpService;
+    protected LeadershipService leadershipService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected XosClientService xosClient;
+    protected ClusterService clusterService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DhcpService dhcpService;
 
     // TODO get access agent container information from XOS
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordConfigService cordConfig;
 
-    private static final Class<CordVtnConfig> CONFIG_CLASS = CordVtnConfig.class;
-    private final ConfigFactory configFactory =
-            new ConfigFactory<ApplicationId, CordVtnConfig>(
-                    SubjectFactories.APP_SUBJECT_FACTORY, CONFIG_CLASS, "cordvtn") {
-                @Override
-                public CordVtnConfig createConfig() {
-                    return new CordVtnConfig();
-                }
-            };
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnService vtnService;
 
     private final ExecutorService eventExecutor =
             newSingleThreadExecutor(groupedThreads(this.getClass().getSimpleName(), "event-handler"));
-    private final NetworkConfigListener configListener = new InternalConfigListener();
+    private final VtnNetworkListener vtnNetListener = new InternalVtnNetworkListener();
 
     private ApplicationId appId;
+    private NodeId localNodeId;
     private HostProviderService hostProvider;
-    private XosAccess xosAccess = null;
-    private OpenStackAccess osAccess = null;
 
     /**
      * Creates an cordvtn host location provider.
@@ -142,22 +132,22 @@
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(CORDVTN_APP_ID);
+        localNodeId = clusterService.getLocalNode().id();
+        leadershipService.runForLeadership(appId.name());
 
         hostProvider = hostProviderRegistry.register(this);
-        configRegistry.registerConfigFactory(configFactory);
-        configRegistry.addListener(configListener);
+        vtnService.addListener(vtnNetListener);
 
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
+        vtnService.removeListener(vtnNetListener);
         hostProviderRegistry.unregister(this);
-
-        configRegistry.unregisterConfigFactory(configFactory);
-        configRegistry.removeListener(configListener);
-
         eventExecutor.shutdown();
+        leadershipService.withdraw(appId.name());
+
         log.info("Stopped");
     }
 
@@ -184,24 +174,26 @@
             return;
         }
 
-        VtnPort vtnPort = getVtnPort(port.annotations().value(PORT_NAME));
+        VtnPort vtnPort = vtnService.getVtnPort(port.annotations().value(PORT_NAME));
         if (vtnPort == null) {
+            log.warn(String.format(ERR_VTN_PORT, port));
             return;
         }
 
-        VtnService vtnService = getVtnService(vtnPort.serviceId());
-        if (vtnService == null) {
+        VtnNetwork vtnNet = vtnService.getVtnNetworkOrDefault(vtnPort.netId());
+        if (vtnNet == null) {
+            log.warn(String.format(ERR_VTN_NETWORK, vtnPort));
             return;
         }
 
         // register DHCP lease for the new instance
-        registerDhcpLease(vtnPort.mac(), vtnPort.ip().getIp4Address(), vtnService);
+        registerDhcpLease(vtnPort.mac(), vtnPort.ip().getIp4Address(), vtnNet);
 
         // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
         // existing instances.
         DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                .set(Instance.SERVICE_TYPE, vtnService.serviceType().toString())
-                .set(Instance.SERVICE_ID, vtnPort.serviceId().id())
+                .set(Instance.NETWORK_TYPE, vtnNet.type().name())
+                .set(Instance.NETWORK_ID, vtnNet.id().id())
                 .set(Instance.PORT_ID, vtnPort.id().id())
                 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
 
@@ -247,54 +239,28 @@
         hostProvider.hostVanished(hostId);
     }
 
-    private void registerDhcpLease(MacAddress macAddr, Ip4Address ipAddr, VtnService service) {
+    private void registerDhcpLease(MacAddress macAddr, Ip4Address ipAddr, VtnNetwork vtnNet) {
         Ip4Address broadcast = Ip4Address.makeMaskedAddress(
                 ipAddr,
-                service.subnet().prefixLength());
+                vtnNet.subnet().prefixLength());
 
         IpAssignment.Builder ipBuilder = IpAssignment.builder()
                 .ipAddress(ipAddr)
                 .leasePeriod(DHCP_INFINITE_LEASE)
                 .timestamp(new Date())
-                .subnetMask(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()))
+                .subnetMask(Ip4Address.makeMaskPrefix(vtnNet.subnet().prefixLength()))
                 .broadcast(broadcast)
                 .domainServer(DEFAULT_DNS)
                 .assignmentStatus(Option_RangeNotEnforced);
 
-        if (service.serviceType() != MANAGEMENT) {
-            ipBuilder = ipBuilder.routerAddress(service.serviceIp().getIp4Address());
+        if (vtnNet.type() != MANAGEMENT_HOST && vtnNet.type() != MANAGEMENT_LOCAL) {
+            ipBuilder = ipBuilder.routerAddress(vtnNet.serviceIp().getIp4Address());
         }
 
         log.debug("Set static DHCP mapping for {} {}", macAddr, ipAddr);
         dhcpService.setStaticMapping(macAddr, ipBuilder.build());
     }
 
-    private VtnService getVtnService(VtnServiceId serviceId) {
-        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
-        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
-
-        // TODO remove openstack access when XOS provides all information
-        VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
-        VtnService service = serviceApi.service(serviceId, osAccess);
-        if (service == null) {
-            log.warn("Failed to get VtnService for {}", serviceId);
-        }
-        return service;
-    }
-
-    private VtnPort getVtnPort(String portName) {
-        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
-        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
-
-        // TODO remove openstack access when XOS provides all information
-        VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
-        VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
-        if (vtnPort == null) {
-            log.warn("Failed to get port information of {}", portName);
-        }
-        return vtnPort;
-    }
-
     // TODO remove this when XOS provides access agent information
     private boolean isAccessAgent(ConnectPoint connectPoint) {
         Optional<AccessAgentData> agent = cordConfig.getAccessAgent(connectPoint.deviceId());
@@ -308,8 +274,8 @@
     private void addAccessAgentInstance(ConnectPoint connectPoint) {
         AccessAgentData agent = cordConfig.getAccessAgent(connectPoint.deviceId()).get();
         DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                .set(Instance.SERVICE_TYPE, ACCESS_AGENT.name())
-                .set(Instance.SERVICE_ID, NOT_APPLICABLE)
+                .set(Instance.NETWORK_TYPE, ACCESS_AGENT.name())
+                .set(Instance.NETWORK_ID, NOT_APPLICABLE)
                 .set(Instance.PORT_ID, NOT_APPLICABLE)
                 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
 
@@ -324,32 +290,34 @@
         hostProvider.hostDetected(hostId, hostDesc, false);
     }
 
-    private void readConfiguration() {
-        CordVtnConfig config = configRegistry.getConfig(appId, CONFIG_CLASS);
-        if (config == null) {
-            log.debug("No configuration found");
-            return;
-        }
-
-        log.info("Load CORD-VTN configurations");
-        xosAccess = config.xosAccess();
-        osAccess = config.openstackAccess();
-    }
-
-    private class InternalConfigListener implements NetworkConfigListener {
+    private class InternalVtnNetworkListener implements VtnNetworkListener {
 
         @Override
-        public void event(NetworkConfigEvent event) {
-            if (!event.configClass().equals(CONFIG_CLASS)) {
+        public void event(VtnNetworkEvent event) {
+            NodeId leader = leadershipService.getLeader(appId.name());
+            if (!Objects.equals(localNodeId, leader)) {
+                // do not allow to proceed without leadership
                 return;
             }
 
             switch (event.type()) {
-                case CONFIG_UPDATED:
-                case CONFIG_ADDED:
-                    readConfiguration();
+                case VTN_PORT_CREATED:
+                case VTN_PORT_UPDATED:
+                    log.debug("Processing service port {}", event.vtnPort());
+                    PortId portId = event.vtnPort().id();
+                    eventExecutor.execute(() -> {
+                        Instance instance = vtnService.getInstance(portId);
+                        if (instance != null) {
+                            addInstance(instance.host().location());
+                        }
+                    });
                     break;
+                case VTN_PORT_REMOVED:
+                case VTN_NETWORK_CREATED:
+                case VTN_NETWORK_UPDATED:
+                case VTN_NETWORK_REMOVED:
                 default:
+                    // do nothing for the other events
                     break;
             }
         }
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
new file mode 100644
index 0000000..b75051f
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
@@ -0,0 +1,153 @@
+/*
+ * 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.opencord.cordvtn.impl.handler;
+
+import com.google.common.collect.ImmutableSet;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.opencord.cordvtn.api.Constants;
+import org.opencord.cordvtn.api.CordVtnService;
+import org.opencord.cordvtn.api.Instance;
+import org.opencord.cordvtn.api.InstanceHandler;
+import org.opencord.cordvtn.api.NetworkId;
+import org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType;
+import org.opencord.cordvtn.api.VtnNetwork;
+import org.opencord.cordvtn.api.VtnPort;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides default virtual network connectivity for service instances.
+ */
+public abstract class AbstractInstanceHandler implements InstanceHandler {
+
+    protected final Logger log = getLogger(getClass());
+
+    protected static final String ERR_VTN_NETWORK = "Failed to get VTN network for %s";
+    protected static final String ERR_VTN_PORT = "Failed to get VTN port for %s";
+
+    protected CoreService coreService;
+    protected MastershipService mastershipService;
+    protected HostService hostService;
+    protected CordVtnService vtnService;
+    protected ApplicationId appId;
+    protected Set<ServiceNetworkType> netTypes = ImmutableSet.of();
+
+    protected HostListener hostListener = new InternalHostListener();
+
+    protected final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+
+    protected void activate() {
+        ServiceDirectory services = new DefaultServiceDirectory();
+        coreService = services.get(CoreService.class);
+        mastershipService = services.get(MastershipService.class);
+        hostService = services.get(HostService.class);
+        vtnService = services.get(CordVtnService.class);
+
+        appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
+        hostService.addListener(hostListener);
+
+        log.info("Started");
+    }
+
+    protected void deactivate() {
+        hostService.removeListener(hostListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void instanceUpdated(Instance instance) {
+        instanceDetected(instance);
+    }
+
+    protected Set<Instance> getInstances(NetworkId netId) {
+        return StreamSupport.stream(hostService.getHosts().spliterator(), false)
+                .filter(host -> Objects.equals(
+                        netId.id(),
+                        host.annotations().value(Instance.NETWORK_ID)))
+                .map(Instance::of)
+                .collect(Collectors.toSet());
+    }
+
+    protected VtnNetwork getVtnNetwork(Instance instance) {
+        VtnNetwork vtnNet = vtnService.getVtnNetworkOrDefault(instance.netId());
+        if (vtnNet == null) {
+            final String error = String.format(ERR_VTN_NETWORK, instance);
+            throw new IllegalStateException(error);
+        }
+        return vtnNet;
+    }
+
+    protected VtnPort getVtnPort(Instance instance) {
+        VtnPort vtnPort = vtnService.getVtnPortOrDefault(instance.portId());
+        if (vtnPort == null) {
+            final String error = String.format(ERR_VTN_PORT, instance);
+            throw new IllegalStateException(error);
+        }
+        return vtnPort;
+    }
+
+    private class InternalHostListener implements HostListener {
+
+        @Override
+        public void event(HostEvent event) {
+            Host host = event.subject();
+            if (!mastershipService.isLocalMaster(host.location().deviceId())) {
+                // do not allow to proceed without mastership
+                return;
+            }
+
+            Instance instance = Instance.of(host);
+            if (!netTypes.isEmpty() && !netTypes.contains(instance.netType())) {
+                // not my service network instance, do nothing
+                return;
+            }
+
+            switch (event.type()) {
+                case HOST_UPDATED:
+                    eventExecutor.execute(() -> instanceUpdated(instance));
+                    break;
+                case HOST_ADDED:
+                    eventExecutor.execute(() -> instanceDetected(instance));
+                    break;
+                case HOST_REMOVED:
+                    eventExecutor.execute(() -> instanceRemoved(instance));
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/AccessAgentInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/AccessAgentInstanceHandler.java
index b2de2c3..8a818b8 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/AccessAgentInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/AccessAgentInstanceHandler.java
@@ -15,6 +15,7 @@
  */
 package org.opencord.cordvtn.impl.handler;
 
+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;
@@ -27,15 +28,12 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.opencord.cordvtn.impl.AbstractInstanceHandler;
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
-import java.util.Optional;
-
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.ACCESS_AGENT;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.ACCESS_AGENT;
 
 /**
  * Provides network connectivity for access agent instances.
@@ -51,7 +49,7 @@
 
     @Activate
     protected void activate() {
-        serviceType = Optional.of(ACCESS_AGENT);
+        netTypes = ImmutableSet.of(ACCESS_AGENT);
         super.activate();
     }
 
@@ -63,16 +61,16 @@
     @Override
     public void instanceDetected(Instance instance) {
         log.info("Access agent instance detected {}", instance);
-        accessAgentRules(instance, true);
+        populateAccessAgentRules(instance, true);
     }
 
     @Override
     public void instanceRemoved(Instance instance) {
         log.info("Access agent instance removed {}", instance);
-        accessAgentRules(instance, false);
+        populateAccessAgentRules(instance, false);
     }
 
-    private void accessAgentRules(Instance instance, boolean install) {
+    private void populateAccessAgentRules(Instance instance, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthDst(instance.mac())
                 .build();
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
index 0036578..ce3de31 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
@@ -15,6 +15,7 @@
  */
 package org.opencord.cordvtn.impl.handler;
 
+import com.google.common.collect.ImmutableSet;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 
@@ -31,17 +32,15 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.opencord.cordvtn.impl.AbstractInstanceHandler;
+import org.opencord.cordvtn.api.VtnNetwork;
 import org.opencord.cordvtn.api.CordVtnNode;
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
-import org.onosproject.xosclient.api.VtnService;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
-import java.util.Optional;
-
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.DEFAULT;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.PRIVATE;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.PUBLIC;
 
 /**
  * Provides network connectivity for default service instances.
@@ -57,7 +56,7 @@
 
     @Activate
     protected void activate() {
-        serviceType = Optional.of(DEFAULT);
+        netTypes = ImmutableSet.of(PRIVATE, PUBLIC);
         super.activate();
     }
 
@@ -70,44 +69,36 @@
     public void instanceDetected(Instance instance) {
         log.info("Instance is detected {}", instance);
 
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            log.warn("Failed to get VtnService for {}", instance);
-            return;
-        }
-        defaultConnectionRules(instance, service, true);
+        VtnNetwork vtnNet = getVtnNetwork(instance);
+        populateDefaultRules(instance, vtnNet, true);
     }
 
     @Override
     public void instanceRemoved(Instance instance) {
         log.info("Instance is removed {}", instance);
 
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            log.warn("Failed to get VtnService for {}", instance);
-            return;
-        }
-        defaultConnectionRules(instance, service, false);
+        VtnNetwork vtnNet = getVtnNetwork(instance);
+        populateDefaultRules(instance, vtnNet, false);
     }
 
-    private void defaultConnectionRules(Instance instance, VtnService service, boolean install) {
-        long vni = service.vni();
-        Ip4Prefix serviceIpRange = service.subnet().getIp4Prefix();
+    private void populateDefaultRules(Instance instance, VtnNetwork vtnNet, boolean install) {
+        long vni = vtnNet.segmentId().id();
+        Ip4Prefix serviceIpRange = vtnNet.subnet().getIp4Prefix();
 
-        inPortRule(instance, install);
-        dstIpRule(instance, vni, install);
-        tunnelInRule(instance, vni, install);
+        populateInPortRule(instance, install);
+        populateDstIpRule(instance, vni, install);
+        populateTunnelInRule(instance, vni, install);
 
         if (install) {
-            directAccessRule(serviceIpRange, serviceIpRange, true);
-            serviceIsolationRule(serviceIpRange, true);
-        } else if (getInstances(service.id()).isEmpty()) {
-            directAccessRule(serviceIpRange, serviceIpRange, false);
-            serviceIsolationRule(serviceIpRange, false);
+            populateDirectAccessRule(serviceIpRange, serviceIpRange, true);
+            populateServiceIsolationRule(serviceIpRange, true);
+        } else if (getInstances(vtnNet.id()).isEmpty()) {
+            populateDirectAccessRule(serviceIpRange, serviceIpRange, false);
+            populateServiceIsolationRule(serviceIpRange, false);
         }
     }
 
-    private void inPortRule(Instance instance, boolean install) {
+    private void populateInPortRule(Instance instance, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchInPort(instance.portNumber())
                 .matchEthType(Ethernet.TYPE_IPV4)
@@ -151,7 +142,7 @@
         pipeline.processFlowRule(install, flowRule);
     }
 
-    private void dstIpRule(Instance instance, long vni, boolean install) {
+    private void populateDstIpRule(Instance instance, long vni, boolean install) {
         Ip4Address tunnelIp = nodeManager.dataIp(instance.deviceId()).getIp4Address();
 
         TrafficSelector selector = DefaultTrafficSelector.builder()
@@ -208,7 +199,7 @@
         }
     }
 
-    private void tunnelInRule(Instance instance, long vni, boolean install) {
+    private void populateTunnelInRule(Instance instance, long vni, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchTunnelId(vni)
                 .matchEthDst(instance.mac())
@@ -231,7 +222,7 @@
         pipeline.processFlowRule(install, flowRule);
     }
 
-    private void directAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
+    private void populateDirectAccessRule(Ip4Prefix srcRange, Ip4Prefix dstRange, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPSrc(srcRange)
@@ -258,7 +249,7 @@
         });
     }
 
-    private void serviceIsolationRule(Ip4Prefix dstRange, boolean install) {
+    private void populateServiceIsolationRule(Ip4Prefix dstRange, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPDst(dstRange)
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
index 7e0285e..496ed66 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
@@ -15,6 +15,7 @@
  */
 package org.opencord.cordvtn.impl.handler;
 
+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;
@@ -28,19 +29,18 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.xosclient.api.VtnService;
-import org.opencord.cordvtn.impl.AbstractInstanceHandler;
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
+import org.opencord.cordvtn.api.VtnNetwork;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
-import java.util.Optional;
-
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.MANAGEMENT;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.MANAGEMENT_HOST;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.MANAGEMENT_LOCAL;
 
 /**
- * Provides network connectivity for management network connected instances.
+ * Provides local management network connectivity to the instance. The instance
+ * is only accessible via local management network from the host machine.
  */
 @Component(immediate = true)
 public class ManagementInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
@@ -53,7 +53,7 @@
 
     @Activate
     protected void activate() {
-        serviceType = Optional.of(MANAGEMENT);
+        netTypes = ImmutableSet.of(MANAGEMENT_LOCAL, MANAGEMENT_HOST);
         super.activate();
     }
 
@@ -64,20 +64,16 @@
 
     @Override
     public void instanceDetected(Instance instance) {
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            log.warn("Failed to get VtnService for {}", instance);
-            return;
-        }
+        VtnNetwork vtnNet = getVtnNetwork(instance);
 
-        switch (service.networkType()) {
+        switch (vtnNet.type()) {
             case MANAGEMENT_LOCAL:
                 log.info("LOCAL management instance is detected {}", instance);
-                localManagementRules(instance, true);
+                populateLocalManagementRules(instance, true);
                 break;
-            case MANAGEMENT_HOSTS:
+            case MANAGEMENT_HOST:
                 log.info("HOSTS management instance is detected {}", instance);
-                hostsManagementRules(instance, true);
+                populateHostsManagementRules(instance, true);
                 break;
             default:
                 break;
@@ -86,27 +82,23 @@
 
     @Override
     public void instanceRemoved(Instance instance) {
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            log.warn("Failed to get VtnService for {}", instance);
-            return;
-        }
+        VtnNetwork vtnNet = getVtnNetwork(instance);
 
-        switch (service.networkType()) {
+        switch (vtnNet.type()) {
             case MANAGEMENT_LOCAL:
                 log.info("LOCAL management instance removed {}", instance);
-                localManagementRules(instance, false);
+                populateLocalManagementRules(instance, false);
                 break;
-            case MANAGEMENT_HOSTS:
+            case MANAGEMENT_HOST:
                 log.info("HOSTS management instance removed {}", instance);
-                hostsManagementRules(instance, false);
+                populateHostsManagementRules(instance, false);
                 break;
             default:
                 break;
         }
     }
 
-    private void localManagementRules(Instance instance, boolean install) {
+    private void populateLocalManagementRules(Instance instance, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
                 .matchIPDst(instance.ipAddress().toIpPrefix())
@@ -130,7 +122,7 @@
         pipeline.processFlowRule(install, flowRule);
     }
 
-    private void hostsManagementRules(Instance instance, boolean install) {
+    private void populateHostsManagementRules(Instance instance, boolean install) {
         PortNumber hostMgmtPort = nodeManager.hostManagementPort(instance.deviceId());
         if (hostMgmtPort == null) {
             log.warn("Can not find host management port in {}", instance.deviceId());
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
index 298d60a..27ea199 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
@@ -16,20 +16,21 @@
 package org.opencord.cordvtn.impl.handler;
 
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.Host;
+import org.opencord.cordvtn.api.AddressPair;
 import org.opencord.cordvtn.api.InstanceService;
-import org.opencord.cordvtn.impl.AbstractInstanceHandler;
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
 import org.onosproject.net.DefaultAnnotations;
@@ -49,35 +50,37 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
-import org.onosproject.xosclient.api.VtnPort;
+import org.opencord.cordvtn.api.VtnPort;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
-import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
+import java.util.stream.Collectors;
 
-import static com.google.common.base.Preconditions.checkNotNull;
 import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
-import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.VSG;
+import static org.opencord.cordvtn.api.ServiceNetwork.ServiceNetworkType.VSG;
 
 /**
  * Provides network connectivity for vSG instances.
  */
 @Component(immediate = true)
-@Service(value = VsgInstanceHandler.class)
 public final class VsgInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
 
+    private static final String STAG = "stag";
+    private static final String VSG_VM = "vsgVm";
+    private static final String ERR_VSG_VM = "vSG VM does not exist for %s";
+    private static final String MSG_VSG_VM = "vSG VM %s: %s";
+    private static final String MSG_VSG_CONTAINER = "vSG container %s: %s";
+    private static final String MSG_DETECTED = "detected";
+    private static final String MSG_REMOVED = "removed";
+
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnPipeline pipeline;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnNodeManager nodeManager;
 
-    private static final String STAG = "stag";
-    private static final String VSG_VM = "vsgVm";
-
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected FlowRuleService flowRuleService;
 
@@ -86,7 +89,7 @@
 
     @Activate
     protected void activate() {
-        serviceType = Optional.of(VSG);
+        netTypes = ImmutableSet.of(VSG);
         super.activate();
     }
 
@@ -98,124 +101,106 @@
     @Override
     public void instanceDetected(Instance instance) {
         if (isVsgContainer(instance)) {
-            log.info("vSG container detected {}", instance);
-
-            // find vsg vm for this vsg container
-            String vsgVmId = instance.getAnnotation(VSG_VM);
-            if (Strings.isNullOrEmpty(vsgVmId)) {
-                log.warn("Failed to find vSG VM for {}", instance);
-                return;
+            log.info(String.format(MSG_VSG_CONTAINER, MSG_DETECTED, instance));
+            Instance vsgVm = getVsgVm(instance);
+            if (vsgVm == null) {
+                final String error = String.format(ERR_VSG_VM, instance);
+                throw new IllegalStateException(error);
             }
 
-            Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
             VtnPort vtnPort = getVtnPort(vsgVm);
-            if (vtnPort == null || getStag(vtnPort) == null) {
-                log.warn("Failed to get vSG information {}", vsgVm);
-                return;
-            }
-            populateVsgRules(vsgVm, getStag(vtnPort),
-                             nodeManager.dataPort(vsgVm.deviceId()),
-                             vtnPort.addressPairs().keySet(),
-                             true);
-
+            Set<IpAddress> wanIps = vtnPort.addressPairs().stream()
+                    .map(AddressPair::ip).collect(Collectors.toSet());
+            populateVsgRules(
+                    vsgVm, vtnPort.vlanId().get(),
+                    nodeManager.dataPort(vsgVm.deviceId()),
+                    wanIps, true);
         } else {
-            VtnPort vtnPort = getVtnPort(instance);
-            if (vtnPort == null || getStag(vtnPort) == null) {
+            log.info(String.format(MSG_VSG_VM, MSG_DETECTED, instance));
+            VtnPort vtnPort = vtnService.getVtnPortOrDefault(instance.portId());
+            if (vtnPort == null || !vtnPort.vlanId().isPresent()) {
+                // service port can be updated after instance is created
                 return;
             }
-            log.info("vSG VM detected {}", instance);
 
             // insert vSG containers inside the vSG VM as a host
-            vtnPort.addressPairs().entrySet().stream()
-                    .forEach(pair -> addVsgContainer(
-                            instance,
-                            pair.getKey(),
-                            pair.getValue(),
-                            getStag(vtnPort).toString()
-                    ));
+            vtnPort.addressPairs().stream().forEach(pair -> addVsgContainer(
+                    instance,
+                    pair.ip(),
+                    pair.mac(),
+                    vtnPort.vlanId().get()));
         }
     }
 
     @Override
-    public void instanceRemoved(Instance instance) {
+    public void instanceUpdated(Instance instance) {
         if (!isVsgContainer(instance)) {
-            // nothing to do for the vSG VM itself
-            return;
+            Set<MacAddress> vsgMacs = getVtnPort(instance).addressPairs().stream()
+                    .map(AddressPair::mac)
+                    .collect(Collectors.toSet());
+            hostService.getConnectedHosts(instance.host().location()).stream()
+                    .filter(h -> !h.mac().equals(instance.mac()))
+                    .filter(h -> !vsgMacs.contains(h.mac()))
+                    .forEach(h -> instanceService.removeNestedInstance(h.id()));
         }
-
-        log.info("vSG container vanished {}", instance);
-
-        // find vsg vm for this vsg container
-        String vsgVmId = instance.getAnnotation(VSG_VM);
-        if (Strings.isNullOrEmpty(vsgVmId)) {
-            log.warn("Failed to find vSG VM for {}", instance);
-            return;
-        }
-
-        Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
-        VtnPort vtnPort = getVtnPort(vsgVm);
-        if (vtnPort == null || getStag(vtnPort) == null) {
-            return;
-        }
-
-        populateVsgRules(vsgVm, getStag(vtnPort),
-                         nodeManager.dataPort(vsgVm.deviceId()),
-                         vtnPort.addressPairs().keySet(),
-                         false);
+        instanceDetected(instance);
     }
 
-    /**
-     * Updates set of vSGs in a given vSG VM.
-     *
-     * @param vsgVmId vsg vm host id
-     * @param stag stag
-     * @param vsgInstances full set of vsg wan ip and mac address pairs in this vsg vm
-     */
-    public void updateVsgInstances(HostId vsgVmId, String stag, Map<IpAddress, MacAddress> vsgInstances) {
-        if (hostService.getHost(vsgVmId) == null) {
-            log.debug("vSG VM {} is not added yet, ignore this update", vsgVmId);
-            return;
-        }
+    @Override
+    public void instanceRemoved(Instance instance) {
+        boolean isVsgContainer = isVsgContainer(instance);
+        log.info(String.format(
+                isVsgContainer ? MSG_VSG_CONTAINER : MSG_VSG_VM,
+                MSG_REMOVED, instance));
 
-        Instance vsgVm = Instance.of(hostService.getHost(vsgVmId));
+        Instance vsgVm = isVsgContainer ? getVsgVm(instance) : instance;
         if (vsgVm == null) {
-            log.warn("Failed to find existing vSG VM for STAG: {}", stag);
+            // the rules are removed when VM is removed, do nothing
             return;
         }
 
-        log.info("Updates vSGs in {} with STAG: {}", vsgVm, stag);
+        VtnPort vtnPort = getVtnPort(instance);
+        Set<IpAddress> wanIps = vtnPort.addressPairs().stream()
+                .map(AddressPair::ip).collect(Collectors.toSet());
+        populateVsgRules(
+                vsgVm, vtnPort.vlanId().get(),
+                nodeManager.dataPort(vsgVm.deviceId()),
+                isVsgContainer ? wanIps : ImmutableSet.of(),
+                false);
+    }
 
-        // adds vSGs in the address pair
-        vsgInstances.entrySet().stream()
-                .filter(addr -> hostService.getHostsByMac(addr.getValue()).isEmpty())
-                .forEach(addr -> addVsgContainer(
-                        vsgVm,
-                        addr.getKey(),
-                        addr.getValue(),
-                        stag));
-
-        // removes vSGs not listed in the address pair
-        hostService.getConnectedHosts(vsgVm.host().location()).stream()
-                .filter(host -> !host.mac().equals(vsgVm.mac()))
-                .filter(host -> !vsgInstances.values().contains(host.mac()))
-                .forEach(host -> {
-                    log.info("Removed vSG {}", host.toString());
-                    instanceService.removeNestedInstance(host.id());
-                });
+    @Override
+    protected VtnPort getVtnPort(Instance instance) {
+        VtnPort vtnPort = vtnService.getVtnPortOrDefault(instance.portId());
+        if (vtnPort == null || !vtnPort.vlanId().isPresent()) {
+            final String error = String.format(ERR_VTN_PORT, instance);
+            throw new IllegalStateException(error);
+        }
+        return vtnPort;
     }
 
     private boolean isVsgContainer(Instance instance) {
-        return !Strings.isNullOrEmpty(instance.host().annotations().value(STAG));
+        return  !Strings.isNullOrEmpty(instance.getAnnotation(STAG)) &&
+                !Strings.isNullOrEmpty(instance.getAnnotation(VSG_VM));
+    }
+
+    private Instance getVsgVm(Instance vsgContainer) {
+        String vsgVmId = vsgContainer.getAnnotation(VSG_VM);
+        Host host = hostService.getHost(HostId.hostId(vsgVmId));
+        if (host == null) {
+            return null;
+        }
+        return Instance.of(host);
     }
 
     private void addVsgContainer(Instance vsgVm, IpAddress vsgWanIp, MacAddress vsgMac,
-                                 String stag) {
+                                 VlanId stag) {
         HostId hostId = HostId.hostId(vsgMac);
         DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                .set(Instance.SERVICE_TYPE, vsgVm.serviceType().toString())
-                .set(Instance.SERVICE_ID, vsgVm.serviceId().id())
+                .set(Instance.NETWORK_TYPE, vsgVm.netType().toString())
+                .set(Instance.NETWORK_ID, vsgVm.netId().id())
                 .set(Instance.PORT_ID, vsgVm.portId().id())
-                .set(STAG, stag)
+                .set(STAG, stag.toString())
                 .set(VSG_VM, vsgVm.host().id().toString())
                 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
 
@@ -278,10 +263,11 @@
 
         // for traffic coming from WAN, tag 500 and take through the vSG VM
         // based on destination ip
-        vsgWanIps.stream().forEach(ip -> {
+        vsgWanIps.stream().forEach(wanIp -> {
+            // for traffic coming from WAN, tag 500 and take through the vSG VM
             TrafficSelector downstream = DefaultTrafficSelector.builder()
                     .matchEthType(Ethernet.TYPE_IPV4)
-                    .matchIPDst(ip.toIpPrefix())
+                    .matchIPDst(wanIp.toIpPrefix())
                     .build();
 
             TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
@@ -322,18 +308,6 @@
         }
     }
 
-    // TODO get stag from XOS when XOS provides it, extract if from port name for now
-    private VlanId getStag(VtnPort vtnPort) {
-        checkNotNull(vtnPort);
-
-        String portName = vtnPort.name();
-        if (portName != null && portName.startsWith(STAG)) {
-            return VlanId.vlanId(portName.split("-")[1]);
-        } else {
-            return null;
-        }
-    }
-
     private PortNumber getOutputFromTreatment(FlowRule flowRule) {
         Instruction instruction = flowRule.treatment().allInstructions().stream()
                 .filter(inst -> inst instanceof Instructions.OutputInstruction)
@@ -356,11 +330,10 @@
     }
 
     private boolean isVlanPushFromTreatment(FlowRule flowRule) {
-        Instruction instruction = flowRule.treatment().allInstructions().stream()
+        return flowRule.treatment().allInstructions().stream()
                 .filter(inst -> inst instanceof L2ModificationInstruction)
                 .filter(inst -> ((L2ModificationInstruction) inst).subtype().equals(VLAN_PUSH))
                 .findAny()
-                .orElse(null);
-        return instruction != null;
+                .isPresent();
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java
index 5e0a580..81f82ee 100644
--- a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java
@@ -15,51 +15,182 @@
  */
 package org.opencord.cordvtn.rest;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.osgi.DefaultServiceDirectory;
 import org.onosproject.rest.AbstractWebResource;
+import org.opencord.cordvtn.api.CordVtnAdminService;
+import org.opencord.cordvtn.api.NetworkId;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.Network;
+import org.openstack4j.openstack.networking.domain.NeutronNetwork;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
+import java.util.Set;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.*;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
 
 /**
- * Dummy Neutron ML2 mechanism driver.
- * It just returns OK for networks resource requests.
+ * Neutron ML2 mechanism driver implementation for the network resource.
  */
 @Path("networks")
 public class NeutronMl2NetworksWebResource extends AbstractWebResource {
     protected final Logger log = LoggerFactory.getLogger(getClass());
-    private static final String NETWORKS_MESSAGE = "Received networks %s";
 
+    private static final String MESSAGE = "Received networks %s request";
+    private static final String NETWORK  = "network";
+    private static final String NETWORKS = "networks";
+
+    private final CordVtnAdminService adminService =
+            DefaultServiceDirectory.getService(CordVtnAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a network from the JSON input stream.
+     *
+     * @param input network JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated network already exists
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createNetwork(InputStream input) {
-        log.trace(String.format(NETWORKS_MESSAGE, "create"));
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronNetwork net = readNetwork(input);
+        adminService.createNetwork(net);
+
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(NETWORKS)
+                .path(net.getId());
+
+        return created(locationBuilder.build()).build();
     }
 
+    /**
+     * Updates the network with the specified identifier.
+     *
+     * @param id network identifier
+     * @param input network JSON input stream
+     * @return 200 OK with the updated network, 400 BAD_REQUEST if the requested
+     * network does not exist
+     */
     @PUT
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response updateNetwork(@PathParam("id") String id, InputStream input) {
-        log.trace(String.format(NETWORKS_MESSAGE, "update"));
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "UPDATE " + id));
+
+        final NeutronNetwork net = readNetwork(input);
+        adminService.updateNetwork(net);
+
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(NETWORK, writeNetwork(net))).build();
     }
 
+    /**
+     * Returns all networks.
+     *
+     * @return 200 OK with set of networks
+     */
+    @GET
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getNetworks() {
+        log.trace(String.format(MESSAGE, "GET"));
+
+        Set<Network> nets = adminService.getNetworks();
+        ArrayNode arrayNodes = mapper().createArrayNode();
+        nets.stream().forEach(net -> {
+            arrayNodes.add(writeNetwork(net));
+        });
+
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(NETWORKS, arrayNodes)).build();
+    }
+
+    /**
+     * Returns the network with the given network id.
+     *
+     * @param id network id
+     * @return 200 OK with the network, 404 NOT_FOUND if the network does not exist
+     */
+    @GET
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getNetwork(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "GET " + id));
+
+        Network net = adminService.getNetwork(NetworkId.of(id));
+        if (net == null) {
+            return status(NOT_FOUND).build();
+        }
+
+        ObjectNode result = mapper().createObjectNode();
+        return ok(result.set(NETWORK, writeNetwork(net))).build();
+    }
+
+    /**
+     * Removes the service network.
+     *
+     * @param id network identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the network does not exist
+     */
     @DELETE
     @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
     public Response deleteNetwork(@PathParam("id") String id) {
-        log.trace(String.format(NETWORKS_MESSAGE, "delete"));
-        return Response.noContent().build();
+        log.trace(String.format(MESSAGE, "DELETE " + id));
+
+        adminService.removeNetwork(NetworkId.of(id));
+        return noContent().build();
+    }
+
+    private NeutronNetwork readNetwork(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronNetwork.class)
+                    .readerFor(NeutronNetwork.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private ObjectNode writeNetwork(Network net) {
+        try {
+            String strNet = ObjectMapperSingleton.getContext(NeutronNetwork.class)
+                    .writerFor(NeutronNetwork.class)
+                    .writeValueAsString(net);
+            log.trace(strNet);
+            return (ObjectNode) mapper().readTree(strNet.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2PortsWebResource.java b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2PortsWebResource.java
index 560d01d..32417bc 100644
--- a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2PortsWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2PortsWebResource.java
@@ -16,101 +16,183 @@
 package org.opencord.cordvtn.rest;
 
 import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.Maps;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.osgi.DefaultServiceDirectory;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.opencord.cordvtn.impl.handler.VsgInstanceHandler;
-import org.onosproject.net.HostId;
+import org.opencord.cordvtn.api.CordVtnAdminService;
+import org.opencord.cordvtn.api.PortId;
 import org.onosproject.rest.AbstractWebResource;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.Port;
+import org.openstack4j.openstack.networking.domain.NeutronPort;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
-import java.util.Map;
+import java.util.Set;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
 
 
 /**
- * Dummy Neutron ML2 mechanism driver.
- * It just returns OK for ports resource requests except for the port update.
+ * Neutron ML2 mechanism driver implementation for the port resource.
  */
 @Path("ports")
 public class NeutronMl2PortsWebResource extends AbstractWebResource {
     protected final Logger log = LoggerFactory.getLogger(getClass());
-    private static final String PORTS_MESSAGE = "Received ports %s";
 
-    private static final String PORT = "port";
-    private static final String DEVICE_ID = "device_id";
-    private static final String NAME = "name";
-    private static final String MAC_ADDRESS = "mac_address";
-    private static final String ADDRESS_PAIRS = "allowed_address_pairs";
-    private static final String IP_ADDERSS = "ip_address";
-    private static final String STAG_PREFIX = "stag-";
-    private static final int STAG_BEGIN_INDEX = 5;
+    private static final String MESSAGE = "Received ports %s request";
+    private static final String PORT  = "port";
+    private static final String PORTS = "ports";
 
-    private final VsgInstanceHandler service = DefaultServiceDirectory.getService(VsgInstanceHandler.class);
+    private final CordVtnAdminService adminService =
+            DefaultServiceDirectory.getService(CordVtnAdminService.class);
 
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a port from the JSON input stream.
+     *
+     * @param input port JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated port already exists
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createPorts(InputStream input) {
-        log.trace(String.format(PORTS_MESSAGE, "create"));
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronPort port = readPort(input);
+        adminService.createPort(port);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(PORTS)
+                .path(port.getId());
+
+        return created(locationBuilder.build()).build();
     }
 
+    /**
+     * Updates the port with the specified identifier.
+     *
+     * @param id    port identifier
+     * @param input port JSON input stream
+     * @return 200 OK with the updated port, 400 BAD_REQUEST if the requested
+     * port does not exist
+     */
     @PUT
     @Path("{id}")
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
-    public Response updatePorts(@PathParam("id") String id, InputStream input) {
-        log.debug(String.format(PORTS_MESSAGE, "update"));
+    public Response updatePort(@PathParam("id") String id, InputStream input) {
+        log.trace(String.format(MESSAGE, "UPDATE " + id));
 
-        // TODO get vSG updates from XOS to CORD VTN service directly
-        try {
-            ObjectMapper mapper = new ObjectMapper();
-            JsonNode jsonNode = mapper.readTree(input).get(PORT);
-            log.trace("{}", jsonNode.toString());
+        final NeutronPort port = readPort(input);
+        adminService.updatePort(port);
 
-            String deviceId = jsonNode.path(DEVICE_ID).asText();
-            String name = jsonNode.path(NAME).asText();
-            if (deviceId.isEmpty() || name.isEmpty() || !name.startsWith(STAG_PREFIX)) {
-                // ignore all updates other than allowed address pairs
-                return Response.status(Response.Status.OK).build();
-            }
-
-            // this is allowed address pairs updates
-            MacAddress mac = MacAddress.valueOf(jsonNode.path(MAC_ADDRESS).asText());
-            Map<IpAddress, MacAddress> vsgInstances = Maps.newHashMap();
-            jsonNode.path(ADDRESS_PAIRS).forEach(addrPair -> {
-                IpAddress pairIp = IpAddress.valueOf(addrPair.path(IP_ADDERSS).asText());
-                MacAddress pairMac = MacAddress.valueOf(addrPair.path(MAC_ADDRESS).asText());
-                vsgInstances.put(pairIp, pairMac);
-            });
-
-            service.updateVsgInstances(HostId.hostId(mac),
-                                       name.substring(STAG_BEGIN_INDEX),
-                                       vsgInstances);
-        } catch (Exception e) {
-            return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
-        }
-
-        return Response.status(Response.Status.OK).build();
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(PORT, writePort(port))).build();
     }
 
+    /**
+     * Returns all ports.
+     *
+     * @return 200 OK with set of ports
+     */
+    @GET
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getPorts() {
+        log.trace(String.format(MESSAGE, "GET"));
+
+        Set<Port> ports = adminService.getPorts();
+        ArrayNode arrayNodes = mapper().createArrayNode();
+        ports.stream().forEach(port -> {
+            arrayNodes.add(writePort(port));
+        });
+
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(PORTS, arrayNodes)).build();
+    }
+
+    /**
+     * Returns the port with the given id.
+     *
+     * @param id port id
+     * @return 200 OK with the port, 404 NOT_FOUND if the port does not exist
+     */
+    @GET
     @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getPort(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "GET " + id));
+
+        Port port = adminService.getPort(PortId.of(id));
+        if (port == null) {
+            return status(NOT_FOUND).build();
+        }
+
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(PORT, writePort(port))).build();
+    }
+
+    /**
+     * Removes the port with the given id.
+     *
+     * @param id port identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the port does not exist
+     */
     @DELETE
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
     public Response deletePorts(@PathParam("id") String id) {
-        log.trace(String.format(PORTS_MESSAGE, "delete"));
-        return Response.noContent().build();
+        log.trace(String.format(MESSAGE, "DELETE " + id));
+
+        adminService.removePort(PortId.of(id));
+        return noContent().build();
+    }
+
+    private NeutronPort readPort(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronPort.class)
+                    .readerFor(NeutronPort.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private ObjectNode writePort(Port port) {
+        try {
+            String strPort = ObjectMapperSingleton.getContext(NeutronPort.class)
+                    .writerFor(NeutronPort.class)
+                    .writeValueAsString(port);
+            log.trace(strPort);
+            return (ObjectNode) mapper().readTree(strPort.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2SubnetsWebResource.java b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2SubnetsWebResource.java
index 71ebbb0..4a57d28 100644
--- a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2SubnetsWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2SubnetsWebResource.java
@@ -15,53 +15,184 @@
  */
 package org.opencord.cordvtn.rest;
 
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.osgi.DefaultServiceDirectory;
 import org.onosproject.rest.AbstractWebResource;
+import org.opencord.cordvtn.api.CordVtnAdminService;
+import org.opencord.cordvtn.api.SubnetId;
+import org.openstack4j.core.transport.ObjectMapperSingleton;
+import org.openstack4j.model.network.Subnet;
+import org.openstack4j.openstack.networking.domain.NeutronSubnet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.ws.rs.Consumes;
 import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriInfo;
 import java.io.InputStream;
+import java.util.Set;
+
+import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
 
 /**
- * Dummy Neutron ML2 mechanism driver.
- * It just returns OK for subnets resource requests.
+ * Neutron ML2 mechanism driver implementation for the subnet resource.
  */
 @Path("subnets")
 public class NeutronMl2SubnetsWebResource extends AbstractWebResource {
     protected final Logger log = LoggerFactory.getLogger(getClass());
-    private static final String SUBNETS_MESSAGE = "Received subnets %s";
 
+    private static final String MESSAGE = "Received subnets %s request";
+    private static final String SUBNET  = "subnet";
+    private static final String SUBNETS = "subnets";
+
+    private final CordVtnAdminService adminService =
+            DefaultServiceDirectory.getService(CordVtnAdminService.class);
+
+    @Context
+    private UriInfo uriInfo;
+
+    /**
+     * Creates a subnet from the JSON input stream.
+     *
+     * @param input subnet JSON input stream
+     * @return 201 CREATED if the JSON is correct, 400 BAD_REQUEST if the JSON
+     * is invalid or duplicated subnet already exists
+     */
     @POST
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response createSubnet(InputStream input) {
-        log.trace(String.format(SUBNETS_MESSAGE, "create"));
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "CREATE"));
+
+        final NeutronSubnet subnet = readSubnet(input);
+        adminService.createSubnet(subnet);
+        UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
+                .path(SUBNETS)
+                .path(subnet.getId());
+
+        // TODO fix networking-onos to send Network UPDATE when subnet created
+        return created(locationBuilder.build()).build();
     }
 
-
+    /**
+     * Updates the subnet with the specified identifier.
+     *
+     * @param id    subnet identifier
+     * @param input subnet JSON input stream
+     * @return 200 OK with the updated subnet, 400 BAD_REQUEST if the requested
+     * subnet does not exist
+     */
     @PUT
     @Path("{id}")
     @Produces(MediaType.APPLICATION_JSON)
     @Consumes(MediaType.APPLICATION_JSON)
     public Response updateSubnet(@PathParam("id") String id, InputStream input) {
-        log.trace(String.format(SUBNETS_MESSAGE, "update"));
-        return Response.status(Response.Status.OK).build();
+        log.trace(String.format(MESSAGE, "UPDATE " + id));
 
+        final NeutronSubnet subnet = readSubnet(input);
+        adminService.updateSubnet(subnet);
+
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(SUBNET, writeSubnet(subnet))).build();
     }
 
+    /**
+     * Returns all subnets.
+     *
+     * @return 200 OK with set of subnets
+     */
+    @GET
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getSubnets() {
+        log.trace(String.format(MESSAGE, "GET"));
+
+        Set<Subnet> subnets = adminService.getSubnets();
+        ArrayNode arrayNodes = mapper().createArrayNode();
+        subnets.stream().forEach(subnet -> {
+            arrayNodes.add(writeSubnet(subnet));
+        });
+
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(SUBNETS, arrayNodes)).build();
+    }
+
+    /**
+     * Returns the subnet with the given subnet id.
+     *
+     * @param id subnet id
+     * @return 200 OK with the subnet, 404 NOT_FOUND if the subnet does not exist
+     */
+    @GET
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response getSubnet(@PathParam("id") String id) {
+        log.trace(String.format(MESSAGE, "GET " + id));
+
+        Subnet subnet = adminService.getSubnet(SubnetId.of(id));
+        if (subnet == null) {
+            return status(NOT_FOUND).build();
+        }
+
+        ObjectNode result = this.mapper().createObjectNode();
+        return ok(result.set(SUBNET, writeSubnet(subnet))).build();
+    }
+
+    /**
+     * Removes the subnet.
+     *
+     * @param id subnet identifier
+     * @return 204 NO_CONTENT, 400 BAD_REQUEST if the subnet does not exist
+     */
     @DELETE
     @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
     public Response deleteSubnet(@PathParam("id") String id) {
-        log.trace(String.format(SUBNETS_MESSAGE, "delete"));
-        return Response.noContent().build();
+        log.trace(String.format(MESSAGE, "DELETE " + id));
+
+        adminService.removeSubnet(SubnetId.of(id));
+        return noContent().build();
+    }
+
+    private NeutronSubnet readSubnet(InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(mapper().writeValueAsString(jsonTree));
+            return ObjectMapperSingleton.getContext(NeutronSubnet.class)
+                    .readerFor(NeutronSubnet.class)
+                    .readValue(jsonTree);
+        } catch (Exception e) {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private ObjectNode writeSubnet(Subnet subnet) {
+        try {
+            String strSubnet = ObjectMapperSingleton.getContext(NeutronSubnet.class)
+                    .writerFor(NeutronSubnet.class)
+                    .writeValueAsString(subnet);
+            log.trace(strSubnet);
+            return (ObjectNode) mapper().readTree(strSubnet.getBytes());
+        } catch (Exception e) {
+            throw new IllegalStateException();
+        }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/rest/ServiceDependencyWebResource.java b/src/main/java/org/opencord/cordvtn/rest/ServiceDependencyWebResource.java
index 9c58757..c194174 100644
--- a/src/main/java/org/opencord/cordvtn/rest/ServiceDependencyWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/ServiceDependencyWebResource.java
@@ -16,8 +16,9 @@
 package org.opencord.cordvtn.rest;
 
 import org.onosproject.rest.AbstractWebResource;
-import org.onosproject.xosclient.api.VtnServiceId;
+import org.opencord.cordvtn.api.Dependency.Type;
 import org.opencord.cordvtn.api.DependencyService;
+import org.opencord.cordvtn.api.NetworkId;
 
 import javax.ws.rs.DELETE;
 import javax.ws.rs.POST;
@@ -26,10 +27,15 @@
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.Response;
+import java.util.Objects;
+
+import static org.opencord.cordvtn.api.Dependency.Type.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.Dependency.Type.UNIDIRECTIONAL;
 
 /**
  * Manages service dependency.
  */
+@Deprecated
 @Path("service-dependency")
 public class ServiceDependencyWebResource extends AbstractWebResource {
 
@@ -48,9 +54,9 @@
     @Produces(MediaType.APPLICATION_JSON)
     public Response createServiceDependency(@PathParam("tenantServiceId") String tServiceId,
                                             @PathParam("providerServiceId") String pServiceId) {
-        service.createDependency(VtnServiceId.of(tServiceId),
-                                 VtnServiceId.of(pServiceId),
-                                 false);
+        service.createDependency(NetworkId.of(tServiceId),
+                                 NetworkId.of(pServiceId),
+                                 UNIDIRECTIONAL);
         return Response.status(Response.Status.OK).build();
     }
 
@@ -68,9 +74,10 @@
     public Response createServiceDependency(@PathParam("tenantServiceId") String tServiceId,
                                             @PathParam("providerServiceId") String pServiceId,
                                             @PathParam("direction") String direction) {
-        service.createDependency(VtnServiceId.of(tServiceId),
-                                 VtnServiceId.of(pServiceId),
-                                 direction.equals(BIDIRECTION));
+        Type type = Objects.equals(direction, BIDIRECTION) ? BIDIRECTIONAL : UNIDIRECTIONAL;
+        service.createDependency(NetworkId.of(tServiceId),
+                                 NetworkId.of(pServiceId),
+                                 type);
         return Response.status(Response.Status.OK).build();
     }
 
@@ -85,7 +92,7 @@
     @Path("{tenantServiceId}/{providerServiceId}")
     public Response removeServiceDependency(@PathParam("tenantServiceId") String tServiceId,
                                             @PathParam("providerServiceId") String pServiceId) {
-        service.removeDependency(VtnServiceId.of(tServiceId), VtnServiceId.of(pServiceId));
+        service.removeDependency(NetworkId.of(tServiceId), NetworkId.of(pServiceId));
         return Response.noContent().build();
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/rest/ServiceNetworkWebResource.java b/src/main/java/org/opencord/cordvtn/rest/ServiceNetworkWebResource.java
index fbd6ddd..a376a55 100644
--- a/src/main/java/org/opencord/cordvtn/rest/ServiceNetworkWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/ServiceNetworkWebResource.java
@@ -19,7 +19,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onosproject.rest.AbstractWebResource;
-import org.opencord.cordvtn.api.CordVtnStore;
+import org.opencord.cordvtn.api.CordVtnAdminService;
 import org.opencord.cordvtn.api.NetworkId;
 import org.opencord.cordvtn.api.ServiceNetwork;
 import org.slf4j.Logger;
@@ -40,7 +40,8 @@
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
 import static javax.ws.rs.core.Response.Status.NOT_FOUND;
@@ -56,10 +57,11 @@
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String MESSAGE = "Received service network ";
-    private static final String SERVICE_NETWORK = "ServiceNetwork";
+    private static final String SERVICE_NETWORK  = "ServiceNetwork";
     private static final String SERVICE_NETWORKS = "ServiceNetworks";
 
-    private final CordVtnStore service = DefaultServiceDirectory.getService(CordVtnStore.class);
+    private final CordVtnAdminService adminService =
+            DefaultServiceDirectory.getService(CordVtnAdminService.class);
 
     @Context
     private UriInfo uriInfo;
@@ -85,7 +87,7 @@
             }
 
             final ServiceNetwork snet = codec(ServiceNetwork.class).decode(snetJson, this);
-            service.createServiceNetwork(snet);
+            adminService.createVtnNetwork(snet);
 
             UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
                     .path(SERVICE_NETWORKS)
@@ -100,7 +102,8 @@
     /**
      * Updates the service network with the specified identifier.
      *
-     * @param id network identifier
+     * @param id    network identifier
+     * @param input service network JSON stream
      * @return 200 OK with a service network, 400 BAD_REQUEST if the requested
      * network does not exist
      */
@@ -119,7 +122,7 @@
             }
 
             final ServiceNetwork snet = codec(ServiceNetwork.class).decode(snetJson, this);
-            service.updateServiceNetwork(snet);
+            adminService.updateVtnNetwork(snet);
 
             ObjectNode result = this.mapper().createObjectNode();
             result.set(SERVICE_NETWORK, codec(ServiceNetwork.class).encode(snet, this));
@@ -140,7 +143,7 @@
     public Response getServiceNetworks() {
         log.trace(MESSAGE + "GET");
 
-        Set<ServiceNetwork> snets = service.getServiceNetworks();
+        List<ServiceNetwork> snets = new ArrayList<>(adminService.getVtnNetworks());
         return ok(encodeArray(ServiceNetwork.class, SERVICE_NETWORKS, snets)).build();
     }
 
@@ -158,13 +161,15 @@
     public Response getServiceNetwork(@PathParam("id") String id) {
         log.trace(MESSAGE + "GET " + id);
 
-        ServiceNetwork snet = service.getServiceNetwork(NetworkId.of(id));
+        ServiceNetwork snet = adminService.getVtnNetwork(NetworkId.of(id));
         if (snet == null) {
+            log.trace("Returned NOT_FOUND");
             return status(NOT_FOUND).build();
         }
 
         ObjectNode result = this.mapper().createObjectNode();
         result.set(SERVICE_NETWORK, codec(ServiceNetwork.class).encode(snet, this));
+        log.trace("Returned OK {}", result);
         return ok(result).build();
     }
 
@@ -181,7 +186,7 @@
     public Response deleteServiceNetwork(@PathParam("id") String id) {
         log.trace(MESSAGE + "DELETE " + id);
 
-        service.removeServiceNetwork(NetworkId.of(id));
+        adminService.removeVtnNetwork(NetworkId.of(id));
         return noContent().build();
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/rest/ServicePortWebResource.java b/src/main/java/org/opencord/cordvtn/rest/ServicePortWebResource.java
index 7b18e10..4300fb0 100644
--- a/src/main/java/org/opencord/cordvtn/rest/ServicePortWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/ServicePortWebResource.java
@@ -19,7 +19,7 @@
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onosproject.rest.AbstractWebResource;
-import org.opencord.cordvtn.api.CordVtnStore;
+import org.opencord.cordvtn.api.CordVtnAdminService;
 import org.opencord.cordvtn.api.PortId;
 import org.opencord.cordvtn.api.ServicePort;
 import org.slf4j.Logger;
@@ -29,6 +29,7 @@
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
@@ -39,7 +40,8 @@
 import javax.ws.rs.core.UriInfo;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
 
 import static com.fasterxml.jackson.databind.SerializationFeature.INDENT_OUTPUT;
 import static javax.ws.rs.core.Response.Status.NOT_FOUND;
@@ -55,10 +57,11 @@
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String MESSAGE = "Received service port ";
-    private static final String SERVICE_PORT = "ServicePort";
+    private static final String SERVICE_PORT  = "ServicePort";
     private static final String SERVICE_PORTS = "ServicePorts";
 
-    private final CordVtnStore service = DefaultServiceDirectory.getService(CordVtnStore.class);
+    private final CordVtnAdminService adminService =
+            DefaultServiceDirectory.getService(CordVtnAdminService.class);
 
     @Context
     private UriInfo uriInfo;
@@ -84,7 +87,7 @@
             }
 
             final ServicePort sport = codec(ServicePort.class).decode(portJson, this);
-            service.createServicePort(sport);
+            adminService.createVtnPort(sport);
 
             UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
                     .path(SERVICE_PORTS)
@@ -97,6 +100,39 @@
     }
 
     /**
+     * Updates the service port with the given identifier.
+     *
+     * @param id    port identifier
+     * @param input service port JSON stream
+     * @return 200 OK with a service port, 400 BAD_REQUEST if the requested
+     * port does not exist
+     */
+    @PUT
+    @Path("{id}")
+    @Consumes(MediaType.APPLICATION_JSON)
+    @Produces(MediaType.APPLICATION_JSON)
+    public Response updateServicePort(@PathParam("id") String id, InputStream input) {
+        try {
+            JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
+            log.trace(MESSAGE + "UPDATE " + mapper().writeValueAsString(jsonTree));
+
+            ObjectNode sportJson = (ObjectNode) jsonTree.get(SERVICE_PORT);
+            if (sportJson == null) {
+                throw new IllegalArgumentException();
+            }
+
+            final ServicePort sport = codec(ServicePort.class).decode(sportJson, this);
+            adminService.updateVtnPort(sport);
+
+            ObjectNode result = this.mapper().createObjectNode();
+            result.set(SERVICE_PORT, codec(ServicePort.class).encode(sport, this));
+            return ok(result).build();
+        } catch (IOException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    /**
      * Returns all service ports.
      *
      * @return 200 OK with set of service ports
@@ -105,9 +141,9 @@
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response getServicePorts() {
-        log.debug(MESSAGE + "GET");
+        log.trace(MESSAGE + "GET");
 
-        Set<ServicePort> sports = service.getServicePorts();
+        List<ServicePort> sports = new ArrayList<>(adminService.getVtnPorts());
         return ok(encodeArray(ServicePort.class, SERVICE_PORTS, sports)).build();
     }
 
@@ -123,15 +159,17 @@
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response getServicePort(@PathParam("id") String id) {
-        log.debug(MESSAGE + "GET " + id);
+        log.trace(MESSAGE + "GET " + id);
 
-        ServicePort sport = service.getServicePort(PortId.of(id));
+        ServicePort sport = adminService.getVtnPort(PortId.of(id));
         if (sport == null) {
+            log.trace("Returned NOT_FOUND");
             return status(NOT_FOUND).build();
         }
 
         ObjectNode result = this.mapper().createObjectNode();
         result.set(SERVICE_PORT, codec(ServicePort.class).encode(sport, this));
+        log.trace("Returned OK {}", result);
         return ok(result).build();
     }
 
@@ -146,9 +184,9 @@
     @Consumes(MediaType.APPLICATION_JSON)
     @Produces(MediaType.APPLICATION_JSON)
     public Response deleteServicePort(@PathParam("id") String id) {
-        log.debug(MESSAGE + "DELETE " + id);
+        log.trace(MESSAGE + "DELETE " + id);
 
-        service.removeServicePort(PortId.of(id));
+        adminService.removeVtnPort(PortId.of(id));
         return noContent().build();
     }
 }