CORD-628 Refactored VTN service network and port API

- Removed direct use of Neutron data model and Neutron API
- Extended service network and service port API to have all network
  information required for VTN
- Removed unnecessary dependency manager and store
- Removed network state sync method with Neutron and XOS
- Removed Neutron and XOS access information from the network config
- Re-organized API packages

Change-Id: I18f49ec733309315f683dfb2e6be6526056118f1
diff --git a/src/main/java/org/opencord/cordvtn/api/config/CordVtnConfig.java b/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
similarity index 81%
rename from src/main/java/org/opencord/cordvtn/api/config/CordVtnConfig.java
rename to src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
index 7f5a102..aa90093 100644
--- a/src/main/java/org/opencord/cordvtn/api/config/CordVtnConfig.java
+++ b/src/main/java/org/opencord/cordvtn/api/CordVtnConfig.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.api.config;
+package org.opencord.cordvtn.api;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
@@ -31,8 +31,8 @@
 import org.onosproject.net.behaviour.ControllerInfo;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.InvalidFieldException;
+import org.opencord.cordvtn.api.net.CidrAddr;
 import org.opencord.cordvtn.api.node.CordVtnNode;
-import org.opencord.cordvtn.api.node.NetworkAddress;
 import org.opencord.cordvtn.api.node.SshAccessInfo;
 import org.slf4j.Logger;
 
@@ -74,12 +74,10 @@
     private static final String SSH_USER = "sshUser";
     private static final String SSH_KEY_FILE = "sshKeyFile";
 
+    @Deprecated
     private static final String OPENSTACK = "openstack";
+    @Deprecated
     private static final String XOS = "xos";
-    private static final String ENDPOINT = "endpoint";
-    private static final String TENANT = "tenant";
-    private static final String USER = "user";
-    private static final String PASSWORD = "password";
 
     private static final String CONTROLLERS = "controllers";
     private static final int INDEX_IP = 0;
@@ -108,23 +106,23 @@
         }
 
         // check all mandatory fields are present and valid
-        result &= isMacAddress(PRIVATE_GATEWAY_MAC, MANDATORY);
-        result &= isIpPrefix(LOCAL_MANAGEMENT_IP, MANDATORY);
+        result = result && isMacAddress(PRIVATE_GATEWAY_MAC, MANDATORY);
+        result = result && isIpPrefix(LOCAL_MANAGEMENT_IP, MANDATORY);
 
         for (JsonNode node : object.get(CORDVTN_NODES)) {
             ObjectNode vtnNode = (ObjectNode) node;
-            result &= hasFields(
+            result = result && hasFields(
                     vtnNode,
                     HOSTNAME,
                     HOST_MANAGEMENT_IP,
                     DATA_IP,
                     DATA_IFACE,
                     INTEGRATION_BRIDGE_ID);
-            result &= isIpPrefix(vtnNode, HOST_MANAGEMENT_IP, MANDATORY);
-            result &= isIpPrefix(vtnNode, DATA_IP, MANDATORY);
+            result = result && isIpPrefix(vtnNode, HOST_MANAGEMENT_IP, MANDATORY);
+            result = result && isIpPrefix(vtnNode, DATA_IP, MANDATORY);
 
-            NetworkAddress localMgmt = NetworkAddress.valueOf(get(LOCAL_MANAGEMENT_IP, ""));
-            NetworkAddress hostsMgmt = NetworkAddress.valueOf(getConfig(vtnNode, HOST_MANAGEMENT_IP));
+            CidrAddr localMgmt = CidrAddr.valueOf(get(LOCAL_MANAGEMENT_IP, ""));
+            CidrAddr hostsMgmt = CidrAddr.valueOf(getConfig(vtnNode, HOST_MANAGEMENT_IP));
             if (hostsMgmt.prefix().contains(localMgmt.prefix()) ||
                     localMgmt.prefix().contains(hostsMgmt.prefix())) {
                 final String msg = "Host and local management network IP conflict";
@@ -132,43 +130,30 @@
             }
         }
 
-        result &= hasFields(
+        result = result && hasFields(
                 (ObjectNode) object.get(SSH),
                 SSH_PORT,
                 SSH_USER,
                 SSH_KEY_FILE);
-        result &= isTpPort(
+        result = result && isTpPort(
                 (ObjectNode) object.get(SSH),
                 SSH_PORT,
                 MANDATORY);
 
-        result &= hasFields(
-                (ObjectNode) object.get(OPENSTACK),
-                ENDPOINT,
-                TENANT,
-                USER,
-                PASSWORD);
-
-        result &= hasFields(
-                (ObjectNode) object.get(XOS),
-                ENDPOINT,
-                USER,
-                PASSWORD);
-
         // check all optional fields are valid
-        result &= isTpPort(OVSDB_PORT, OPTIONAL);
+        result = result && isTpPort(OVSDB_PORT, OPTIONAL);
 
         if (object.get(PUBLIC_GATEWAYS) != null && object.get(PUBLIC_GATEWAYS).isArray()) {
             for (JsonNode node : object.get(PUBLIC_GATEWAYS)) {
                 ObjectNode gateway = (ObjectNode) node;
-                result &= isIpAddress(gateway, GATEWAY_IP, MANDATORY);
-                result &= isMacAddress(gateway, GATEWAY_MAC, MANDATORY);
+                result = result && isIpAddress(gateway, GATEWAY_IP, MANDATORY);
+                result = result && isMacAddress(gateway, GATEWAY_MAC, MANDATORY);
             }
         }
 
         if (object.get(CONTROLLERS) != null) {
             for (JsonNode jsonNode : object.get(CONTROLLERS)) {
-                result &= isController(jsonNode);
+                result = result && isController(jsonNode);
             }
         }
         return result;
@@ -211,8 +196,8 @@
         String ovsdbPort = getConfig(object, OVSDB_PORT);
 
         object.get(CORDVTN_NODES).forEach(vtnNode -> {
-            NetworkAddress localMgmt = NetworkAddress.valueOf(get(LOCAL_MANAGEMENT_IP, ""));
-            NetworkAddress hostsMgmt = NetworkAddress.valueOf(getConfig(vtnNode, HOST_MANAGEMENT_IP));
+            CidrAddr localMgmt = CidrAddr.valueOf(get(LOCAL_MANAGEMENT_IP, ""));
+            CidrAddr hostsMgmt = CidrAddr.valueOf(getConfig(vtnNode, HOST_MANAGEMENT_IP));
 
             SshAccessInfo sshInfo = new SshAccessInfo(
                     hostsMgmt.ip().getIp4Address(),
@@ -286,31 +271,6 @@
     }
 
     /**
-     * Returns XOS API endpoint and credential configuration.
-     *
-     * @return xos api configuration
-     */
-    public XosConfig xosConfig() {
-        JsonNode jsonNode = object.get(XOS);
-        return new XosConfig(getConfig(jsonNode, ENDPOINT),
-                             getConfig(jsonNode, USER),
-                             getConfig(jsonNode, PASSWORD));
-    }
-
-    /**
-     * Returns OpenStack API endpoint and credential configuration.
-     *
-     * @return openstack api configuration
-     */
-    public OpenStackConfig openStackConfig() {
-        JsonNode jsonNode = object.get(OPENSTACK);
-        return new OpenStackConfig(jsonNode.path(ENDPOINT).asText(),
-                                   jsonNode.path(TENANT).asText(),
-                                   jsonNode.path(USER).asText(),
-                                   jsonNode.path(PASSWORD).asText());
-    }
-
-    /**
      * Returns controllers for the integration bridge.
      * It returns the information taken from cluster service with the default OF
      * port if no controller is specified in the network config.
diff --git a/src/main/java/org/opencord/cordvtn/api/config/AbstractApiConfig.java b/src/main/java/org/opencord/cordvtn/api/config/AbstractApiConfig.java
deleted file mode 100644
index ffac44f..0000000
--- a/src/main/java/org/opencord/cordvtn/api/config/AbstractApiConfig.java
+++ /dev/null
@@ -1,100 +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.api.config;
-
-import com.google.common.base.MoreObjects;
-
-import java.util.Objects;
-
-/**
- * Representation of external API access configuration.
- */
-public abstract class AbstractApiConfig {
-
-    protected final String endpoint;
-    protected final String user;
-    protected final String password;
-
-    /**
-     * Default constructor.
-     *
-     * @param endpoint api endpoint
-     * @param user     user name
-     * @param password password of the user
-     */
-    protected AbstractApiConfig(String endpoint, String user, String password) {
-        this.endpoint = endpoint;
-        this.user = user;
-        this.password = password;
-    }
-
-    /**
-     * Returns the endpoint.
-     *
-     * @return endpoint
-     */
-    public String endpoint() {
-        return endpoint;
-    }
-
-    /**
-     * Returns the user.
-     *
-     * @return user
-     */
-    public String user() {
-        return user;
-    }
-
-    /**
-     * Returns the password.
-     *
-     * @return password
-     */
-    public String password() {
-        return password;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(endpoint, user, password);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if ((obj instanceof AbstractApiConfig)) {
-            AbstractApiConfig that = (AbstractApiConfig) obj;
-            if (Objects.equals(endpoint, that.endpoint) &&
-                    Objects.equals(user, that.user) &&
-                    Objects.equals(password, that.password)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("endpoint", endpoint)
-                .add("user", user)
-                .add("password", password)
-                .toString();
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/api/config/OpenStackConfig.java b/src/main/java/org/opencord/cordvtn/api/config/OpenStackConfig.java
deleted file mode 100644
index 913b5e8..0000000
--- a/src/main/java/org/opencord/cordvtn/api/config/OpenStackConfig.java
+++ /dev/null
@@ -1,83 +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.api.config;
-
-import com.google.common.base.MoreObjects;
-
-import java.util.Objects;
-
-/**
- * Representation of OpenStack API access configuration.
- */
-public final class OpenStackConfig extends AbstractApiConfig {
-
-    private final String tenant;
-
-    /**
-     * Default constructor.
-     *
-     * @param endpoint api endpoint
-     * @param tenant   tenant name
-     * @param user     user name
-     * @param password password of the user
-     */
-    public OpenStackConfig(String endpoint, String tenant, String user,
-                           String password) {
-        super(endpoint, user, password);
-        this.tenant = tenant;
-    }
-
-    /**
-     * Returns the tenant name.
-     *
-     * @return tenant name
-     */
-    public String tenant() {
-        return tenant;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(endpoint, tenant, user, password);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if ((obj instanceof OpenStackConfig)) {
-            OpenStackConfig that = (OpenStackConfig) obj;
-            if (Objects.equals(endpoint, that.endpoint) &&
-                    Objects.equals(tenant, that.tenant) &&
-                    Objects.equals(user, that.user) &&
-                    Objects.equals(password, that.password)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("endpoint", endpoint)
-                .add("tenant", tenant)
-                .add("user", user)
-                .add("password", password)
-                .toString();
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/api/config/XosConfig.java b/src/main/java/org/opencord/cordvtn/api/config/XosConfig.java
deleted file mode 100644
index b2387a6..0000000
--- a/src/main/java/org/opencord/cordvtn/api/config/XosConfig.java
+++ /dev/null
@@ -1,33 +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.api.config;
-
-/**
- * Representation of XOS API access configuration.
- */
-public final class XosConfig extends AbstractApiConfig {
-
-    /**
-     * Default constructor.
-     *
-     * @param endpoint api endpoint
-     * @param user     user name
-     * @param password password of the user
-     */
-    public XosConfig(String endpoint, String user, String password) {
-        super(endpoint, user, password);
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/api/config/package-info.java b/src/main/java/org/opencord/cordvtn/api/config/package-info.java
deleted file mode 100644
index b4fa066..0000000
--- a/src/main/java/org/opencord/cordvtn/api/config/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * CORD VTN configuration API definitions.
- */
-package org.opencord.cordvtn.api.config;
\ No newline at end of file
diff --git a/src/main/java/org/opencord/cordvtn/api/core/CordVtnAdminService.java b/src/main/java/org/opencord/cordvtn/api/core/CordVtnAdminService.java
deleted file mode 100644
index 2411628..0000000
--- a/src/main/java/org/opencord/cordvtn/api/core/CordVtnAdminService.java
+++ /dev/null
@@ -1,150 +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.api.core;
-
-import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.NetworkService;
-import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.ServiceNetwork;
-import org.opencord.cordvtn.api.net.ServiceNetworkService;
-import org.opencord.cordvtn.api.net.ServicePort;
-import org.opencord.cordvtn.api.net.SubnetId;
-import org.openstack4j.model.network.Network;
-import org.openstack4j.model.network.Port;
-import org.openstack4j.model.network.Subnet;
-
-/**
- * Service for administering the inventory of {@link Network} and
- * {@link ServiceNetwork}.
- */
-public interface CordVtnAdminService extends CordVtnService, NetworkService,
-        ServiceNetworkService {
-
-    /**
-     * Purges internal network states.
-     */
-    void purgeStates();
-
-    /**
-     * Synchronizes internal network states with external services.
-     */
-    void syncStates();
-
-    /**
-     * Creates a service port with the given information.
-     *
-     * @param servicePort the new service port
-     */
-    void createServicePort(ServicePort servicePort);
-
-    /**
-     * Updates a service port with the given information.
-     *
-     * @param servicePort the updated service port
-     */
-    void updateServicePort(ServicePort servicePort);
-
-    /**
-     * Removes a service port with the given port id.
-     *
-     * @param portId port id
-     */
-    void removeServicePort(PortId portId);
-
-    /**
-     * Creates a service network with the given information.
-     *
-     * @param serviceNet the new service network
-     */
-    void createServiceNetwork(ServiceNetwork serviceNet);
-
-    /**
-     * Updates a service network with the given information.
-     *
-     * @param serviceNet the updated service network
-     */
-    void updateServiceNetwork(ServiceNetwork serviceNet);
-
-    /**
-     * Removes a service network with the given network id.
-     *
-     * @param netId network id
-     */
-    void removeServiceNetwork(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/core/CordVtnService.java b/src/main/java/org/opencord/cordvtn/api/core/CordVtnService.java
deleted file mode 100644
index 6f5e55e..0000000
--- a/src/main/java/org/opencord/cordvtn/api/core/CordVtnService.java
+++ /dev/null
@@ -1,72 +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.api.core;
-
-import org.onosproject.event.ListenerService;
-import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-import org.opencord.cordvtn.api.net.VtnNetworkEvent;
-import org.opencord.cordvtn.api.net.VtnNetworkListener;
-import org.opencord.cordvtn.api.net.VtnPort;
-
-import java.util.Set;
-
-/**
- * Service for interacting with the inventory of {@link VtnNetwork} and
- * {@link VtnPort}.
- */
-public interface CordVtnService
-        extends ListenerService<VtnNetworkEvent, VtnNetworkListener> {
-
-    /**
-     * Returns the VTN port with the given port id.
-     *
-     * @param portId port id
-     * @return service port
-     */
-    VtnPort vtnPort(PortId portId);
-
-    /**
-     * Returns the VTN port with the given port name.
-     *
-     * @param portName port name
-     * @return vtn port
-     */
-    VtnPort vtnPort(String portName);
-
-    /**
-     * Returns all VTN ports.
-     *
-     * @return set of service ports
-     */
-    Set<VtnPort> vtnPorts();
-
-    /**
-     * Returns the VTN network with the given network id.
-     *
-     * @param netId network id
-     * @return service network
-     */
-    VtnNetwork vtnNetwork(NetworkId netId);
-
-    /**
-     * Returns all VTN networks.
-     *
-     * @return set of service networks
-     */
-    Set<VtnNetwork> vtnNetworks();
-}
diff --git a/src/main/java/org/opencord/cordvtn/api/core/CordVtnStore.java b/src/main/java/org/opencord/cordvtn/api/core/CordVtnStore.java
deleted file mode 100644
index 78a60a4..0000000
--- a/src/main/java/org/opencord/cordvtn/api/core/CordVtnStore.java
+++ /dev/null
@@ -1,220 +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.api.core;
-
-import org.onosproject.store.Store;
-import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.SubnetId;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-import org.opencord.cordvtn.api.net.VtnNetworkEvent;
-import org.opencord.cordvtn.api.net.VtnPort;
-import org.openstack4j.model.network.Network;
-import org.openstack4j.model.network.Port;
-import org.openstack4j.model.network.Subnet;
-
-import java.util.Set;
-
-/**
- * Manages inventory of virtual and vtn networks; not intended for direct use.
- */
-public interface CordVtnStore extends Store<VtnNetworkEvent, CordVtnStoreDelegate> {
-
-    /**
-     * Purges vtn store.
-     */
-    void clear();
-
-    /**
-     * Creates vtn network.
-     *
-     * @param vtnNet vtn network
-     */
-    void createVtnNetwork(VtnNetwork vtnNet);
-
-    /**
-     * Updates the vtn network.
-     *
-     * @param vtnNet vtn network
-     */
-    void updateVtnNetwork(VtnNetwork vtnNet);
-
-    /**
-     * Returns the vtn network with the given network id.
-     *
-     * @param netId network id
-     * @return vtn network
-     */
-    VtnNetwork vtnNetwork(NetworkId netId);
-
-    /**
-     * Returns all vtn networks.
-     *
-     * @return set of vtn networks
-     */
-    Set<VtnNetwork> vtnNetworks();
-
-    /**
-     * Removes the vtn network with the given network id.
-     *
-     * @param netId network id
-     */
-    void removeVtnNetwork(NetworkId netId);
-
-    /**
-     * Creates vtn port.
-     *
-     * @param vtnPort the new vtn port
-     */
-    void createVtnPort(VtnPort vtnPort);
-
-    /**
-     * Updates the vtn port.
-     *
-     * @param vtnPort vtn port
-     */
-    void updateVtnPort(VtnPort vtnPort);
-
-    /**
-     * Returns the vtn port with the given port id.
-     *
-     * @param portId port id
-     * @return vtn port
-     */
-    VtnPort vtnPort(PortId portId);
-
-    /**
-     * Returns all vtn ports.
-     *
-     * @return set of vtn ports
-     */
-    Set<VtnPort> vtnPorts();
-
-    /**
-     * Removes vtn port.
-     *
-     * @param portId port id
-     */
-    void removeVtnPort(PortId portId);
-
-    /**
-     * 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 network(NetworkId netId);
-
-    /**
-     * Returns all networks.
-     *
-     * @return set of networks
-     */
-    Set<Network> networks();
-
-    /**
-     * 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 port(PortId portId);
-
-    /**
-     * Returns all ports.
-     *
-     * @return set of ports
-     */
-    Set<Port> ports();
-
-    /**
-     * 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 subnet(SubnetId subnetId);
-
-    /**
-     * Returns all subnets.
-     *
-     * @return set of subnets
-     */
-    Set<Subnet> subnets();
-
-    /**
-     * 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/instance/Instance.java b/src/main/java/org/opencord/cordvtn/api/core/Instance.java
similarity index 95%
rename from src/main/java/org/opencord/cordvtn/api/instance/Instance.java
rename to src/main/java/org/opencord/cordvtn/api/core/Instance.java
index 79e478f..24c81b5 100644
--- a/src/main/java/org/opencord/cordvtn/api/instance/Instance.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/Instance.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.api.instance;
+package org.opencord.cordvtn.api.core;
 
 import com.google.common.base.Strings;
 import org.onlab.packet.Ip4Address;
@@ -23,7 +23,7 @@
 import org.onosproject.net.PortNumber;
 import org.opencord.cordvtn.api.net.NetworkId;
 import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType;
+import org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
@@ -91,9 +91,9 @@
      *
      * @return network type
      */
-    public ServiceNetworkType netType() {
+    public NetworkType netType() {
         String netType = host.annotations().value(NETWORK_TYPE);
-        return ServiceNetworkType.valueOf(netType);
+        return NetworkType.valueOf(netType);
     }
 
     /**
diff --git a/src/main/java/org/opencord/cordvtn/api/instance/InstanceHandler.java b/src/main/java/org/opencord/cordvtn/api/core/InstanceHandler.java
similarity index 96%
rename from src/main/java/org/opencord/cordvtn/api/instance/InstanceHandler.java
rename to src/main/java/org/opencord/cordvtn/api/core/InstanceHandler.java
index 6e1cdc9..bc06992 100644
--- a/src/main/java/org/opencord/cordvtn/api/instance/InstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/InstanceHandler.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.api.instance;
+package org.opencord.cordvtn.api.core;
 
 /**
  * Handles service instance detection and removal.
diff --git a/src/main/java/org/opencord/cordvtn/api/instance/InstanceService.java b/src/main/java/org/opencord/cordvtn/api/core/InstanceService.java
similarity index 96%
rename from src/main/java/org/opencord/cordvtn/api/instance/InstanceService.java
rename to src/main/java/org/opencord/cordvtn/api/core/InstanceService.java
index 4d3ccca..c8eb5ef 100644
--- a/src/main/java/org/opencord/cordvtn/api/instance/InstanceService.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/InstanceService.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.api.instance;
+package org.opencord.cordvtn.api.core;
 
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.HostId;
@@ -24,6 +24,8 @@
  */
 public interface InstanceService {
 
+    // TODO add get instance
+
     /**
      * Adds a service instance on a given connect point. Or updates if the
      * instance already exists.
diff --git a/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkAdminService.java b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkAdminService.java
new file mode 100644
index 0000000..55edc53
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkAdminService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.api.core;
+
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
+
+/**
+ * Service for administering the inventory of {@link ServiceNetwork}.
+ */
+public interface ServiceNetworkAdminService extends ServiceNetworkService {
+
+    /**
+     * Purges internal network states.
+     */
+    void purgeStates();
+
+    /**
+     * Creates a service network with the given information.
+     *
+     * @param serviceNetwork the new service network
+     */
+    void createServiceNetwork(ServiceNetwork serviceNetwork);
+
+    /**
+     * Updates a service network with the given information.
+     *
+     * @param serviceNetwork the updated service network
+     */
+    void updateServiceNetwork(ServiceNetwork serviceNetwork);
+
+    /**
+     * Removes a service network with the given network id.
+     *
+     * @param networkId network id
+     */
+    void removeServiceNetwork(NetworkId networkId);
+
+    /**
+     * Creates a service port with the given information.
+     *
+     * @param servicePort the new service port
+     */
+    void createServicePort(ServicePort servicePort);
+
+    /**
+     * Updates a service port with the given information.
+     *
+     * @param servicePort the updated service port
+     */
+    void updateServicePort(ServicePort servicePort);
+
+    /**
+     * Removes a service port with the given port id.
+     *
+     * @param portId port id
+     */
+    void removeServicePort(PortId portId);
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkEvent.java b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkEvent.java
new file mode 100644
index 0000000..5392ba0
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkEvent.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.api.core;
+
+import org.joda.time.LocalDateTime;
+import org.onosproject.event.AbstractEvent;
+import org.opencord.cordvtn.api.net.Provider;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Describes service network event.
+ */
+public class ServiceNetworkEvent extends AbstractEvent<ServiceNetworkEvent.Type, ServiceNetwork> {
+
+    private final ServicePort servicePort;
+    private final Provider provider;
+
+    /**
+     * Type of service network event.
+     */
+    public enum Type {
+        /**
+         * Signifies that a new service network has been created.
+         */
+        SERVICE_NETWORK_CREATED,
+
+        /**
+         * Signifies that some service network attributes have changed.
+         */
+        SERVICE_NETWORK_UPDATED,
+
+        /**
+         * Signifies that provider network was added.
+         */
+        SERVICE_NETWORK_PROVIDER_ADDED,
+
+        /**
+         * Signifies that provider network was removed.
+         */
+        SERVICE_NETWORK_PROVIDER_REMOVED,
+
+        /**
+         * Signifies that a service network has been removed.
+         */
+        SERVICE_NETWORK_REMOVED,
+
+        /**
+         * Signifies that a new service port has been created.
+         */
+        SERVICE_PORT_CREATED,
+
+        /**
+         * Signifies that some service port attributes have changed.
+         */
+        SERVICE_PORT_UPDATED,
+
+        /**
+         * Signifies that a service port has been removed.
+         */
+        SERVICE_PORT_REMOVED
+    }
+
+    /**
+     * Creates an event of a given type and for the specified service network and
+     * the current time.
+     *
+     * @param type           service network event type
+     * @param serviceNetwork service network subject
+     */
+    public ServiceNetworkEvent(Type type, ServiceNetwork serviceNetwork) {
+        super(type, serviceNetwork);
+        this.servicePort = null;
+        this.provider = null;
+    }
+
+    /**
+     * Creates an event of a given type and for the specified service network,
+     * port and the current time.
+     *
+     * @param type            service network event type
+     * @param serviceNetwork  service network subject
+     * @param servicePort     optional service port subject
+     */
+    public ServiceNetworkEvent(Type type, ServiceNetwork serviceNetwork, ServicePort servicePort) {
+        super(type, serviceNetwork);
+        this.servicePort = servicePort;
+        this.provider = null;
+    }
+
+    /**
+     * Creates an event of a given type and for the specified service network,
+     * provider, dependency type with the provider, and the current time.
+     *
+     * @param type           service network event type
+     * @param serviceNetwork service network subject
+     * @param provider       optional provider network
+     */
+    public ServiceNetworkEvent(Type type, ServiceNetwork serviceNetwork, Provider provider) {
+        super(type, serviceNetwork);
+        this.servicePort = null;
+        this.provider = provider;
+    }
+
+    /**
+     * Returns the service port subject.
+     * It returns valid value only with the service port events.
+     *
+     * @return service port; null if the event is not service port specific
+     */
+    public ServicePort servicePort() {
+        return servicePort;
+    }
+
+    /**
+     * Returns the provider of the service network.
+     *
+     * @return provider network; null if the event is not provider specific
+     */
+    public Provider provider() {
+        return provider;
+    }
+
+    @Override
+    public String toString() {
+        if (servicePort == null) {
+            return super.toString();
+        }
+        return toStringHelper(this)
+                .add("time", new LocalDateTime(time()))
+                .add("type", type())
+                .add("serviceNetwork", subject())
+                .add("servicePort", servicePort)
+                .add("provider", provider)
+                .toString();
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/net/VtnNetworkListener.java b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkListener.java
similarity index 80%
rename from src/main/java/org/opencord/cordvtn/api/net/VtnNetworkListener.java
rename to src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkListener.java
index 62cf834..dc8782c 100644
--- a/src/main/java/org/opencord/cordvtn/api/net/VtnNetworkListener.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkListener.java
@@ -13,12 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.api.net;
+package org.opencord.cordvtn.api.core;
 
 import org.onosproject.event.EventListener;
 
 /**
- * Listener for vtn network event.
+ * Listener for service network event.
  */
-public interface VtnNetworkListener extends EventListener<VtnNetworkEvent> {
+public interface ServiceNetworkListener extends EventListener<ServiceNetworkEvent> {
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/net/ServiceNetworkService.java b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkService.java
similarity index 66%
rename from src/main/java/org/opencord/cordvtn/api/net/ServiceNetworkService.java
rename to src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkService.java
index 173f723..4217df0 100644
--- a/src/main/java/org/opencord/cordvtn/api/net/ServiceNetworkService.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkService.java
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.api.net;
+package org.opencord.cordvtn.api.core;
+
+import org.onosproject.event.ListenerService;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
 
 import java.util.Set;
 
@@ -21,15 +27,16 @@
  * Service for interacting with the inventory of {@link ServiceNetwork} and
  * {@link ServicePort}.
  */
-public interface ServiceNetworkService {
+public interface ServiceNetworkService
+        extends ListenerService<ServiceNetworkEvent, ServiceNetworkListener> {
 
     /**
      * Returns the service network with the supplied network ID.
      *
-     * @param netId network id
+     * @param networkId network id
      * @return service network
      */
-    ServiceNetwork serviceNetwork(NetworkId netId);
+    ServiceNetwork serviceNetwork(NetworkId networkId);
 
     /**
      * Returns all service networks registered in the service.
@@ -52,4 +59,11 @@
      * @return set of service ports
      */
     Set<ServicePort> servicePorts();
+
+    /**
+     * Returns all service ports associated with the supplied network.
+     * @param networkId network id
+     * @return set of service ports
+     */
+    Set<ServicePort> servicePorts(NetworkId networkId);
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkStore.java b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkStore.java
new file mode 100644
index 0000000..536a9c5
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkStore.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.api.core;
+
+import org.onosproject.store.Store;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
+
+import java.util.Set;
+
+/**
+ * Manages inventory of service networks; not intended for direct use.
+ */
+public interface ServiceNetworkStore extends Store<ServiceNetworkEvent, ServiceNetworkStoreDelegate> {
+
+    /**
+     * Purges the service network store.
+     */
+    void clear();
+
+    /**
+     * Creates new service network.
+     *
+     * @param serviceNetwork service network
+     */
+    void createServiceNetwork(ServiceNetwork serviceNetwork);
+
+    /**
+     * Updates the service network.
+     *
+     * @param serviceNetwork service network
+     */
+    void updateServiceNetwork(ServiceNetwork serviceNetwork);
+
+    /**
+     * Returns the service network with the given network id.
+     *
+     * @param networkId network id
+     * @return service network
+     */
+    ServiceNetwork serviceNetwork(NetworkId networkId);
+
+    /**
+     * Returns all service networks.
+     *
+     * @return set of service networks
+     */
+    Set<ServiceNetwork> serviceNetworks();
+
+    /**
+     * Removes the service network with the given network id.
+     *
+     * @param networkId network id
+     * @return service network removed; null if failed
+     */
+    ServiceNetwork removeServiceNetwork(NetworkId networkId);
+
+    /**
+     * Creates service port.
+     *
+     * @param servicePort the new service port
+     */
+    void createServicePort(ServicePort servicePort);
+
+    /**
+     * Updates the service port.
+     *
+     * @param servicePort service port
+     */
+    void updateServicePort(ServicePort servicePort);
+
+    /**
+     * Returns the service port with the given port id.
+     *
+     * @param portId port id
+     * @return service port
+     */
+    ServicePort servicePort(PortId portId);
+
+    /**
+     * Returns all service ports.
+     *
+     * @return set of service ports
+     */
+    Set<ServicePort> servicePorts();
+
+    /**
+     * Removes service port.
+     *
+     * @param portId port id
+     */
+    ServicePort removeServicePort(PortId portId);
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/core/CordVtnStoreDelegate.java b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkStoreDelegate.java
similarity index 84%
rename from src/main/java/org/opencord/cordvtn/api/core/CordVtnStoreDelegate.java
rename to src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkStoreDelegate.java
index d5d7b91..c1ff606 100644
--- a/src/main/java/org/opencord/cordvtn/api/core/CordVtnStoreDelegate.java
+++ b/src/main/java/org/opencord/cordvtn/api/core/ServiceNetworkStoreDelegate.java
@@ -16,10 +16,9 @@
 package org.opencord.cordvtn.api.core;
 
 import org.onosproject.store.StoreDelegate;
-import org.opencord.cordvtn.api.net.VtnNetworkEvent;
 
 /**
  * VTN store delegate abstraction.
  */
-public interface CordVtnStoreDelegate extends StoreDelegate<VtnNetworkEvent> {
+public interface ServiceNetworkStoreDelegate extends StoreDelegate<ServiceNetworkEvent> {
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/dependency/Dependency.java b/src/main/java/org/opencord/cordvtn/api/dependency/Dependency.java
deleted file mode 100644
index 775fb16..0000000
--- a/src/main/java/org/opencord/cordvtn/api/dependency/Dependency.java
+++ /dev/null
@@ -1,169 +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.api.dependency;
-
-import com.google.common.base.MoreObjects;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-
-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/dependency/DependencyService.java b/src/main/java/org/opencord/cordvtn/api/dependency/DependencyService.java
deleted file mode 100644
index 3323d66..0000000
--- a/src/main/java/org/opencord/cordvtn/api/dependency/DependencyService.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2015-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.dependency;
-
-import org.opencord.cordvtn.api.dependency.Dependency.Type;
-import org.opencord.cordvtn.api.net.NetworkId;
-
-/**
- * Provides dependency services.
- */
-public interface DependencyService {
-
-    /**
-     * Creates dependencies for a given tenant service.
-     *
-     * @param subscriber subscriber network id
-     * @param provider   provider network id
-     * @param type       bidirectional access type
-     */
-    void createDependency(NetworkId subscriber, NetworkId provider, Type type);
-
-    /**
-     * Removes all dependencies from a given tenant service.
-     *
-     * @param subscriber subscriber network id
-     * @param provider   provider network id
-     */
-    void removeDependency(NetworkId subscriber, NetworkId provider);
-}
diff --git a/src/main/java/org/opencord/cordvtn/api/dependency/package-info.java b/src/main/java/org/opencord/cordvtn/api/dependency/package-info.java
deleted file mode 100644
index 7e6589f..0000000
--- a/src/main/java/org/opencord/cordvtn/api/dependency/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * CORD VTN service dependency API definitions.
- */
-package org.opencord.cordvtn.api.dependency;
\ No newline at end of file
diff --git a/src/main/java/org/opencord/cordvtn/api/instance/package-info.java b/src/main/java/org/opencord/cordvtn/api/instance/package-info.java
deleted file mode 100644
index bdb3900..0000000
--- a/src/main/java/org/opencord/cordvtn/api/instance/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * CORD VTN service instance API definitions.
- */
-package org.opencord.cordvtn.api.instance;
\ No newline at end of file
diff --git a/src/main/java/org/opencord/cordvtn/api/node/NetworkAddress.java b/src/main/java/org/opencord/cordvtn/api/net/CidrAddr.java
similarity index 84%
rename from src/main/java/org/opencord/cordvtn/api/node/NetworkAddress.java
rename to src/main/java/org/opencord/cordvtn/api/net/CidrAddr.java
index 93bcb7a..5cd1d7e 100644
--- a/src/main/java/org/opencord/cordvtn/api/node/NetworkAddress.java
+++ b/src/main/java/org/opencord/cordvtn/api/net/CidrAddr.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.api.node;
+package org.opencord.cordvtn.api.net;
 
 import com.google.common.base.MoreObjects;
 import org.onlab.packet.IpAddress;
@@ -24,19 +24,19 @@
 import static com.google.common.base.Preconditions.checkArgument;
 
 /**
- * Representation of a network address, which consists of IP address and prefix.
+ * Representation of a network address with CIDR notation.
  */
-public final class NetworkAddress {
+public final class CidrAddr {
     private final IpAddress ip;
     private final IpPrefix prefix;
 
     /**
      * Constructor for a given IP address and prefix.
      *
-     * @param ip ip address
+     * @param ip     ip address
      * @param prefix ip prefix
      */
-    public NetworkAddress(IpAddress ip, IpPrefix prefix) {
+    public CidrAddr(IpAddress ip, IpPrefix prefix) {
         this.ip = ip;
         this.prefix = prefix;
     }
@@ -48,13 +48,13 @@
      * @return network address
      * @throws IllegalArgumentException if the cidr is not valid
      */
-    public static NetworkAddress valueOf(String cidr) {
+    public static CidrAddr valueOf(String cidr) {
         checkArgument(cidr.contains("/"));
 
         IpAddress ipAddress = IpAddress.valueOf(cidr.split("/")[0]);
         IpPrefix ipPrefix = IpPrefix.valueOf(cidr);
 
-        return new NetworkAddress(ipAddress, ipPrefix);
+        return new CidrAddr(ipAddress, ipPrefix);
     }
 
     /**
@@ -90,8 +90,8 @@
             return true;
         }
 
-        if (obj instanceof NetworkAddress) {
-            NetworkAddress that = (NetworkAddress) obj;
+        if (obj instanceof CidrAddr) {
+            CidrAddr that = (CidrAddr) obj;
             if (Objects.equals(ip, that.ip) && Objects.equals(prefix, that.prefix)) {
                 return true;
             }
diff --git a/src/main/java/org/opencord/cordvtn/api/net/NetworkService.java b/src/main/java/org/opencord/cordvtn/api/net/NetworkService.java
deleted file mode 100644
index ac505e2..0000000
--- a/src/main/java/org/opencord/cordvtn/api/net/NetworkService.java
+++ /dev/null
@@ -1,74 +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.api.net;
-
-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 {@link Network}, {@link Port},
- * and {@link Subnet}.
- */
-public interface NetworkService {
-
-    /**
-     * Returns the network with the supplied network ID.
-     *
-     * @param netId network id
-     * @return network
-     */
-    Network network(NetworkId netId);
-
-    /**
-     * Returns all networks registered in the service.
-     *
-     * @return set of networks
-     */
-    Set<Network> networks();
-
-    /**
-     * Returns the port with the supplied port ID.
-     *
-     * @param portId port id
-     * @return port
-     */
-    Port port(PortId portId);
-
-    /**
-     * Returns all ports registered in the service.
-     *
-     * @return set of ports
-     */
-    Set<Port> ports();
-
-    /**
-     * Returns the subnet with the supplied subnet ID.
-     *
-     * @param subnetId subnet id
-     * @return subnet
-     */
-    Subnet subnet(SubnetId subnetId);
-
-    /**
-     * Returns all subnets registered in the service.
-     *
-     * @return set of subnets
-     */
-    Set<Subnet> subnets();
-}
diff --git a/src/main/java/org/opencord/cordvtn/api/net/Provider.java b/src/main/java/org/opencord/cordvtn/api/net/Provider.java
new file mode 100644
index 0000000..e093137
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/api/net/Provider.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.api.net;
+
+import com.google.common.base.MoreObjects;
+import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Representation of a provider network.
+ */
+public final class Provider {
+
+    private final ServiceNetwork provider;
+    private final DependencyType type;
+
+    private Provider(ServiceNetwork provider, DependencyType type) {
+        this.provider = provider;
+        this.type = type;
+    }
+
+    /**
+     * Returns provider network.
+     *
+     * @return service network
+     */
+    public ServiceNetwork provider() {
+        return provider;
+    }
+
+    /**
+     * Returns direct access type between subscriber and provider networks.
+     *
+     * @return type
+     */
+    public DependencyType type() {
+        return type;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof Provider) {
+            Provider that = (Provider) obj;
+            if (Objects.equals(provider, that.provider) &&
+                    Objects.equals(type, that.type)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(provider, type);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("provider", provider.id())
+                .add("type", type)
+                .toString();
+    }
+
+    /**
+     * Returns new provider network builder instance.
+     *
+     * @return provider network
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of the provider network entities.
+     */
+    public static final class Builder {
+        private ServiceNetwork provider;
+        private DependencyType type;
+
+        private Builder() {
+        }
+
+        /**
+         * Builds an immutable provider network.
+         *
+         * @return provider network instance
+         */
+        public Provider build() {
+            checkNotNull(provider);
+            checkNotNull(type);
+
+            return new Provider(provider, type);
+        }
+
+        /**
+         * Returns provider network with the supplied provider.
+         *
+         * @param provider provider network
+         * @return provider network builder
+         */
+        public Builder provider(ServiceNetwork provider) {
+            this.provider = provider;
+            return this;
+        }
+
+        /**
+         * Returns provider network with the supplied type.
+         *
+         * @param type type
+         * @return provider network builder
+         */
+        public Builder type(DependencyType type) {
+            this.type = type;
+            return this;
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/api/net/ProviderNetwork.java b/src/main/java/org/opencord/cordvtn/api/net/ProviderNetwork.java
deleted file mode 100644
index 37fce8c..0000000
--- a/src/main/java/org/opencord/cordvtn/api/net/ProviderNetwork.java
+++ /dev/null
@@ -1,97 +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.api.net;
-
-import com.google.common.base.MoreObjects;
-import org.opencord.cordvtn.api.dependency.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/net/ServiceNetwork.java b/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
index 6b26daf..849a6ea 100644
--- a/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
+++ b/src/main/java/org/opencord/cordvtn/api/net/ServiceNetwork.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,25 +15,19 @@
  */
 package org.opencord.cordvtn.api.net;
 
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 
-import java.util.Objects;
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.BIDIRECTIONAL;
+import java.util.Comparator;
+import java.util.Map;
 
 /**
  * Representation of a service network which holds service specific information,
  * like service type or dependency, in addition to the common network.
  */
-public class ServiceNetwork {
+public interface 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 {
+    enum NetworkType {
         PRIVATE,
         PUBLIC,
         MANAGEMENT_HOST,
@@ -42,97 +36,129 @@
         ACCESS_AGENT
     }
 
-    protected final NetworkId id;
-    protected final ServiceNetworkType type;
-    protected final Set<ProviderNetwork> 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;
+    enum DependencyType {
+        BIDIRECTIONAL,
+        UNIDIRECTIONAL
     }
 
+    Comparator<ServiceNetwork> SERVICE_NETWORK_COMPARATOR =
+            (net1, net2) -> net1.id().id().compareTo(net2.id().id());
+
     /**
-     * Returns the network id of the service network.
+     * Returns the service network identifier.
      *
-     * @return network id
+     * @return service network identifier
      */
-    public NetworkId id() {
-        return id;
-    }
+    NetworkId id();
+
+    /**
+     * Returns the service network name.
+     *
+     * @return service network name.
+     */
+    String name();
 
     /**
      * Returns the type of the service network.
      *
-     * @return service network type
+     * @return service network type; empty value if type is not set
      */
-    public ServiceNetworkType type() {
-        return type;
-    }
+    NetworkType type();
 
     /**
-     * Returns the provider networks of this service network if exists.
+     * Returns the service network segmentation identifier.
      *
-     * @return provider networks
+     * @return segmentation id; empty value if segment id is not set
      */
-    public Set<ProviderNetwork> providers() {
-        return providers;
-    }
+    SegmentId segmentId();
 
     /**
-     * Returns if the given network is the provider of this network or not.
+     * Returns the subnet of the service network.
      *
-     * @param netId network id
-     * @return true if the given network is the provider of this network
+     * @return subnet ip prefix; empty value if subnet is not set
      */
-    public boolean isProvider(NetworkId netId) {
-        return providers.stream().filter(p -> Objects.equals(p.id(), netId))
-                .findAny().isPresent();
-    }
+    IpPrefix subnet();
 
     /**
-     * Returns if the given network is the provider of this network with
-     * bidirectional access type.
+     * Returns the service IP address of the service network.
      *
-     * @param netId network id
-     * @return true if the given network is a bidrectional provider
+     * @return service ip; empty value if service ip is not set
      */
-    public boolean isBidirectionalProvider(NetworkId netId) {
-        return providers.stream().filter(p -> Objects.equals(p.id(), netId))
-                .filter(p -> p.type() == BIDIRECTIONAL)
-                .findAny().isPresent();
-    }
+    IpAddress serviceIp();
 
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
+    /**
+     * Returns the providers of the service network.
+     *
+     * @return set of provider networks; empty map if no providers exist
+     */
+    Map<NetworkId, DependencyType> providers();
 
-        if (obj instanceof ServiceNetwork) {
-            ServiceNetwork that = (ServiceNetwork) obj;
-            if (Objects.equals(id, that.id) &&
-                    Objects.equals(type, that.type) &&
-                    Objects.equals(providers, that.providers)) {
-                return true;
-            }
-        }
-        return false;
-    }
+    /**
+     * Builder of new service network entities.
+     */
+    interface Builder {
 
-    @Override
-    public int hashCode() {
-        return Objects.hash(id, type, providers);
-    }
+        /**
+         * Builds an immutable service network instance.
+         *
+         * @return service network instance
+         */
+        ServiceNetwork build();
 
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("networkId", id)
-                .add("type", type)
-                .add("providers", providers)
-                .toString();
+        /**
+         * Returns service network builder with the supplied identifier.
+         *
+         * @param networkId network id
+         * @return service network builder
+         */
+        Builder id(NetworkId networkId);
+
+        /**
+         * Returns service network builder with the supplied name.
+         *
+         * @param name network name
+         * @return service network builder
+         */
+        Builder name(String name);
+
+        /**
+         * Returns service network builder with the supplied type.
+         *
+         * @param type service network type
+         * @return service network builder
+         */
+        Builder type(NetworkType type);
+
+        /**
+         * Returns service network builder with the supplied segmentation id.
+         *
+         * @param segmentId segmentation id
+         * @return service network builder
+         */
+        Builder segmentId(SegmentId segmentId);
+
+        /**
+         * Returns service network builder with the supplied subnet.
+         *
+         * @param subnet subnet
+         * @return service network builder
+         */
+        Builder subnet(IpPrefix subnet);
+
+        /**
+         * Returns service network builder with the supplied service IP address.
+         *
+         * @param serviceIp service ip address
+         * @return service network builder
+         */
+        Builder serviceIp(IpAddress serviceIp);
+
+        /**
+         * Returns service network builder with the supplied providers.
+         *
+         * @param providers set of provider network
+         * @return service network builder
+         */
+        Builder providers(Map<NetworkId, DependencyType> providers);
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/net/ServicePort.java b/src/main/java/org/opencord/cordvtn/api/net/ServicePort.java
index 99a79ff..83117ee 100644
--- a/src/main/java/org/opencord/cordvtn/api/net/ServicePort.java
+++ b/src/main/java/org/opencord/cordvtn/api/net/ServicePort.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,91 +15,137 @@
  */
 package org.opencord.cordvtn.api.net;
 
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 
-import java.util.Objects;
-import java.util.Optional;
+import java.util.Comparator;
 import java.util.Set;
 
-import static com.google.common.base.Preconditions.checkNotNull;
-
 /**
  * Representation of a service port which holds service specific port information,
  * like vlan tag or additional addresses, to the common network port.
  */
-public class ServicePort {
+public interface ServicePort {
 
-    private static final String ERR_ID = "Service port ID cannot be null";
-
-    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 == null ? ImmutableSet.of() : addressPairs;
-    }
+    Comparator<ServicePort> SERVICE_PORT_COMPARATOR =
+            (port1, port2) -> port1.networkId().id().compareTo(port2.networkId().id());
 
     /**
-     * Returns the port id of the service port.
+     * Returns the port identifier.
      *
      * @return port id
      */
-    public PortId id() {
-        return id;
-    }
+    PortId id();
 
     /**
-     * Returns the vlan id of the the service port if exists.
+     * Returns the port name.
+     *
+     * @return port name
+     */
+    String name();
+
+    /**
+     * Returns associated network identifier of the service port.
+     *
+     * @return network id
+     */
+    NetworkId networkId();
+
+    /**
+     * Returns the MAC address of the service port.
+     *
+     * @return mac address
+     */
+    MacAddress mac();
+
+    /**
+     * Returns the fixed IP address of the service port.
+     *
+     * @return ip address
+     */
+    IpAddress ip();
+
+    /**
+     * Returns VLAN of service the port.
      *
      * @return vlan id
      */
-    public Optional<VlanId> vlanId() {
-        return Optional.ofNullable(vlanId);
-    }
+    VlanId vlanId();
 
     /**
-     * Returns the additional address pairs used in this port.
+     * Returns additional floating address pairs of the service port.
      *
-     * @return set of ip and mac address pairs
+     * @return set of mac and ip address pair
      */
-    public Set<AddressPair> addressPairs() {
-        return addressPairs;
-    }
+    Set<AddressPair> addressPairs();
 
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
+    /**
+     * Builder of new service port entities.
+     */
+    interface Builder {
 
-        if (obj instanceof ServicePort) {
-            ServicePort that = (ServicePort) obj;
-            if (Objects.equals(id, that.id) &&
-                    Objects.equals(vlanId, that.vlanId) &&
-                    Objects.equals(addressPairs, that.addressPairs)) {
-                return true;
-            }
-        }
-        return false;
-    }
+        /**
+         * Builds an immutable service port instance.
+         *
+         * @return service port
+         */
+        ServicePort build();
 
-    @Override
-    public int hashCode() {
-        return Objects.hash(id, vlanId, addressPairs);
-    }
+        /**
+         * Returns service port builder with the supplied identifier.
+         *
+         * @param id port id
+         * @return service port builder
+         */
+        Builder id(PortId id);
 
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("portId", id)
-                .add("vlanId", vlanId)
-                .add("addressPairs", addressPairs)
-                .toString();
+        /**
+         * Returns service port builder with the supplied name.
+         *
+         * @param name port name
+         * @return service port builder
+         */
+        Builder name(String name);
+
+        /**
+         * Returns service port builder with the supplied network identifier.
+         *
+         * @param networkId network id
+         * @return service port builder
+         */
+        Builder networkId(NetworkId networkId);
+
+        /**
+         * Returns service port builder with the supplied MAC address.
+         *
+         * @param mac mac address
+         * @return service port builder
+         */
+        Builder mac(MacAddress mac);
+
+        /**
+         * Returns service port builder with the supplied IP address.
+         *
+         * @param ip ip address
+         * @return service port builder
+         */
+        Builder ip(IpAddress ip);
+
+        /**
+         * Returns service port builder with the supplied VLAN.
+         *
+         * @param vlanId vlan id
+         * @return service port builder
+         */
+        Builder vlanId(VlanId vlanId);
+
+        /**
+         * Returns service port builder with the supplied address pairs.
+         *
+         * @param addressPairs set of address pair
+         * @return service port builder
+         */
+        Builder addressPairs(Set<AddressPair> addressPairs);
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/api/net/SubnetId.java b/src/main/java/org/opencord/cordvtn/api/net/SubnetId.java
deleted file mode 100644
index b7c49bd..0000000
--- a/src/main/java/org/opencord/cordvtn/api/net/SubnetId.java
+++ /dev/null
@@ -1,43 +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.api.net;
-
-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/net/VtnNetwork.java b/src/main/java/org/opencord/cordvtn/api/net/VtnNetwork.java
deleted file mode 100644
index c4ae68e..0000000
--- a/src/main/java/org/opencord/cordvtn/api/net/VtnNetwork.java
+++ /dev/null
@@ -1,330 +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.api.net;
-
-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.opencord.cordvtn.api.dependency.Dependency;
-import org.openstack4j.model.network.Network;
-import org.openstack4j.model.network.Subnet;
-
-import java.util.Comparator;
-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.net.ServiceNetwork.ServiceNetworkType.PRIVATE;
-
-/**
- * Representation of a network holding the basic virtual network and additional
- * service network specific information.
- * All the services making use of CordVtnService are intended to interface
- * with this VtnNetwork, and not allowed to directly access {@link Network} or
- * {@link ServiceNetwork}.
- */
-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;
-    }
-
-    public static final Comparator<VtnNetwork> VTN_NETWORK_COMPARATOR =
-            (net1, net2) -> net1.serviceIp().compareTo(net2.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/net/VtnNetworkEvent.java b/src/main/java/org/opencord/cordvtn/api/net/VtnNetworkEvent.java
deleted file mode 100644
index c6f187f..0000000
--- a/src/main/java/org/opencord/cordvtn/api/net/VtnNetworkEvent.java
+++ /dev/null
@@ -1,112 +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.api.net;
-
-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/net/VtnPort.java b/src/main/java/org/opencord/cordvtn/api/net/VtnPort.java
deleted file mode 100644
index 1f07218..0000000
--- a/src/main/java/org/opencord/cordvtn/api/net/VtnPort.java
+++ /dev/null
@@ -1,304 +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.api.net;
-
-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.Comparator;
-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 a port holding the basic virtual port and additional service
- * port specific information.
- * All the services making use of CordVtnService are intended to interface
- * with this VtnPort, and not allowed to directly access {@link Port} or
- * {@link ServicePort}.
- */
-public final class VtnPort extends ServicePort {
-
-    private static final String ERR_IP_MISSING = "VTN port IP address 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;
-    }
-
-    public static final Comparator<VtnPort> VTN_PORT_COMPARATOR =
-            (port1, port2) -> port1.ip().compareTo(port2.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/api/node/CordVtnNode.java b/src/main/java/org/opencord/cordvtn/api/node/CordVtnNode.java
index d4339df..153eed3 100644
--- a/src/main/java/org/opencord/cordvtn/api/node/CordVtnNode.java
+++ b/src/main/java/org/opencord/cordvtn/api/node/CordVtnNode.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.Sets;
 import org.onlab.packet.TpPort;
 import org.onosproject.net.DeviceId;
+import org.opencord.cordvtn.api.net.CidrAddr;
 
 import java.util.Comparator;
 import java.util.Objects;
@@ -37,9 +38,9 @@
 public final class CordVtnNode {
 
     private final String hostname;
-    private final NetworkAddress hostMgmtIp;
-    private final NetworkAddress localMgmtIp;
-    private final NetworkAddress dataIp;
+    private final CidrAddr hostMgmtIp;
+    private final CidrAddr localMgmtIp;
+    private final CidrAddr dataIp;
     private final Optional<TpPort> ovsdbPort;
     private final SshAccessInfo sshInfo;
     private final DeviceId integrationBridgeId;
@@ -65,9 +66,9 @@
      * @param state cordvtn node state
      */
     private CordVtnNode(String hostname,
-                        NetworkAddress hostMgmtIp,
-                        NetworkAddress localMgmtIp,
-                        NetworkAddress dataIp,
+                        CidrAddr hostMgmtIp,
+                        CidrAddr localMgmtIp,
+                        CidrAddr dataIp,
                         Optional<TpPort> ovsdbPort,
                         SshAccessInfo sshInfo,
                         DeviceId integrationBridgeId,
@@ -117,7 +118,7 @@
      *
      * @return network address
      */
-    public NetworkAddress hostMgmtIp() {
+    public CidrAddr hostMgmtIp() {
         return this.hostMgmtIp;
     }
 
@@ -126,7 +127,7 @@
      *
      * @return network address
      */
-    public NetworkAddress localMgmtIp() {
+    public CidrAddr localMgmtIp() {
         return this.localMgmtIp;
     }
 
@@ -135,7 +136,7 @@
      *
      * @return network address
      */
-    public NetworkAddress dataIp() {
+    public CidrAddr dataIp() {
         return this.dataIp;
     }
 
@@ -286,9 +287,9 @@
      */
     public static final class Builder {
         private String hostname;
-        private NetworkAddress hostMgmtIp;
-        private NetworkAddress localMgmtIp;
-        private NetworkAddress dataIp;
+        private CidrAddr hostMgmtIp;
+        private CidrAddr localMgmtIp;
+        private CidrAddr dataIp;
         private Optional<TpPort> ovsdbPort =
                 Optional.of(TpPort.tpPort(DEFAULT_OVSDB_PORT));
         private SshAccessInfo sshInfo;
@@ -344,7 +345,7 @@
          * @param hostMgmtIp host management netework ip address
          * @return cordvtn node builder
          */
-        public Builder hostMgmtIp(NetworkAddress hostMgmtIp) {
+        public Builder hostMgmtIp(CidrAddr hostMgmtIp) {
             checkNotNull(hostMgmtIp);
             this.hostMgmtIp = hostMgmtIp;
             return this;
@@ -357,7 +358,7 @@
          * @return cordvtn node builder
          */
         public Builder hostMgmtIp(String cidr) {
-            this.hostMgmtIp = NetworkAddress.valueOf(cidr);
+            this.hostMgmtIp = CidrAddr.valueOf(cidr);
             return this;
         }
 
@@ -367,7 +368,7 @@
          * @param localMgmtIp local management network ip address
          * @return cordvtn node builder
          */
-        public Builder localMgmtIp(NetworkAddress localMgmtIp) {
+        public Builder localMgmtIp(CidrAddr localMgmtIp) {
             checkNotNull(localMgmtIp);
             this.localMgmtIp = localMgmtIp;
             return this;
@@ -380,7 +381,7 @@
          * @return cordvtn node builder
          */
         public Builder localMgmtIp(String cidr) {
-            this.localMgmtIp = NetworkAddress.valueOf(cidr);
+            this.localMgmtIp = CidrAddr.valueOf(cidr);
             return this;
         }
 
@@ -390,7 +391,7 @@
          * @param dataIp data network ip address
          * @return cordvtn node builder
          */
-        public Builder dataIp(NetworkAddress dataIp) {
+        public Builder dataIp(CidrAddr dataIp) {
             checkNotNull(dataIp);
             this.dataIp = dataIp;
             return this;
@@ -403,7 +404,7 @@
          * @return cordvtn node builder
          */
         public Builder dataIp(String cidr) {
-            this.dataIp = NetworkAddress.valueOf(cidr);
+            this.dataIp = CidrAddr.valueOf(cidr);
             return this;
         }
 
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnNetworkListCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnNetworkListCommand.java
index fac50f8..7f42dc2 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnNetworkListCommand.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnNetworkListCommand.java
@@ -21,8 +21,8 @@
 import com.google.common.collect.Lists;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.opencord.cordvtn.api.core.CordVtnService;
-import org.opencord.cordvtn.api.net.VtnNetwork;
+import org.opencord.cordvtn.api.core.ServiceNetworkService;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
 
 import java.util.Collections;
 import java.util.List;
@@ -36,13 +36,13 @@
         description = "Lists all VTN networks")
 public class CordVtnNetworkListCommand extends AbstractShellCommand {
 
-    private static final String FORMAT = "%-40s%-20s%-8s%-20s%s";
+    private static final String FORMAT = "%-40s%-20s%-20s%-8s%-20s%s";
 
     @Override
     protected void execute() {
-        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
-        List<VtnNetwork> networks = Lists.newArrayList(service.vtnNetworks());
-        Collections.sort(networks, VtnNetwork.VTN_NETWORK_COMPARATOR);
+        ServiceNetworkService service = AbstractShellCommand.get(ServiceNetworkService.class);
+        List<ServiceNetwork> networks = Lists.newArrayList(service.serviceNetworks());
+        Collections.sort(networks, ServiceNetwork.SERVICE_NETWORK_COMPARATOR);
 
         if (outputJson()) {
             try {
@@ -51,9 +51,10 @@
                 print("Failed to list networks in JSON format");
             }
         } else {
-            print(FORMAT, "ID", "Type", "VNI", "Subnet", "Service IP");
-            for (VtnNetwork net: networks) {
+            print(FORMAT, "ID", "Name", "Type", "VNI", "Subnet", "Service IP");
+            for (ServiceNetwork net: networks) {
                 print(FORMAT, net.id(),
+                      net.name(),
                       net.type(),
                       net.segmentId(),
                       net.subnet(),
@@ -62,17 +63,18 @@
         }
     }
 
-    private JsonNode json(List<VtnNetwork> networks) {
+    private JsonNode json(List<ServiceNetwork> networks) {
         ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
-        for (VtnNetwork net: networks) {
+        for (ServiceNetwork net: networks) {
             ArrayNode providers = mapper().createArrayNode();
-            net.providers().forEach(provider -> providers.add(
+            net.providers().entrySet().forEach(provider -> providers.add(
                     mapper().createObjectNode()
-                            .put("networkId", provider.id().id())
-                            .put("type", provider.type().name())));
+                            .put("networkId", provider.getKey().id())
+                            .put("type", provider.getValue().name())));
 
             result.add(mapper().createObjectNode()
                                .put("id", net.id().id())
+                               .put("name", net.name())
                                .put("type", net.type().name())
                                .put("vni", net.segmentId().id())
                                .put("subnet", net.subnet().toString())
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnPortListCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnPortListCommand.java
index 32eaa22..6a7618d 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnPortListCommand.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnPortListCommand.java
@@ -23,8 +23,8 @@
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.opencord.cordvtn.api.core.CordVtnService;
-import org.opencord.cordvtn.api.net.VtnPort;
+import org.opencord.cordvtn.api.core.ServiceNetworkService;
+import org.opencord.cordvtn.api.net.ServicePort;
 
 import java.util.Collections;
 import java.util.List;
@@ -46,12 +46,12 @@
 
     @Override
     protected void execute() {
-        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+        ServiceNetworkService service = AbstractShellCommand.get(ServiceNetworkService.class);
 
-        List<VtnPort> ports = Lists.newArrayList(service.vtnPorts());
-        Collections.sort(ports, VtnPort.VTN_PORT_COMPARATOR);
+        List<ServicePort> ports = Lists.newArrayList(service.servicePorts());
+        Collections.sort(ports, ServicePort.SERVICE_PORT_COMPARATOR);
         if (!Strings.isNullOrEmpty(networkId)) {
-            ports.removeIf(port -> !port.netId().id().equals(networkId));
+            ports.removeIf(port -> !port.networkId().id().equals(networkId));
         }
 
         if (outputJson()) {
@@ -62,22 +62,22 @@
             }
         } else {
             print(FORMAT, "ID", "MAC", "IP", "VLAN", "WAN IPs");
-            for (VtnPort port: ports) {
+            for (ServicePort port: ports) {
                 List<String> floatingIps = port.addressPairs().stream()
                         .map(ip -> ip.ip().toString())
                         .collect(Collectors.toList());
                 print(FORMAT, port.id(),
                       port.mac(),
                       port.ip(),
-                      port.vlanId().isPresent() ? port.vlanId().get() : "",
+                      port.vlanId() != null ? port.vlanId() : "",
                       floatingIps.isEmpty() ? "" : floatingIps);
             }
         }
     }
 
-    private JsonNode json(List<VtnPort> ports) {
+    private JsonNode json(List<ServicePort> ports) {
         ArrayNode result = mapper().enable(INDENT_OUTPUT).createArrayNode();
-        for (VtnPort port: ports) {
+        for (ServicePort port: ports) {
             ArrayNode addrPairs = mapper().createArrayNode();
             port.addressPairs().forEach(pair -> addrPairs.add(
                     mapper().createObjectNode()
@@ -86,11 +86,11 @@
 
             result.add(mapper().createObjectNode()
                                .put("id", port.id().id())
-                               .put("networkId", port.netId().id())
+                               .put("networkId", port.networkId().id())
                                .put("mac", port.mac().toString())
                                .put("ip", port.ip().toString())
-                               .put("vlan", port.vlanId().isPresent() ?
-                                       port.vlanId().get().toString() : null)
+                               .put("vlan", port.vlanId() != null ?
+                                       port.vlanId().toString() : null)
                                .set("addressPairs", addrPairs));
         }
         return result;
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeStatesCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeStatesCommand.java
index b26ec66..a6f63b7 100644
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeStatesCommand.java
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnPurgeStatesCommand.java
@@ -17,7 +17,7 @@
 
 import org.apache.karaf.shell.commands.Command;
 import org.onosproject.cli.AbstractShellCommand;
-import org.opencord.cordvtn.api.core.CordVtnAdminService;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
 
 /**
  * Purges internal network states.
@@ -28,8 +28,8 @@
 
     @Override
     protected void execute() {
-        CordVtnAdminService adminService =
-                AbstractShellCommand.get(CordVtnAdminService.class);
+        ServiceNetworkAdminService adminService =
+                AbstractShellCommand.get(ServiceNetworkAdminService.class);
         adminService.purgeStates();
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncNeutronStatesCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncNeutronStatesCommand.java
new file mode 100644
index 0000000..c7dcdeb
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncNeutronStatesCommand.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.cli;
+
+import com.google.common.base.Strings;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.cli.AbstractShellCommand;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.SegmentId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
+import org.opencord.cordvtn.impl.DefaultServiceNetwork;
+import org.opencord.cordvtn.impl.DefaultServicePort;
+import org.openstack4j.api.OSClient;
+import org.openstack4j.api.exceptions.AuthenticationException;
+import org.openstack4j.model.identity.Access;
+import org.openstack4j.openstack.OSFactory;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Synchronizes network states with OpenStack Neutron.
+ * This command can be used to actively synchronize Neutron network with VTN
+ * service network.
+ */
+@Command(scope = "onos", name = "cordvtn-sync-neutron-states",
+        description = "Synchronizes network states with Neutron")
+public class CordVtnSyncNeutronStatesCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "endpoint", description = "OpenStack service endpoint",
+            required = true, multiValued = false)
+    private String endpoint = null;
+
+    @Argument(index = 1, name = "tenant", description = "OpenStack admin tenant name",
+            required = true, multiValued = false)
+    private String tenant = null;
+
+    @Argument(index = 2, name = "user", description = "OpenStack admin user name",
+            required = true, multiValued = false)
+    private String user = null;
+
+    @Argument(index = 3, name = "password", description = "OpenStack admin user password",
+            required = true, multiValued = false)
+    private String password = null;
+
+    private static final String NET_FORMAT = "%-40s%-20s%-20s%-8s%-20s%s";
+    private static final String PORT_FORMAT = "%-40s%-20s%-18s%-8s%s";
+
+    @Override
+    protected void execute() {
+        ServiceNetworkAdminService snetService =
+                AbstractShellCommand.get(ServiceNetworkAdminService.class);
+        Access osAccess;
+        try {
+            osAccess = OSFactory.builder()
+                    .endpoint(this.endpoint)
+                    .tenantName(this.tenant)
+                    .credentials(this.user, this.password)
+                    .authenticate()
+                    .getAccess();
+        } catch (AuthenticationException e) {
+            print("Authentication failed");
+            return;
+        } catch (Exception e) {
+            print("OpenStack service endpoint is unreachable");
+            return;
+        }
+
+        print("Synchronizing service networks...");
+        print(NET_FORMAT, "ID", "Name", "Type", "VNI", "Subnet", "Service IP");
+        OSClient osClient = OSFactory.clientFromAccess(osAccess);
+        osClient.networking().network().list().forEach(osNet -> {
+            ServiceNetwork snet = DefaultServiceNetwork.builder()
+                    .id(NetworkId.of(osNet.getId()))
+                    .name(osNet.getName())
+                    .type(ServiceNetwork.NetworkType.PRIVATE)
+                    .segmentId(SegmentId.of(Long.valueOf(osNet.getProviderSegID())))
+                    .build();
+            try {
+                if (snetService.serviceNetwork(snet.id()) != null) {
+                    snetService.updateServiceNetwork(snet);
+                } else {
+                    snetService.createServiceNetwork(snet);
+                }
+            } catch (Exception ignore) {
+            }
+        });
+
+        osClient.networking().subnet().list().forEach(osSubnet -> {
+            try {
+                ServiceNetwork snet = DefaultServiceNetwork.builder()
+                        .id(NetworkId.of(osSubnet.getNetworkId()))
+                        .subnet(IpPrefix.valueOf(osSubnet.getCidr()))
+                        .serviceIp(IpAddress.valueOf(osSubnet.getGateway()))
+                        .build();
+                snetService.updateServiceNetwork(snet);
+                ServiceNetwork updated = snetService.serviceNetwork(snet.id());
+                print(NET_FORMAT, updated.id(),
+                      updated.name(),
+                      updated.type(),
+                      updated.segmentId(),
+                      updated.subnet(),
+                      updated.serviceIp());
+            } catch (Exception e) {
+                print(e.getMessage());
+            }
+        });
+
+        print("\nSynchronizing service ports...");
+        print(PORT_FORMAT, "ID", "MAC", "IP", "VLAN", "WAN IPs");
+        osClient.networking().port().list().forEach(osPort -> {
+            ServicePort.Builder sportBuilder = DefaultServicePort.builder()
+                    .id(PortId.of(osPort.getId()))
+                    .networkId(NetworkId.of(osPort.getNetworkId()));
+
+            if (!Strings.isNullOrEmpty(osPort.getName())) {
+                sportBuilder.name(osPort.getName());
+            }
+            if (osPort.getMacAddress() != null) {
+                sportBuilder.mac(MacAddress.valueOf(osPort.getMacAddress()));
+            }
+            if (!osPort.getFixedIps().isEmpty()) {
+                sportBuilder.ip(IpAddress.valueOf(
+                        osPort.getFixedIps().iterator().next().getIpAddress()));
+            }
+            ServicePort sport = sportBuilder.build();
+            if (snetService.servicePort(sport.id()) != null) {
+                snetService.updateServicePort(sport);
+            } else {
+                snetService.createServicePort(sport);
+            }
+            ServicePort updated = snetService.servicePort(sport.id());
+            List<String> floatingIps = updated.addressPairs().stream()
+                    .map(ip -> ip.ip().toString())
+                    .collect(Collectors.toList());
+            print(PORT_FORMAT, updated.id(),
+                  updated.mac(),
+                  updated.ip(),
+                  updated.vlanId() != null ? updated.vlanId() : "",
+                  floatingIps.isEmpty() ? "" : floatingIps);
+        });
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncStatesCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncStatesCommand.java
deleted file mode 100644
index 5540391..0000000
--- a/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncStatesCommand.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Laboratory
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.opencord.cordvtn.cli;
-
-import org.apache.karaf.shell.commands.Command;
-import org.onosproject.cli.AbstractShellCommand;
-import org.opencord.cordvtn.api.core.CordVtnAdminService;
-
-/**
- * Synchronizes network states with external services.
- */
-@Command(scope = "onos", name = "cordvtn-sync-states",
-        description = "Synchronizes network states with external services")
-public class CordVtnSyncStatesCommand extends AbstractShellCommand {
-
-    @Override
-    protected void execute() {
-        CordVtnAdminService adminService =
-                AbstractShellCommand.get(CordVtnAdminService.class);
-        adminService.syncStates();
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncXosStatesCommand.java b/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncXosStatesCommand.java
new file mode 100644
index 0000000..b3fc988
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/cli/CordVtnSyncXosStatesCommand.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
+import org.opencord.cordvtn.rest.XosVtnNetworkingClient;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Synchronizes network states with XOS VTN service.
+ * This command can be used to actively synchronize XOS network with VTN
+ * service network.
+ */
+@Command(scope = "onos", name = "cordvtn-sync-xos-states",
+        description = "Synchronizes network states with Neutron")
+public class CordVtnSyncXosStatesCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "endpoint", description = "XOS VTN service endpoint",
+            required = true, multiValued = false)
+    private String endpoint = null;
+
+    @Argument(index = 1, name = "user", description = "XOS admin user name",
+            required = true, multiValued = false)
+    private String user = null;
+
+    @Argument(index = 2, name = "password", description = "XOS admin user password",
+            required = true, multiValued = false)
+    private String password = null;
+
+    private static final String NET_FORMAT = "%-40s%-20s%-20s%-8s%-20s%s";
+    private static final String PORT_FORMAT = "%-40s%-20s%-18s%-8s%s";
+
+    @Override
+    protected void execute() {
+        ServiceNetworkAdminService snetService =
+                AbstractShellCommand.get(ServiceNetworkAdminService.class);
+
+        XosVtnNetworkingClient client = XosVtnNetworkingClient.builder()
+                .endpoint(endpoint)
+                .user(user)
+                .password(password)
+                .build();
+
+        print("Synchronizing service networks...");
+        print(NET_FORMAT, "ID", "Name", "Type", "VNI", "Subnet", "Service IP");
+        client.serviceNetworks().forEach(snet -> {
+            if (snetService.serviceNetwork(snet.id()) != null) {
+                snetService.updateServiceNetwork(snet);
+            } else {
+                snetService.createServiceNetwork(snet);
+            }
+            ServiceNetwork updated = snetService.serviceNetwork(snet.id());
+            print(NET_FORMAT, updated.id(),
+                  updated.name(),
+                  updated.type(),
+                  updated.segmentId(),
+                  updated.subnet(),
+                  updated.serviceIp());
+        });
+
+        // FIXME creating a port fails until XOS service API provides network ID
+        print("\nSynchronizing service ports...");
+        print(PORT_FORMAT, "ID", "MAC", "IP", "VLAN", "WAN IPs");
+        client.servicePorts().forEach(sport -> {
+            if (snetService.servicePort(sport.id()) != null) {
+                snetService.updateServicePort(sport);
+            } else {
+                snetService.createServicePort(sport);
+            }
+            ServicePort updated = snetService.servicePort(sport.id());
+            List<String> floatingIps = updated.addressPairs().stream()
+                    .map(ip -> ip.ip().toString())
+                    .collect(Collectors.toList());
+            print(PORT_FORMAT, updated.id(),
+                  updated.mac(),
+                  updated.ip(),
+                  updated.vlanId() != null ? updated.vlanId() : "",
+                  floatingIps.isEmpty() ? "" : floatingIps);
+        });
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java b/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
index 84226b9..331bffe 100644
--- a/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
+++ b/src/main/java/org/opencord/cordvtn/codec/ServiceNetworkCodec.java
@@ -16,25 +16,28 @@
 package org.opencord.cordvtn.codec;
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.Sets;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.codec.CodecContext;
 import org.onosproject.codec.JsonCodec;
-import org.opencord.cordvtn.api.dependency.Dependency;
 import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.ProviderNetwork;
+import org.opencord.cordvtn.api.net.SegmentId;
 import org.opencord.cordvtn.api.net.ServiceNetwork;
-import org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType;
+import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
+import org.opencord.cordvtn.impl.DefaultServiceNetwork;
 
-import java.util.Set;
+import java.util.Map;
 
 import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.Boolean.FALSE;
 import static java.lang.Boolean.TRUE;
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.BIDIRECTIONAL;
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.UNIDIRECTIONAL;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.valueOf;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.UNIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.valueOf;
 
 /**
  * Service network JSON codec.
@@ -42,53 +45,176 @@
 public final class ServiceNetworkCodec extends JsonCodec<ServiceNetwork> {
 
     private static final String ID = "id";
+    private static final String NAME = "name";
     private static final String TYPE = "type";
+    private static final String SEGMENT_ID = "segment_id";
+    private static final String SUBNET = "subnet";
+    private static final String SERVICE_IP = "service_ip";
+    @Deprecated
     private static final String PROVIDER_NETWORKS = "providerNetworks";
-    private static final String BIDIRECT = "bidirectional";
+    private static final String PROVIDERS = "providers";
+    private static final String DEP_TYPE = "bidirectional";
 
     private static final String ERR_JSON = "Invalid ServiceNetwork received";
-    private static final String ERR_ID = ": network ID cannot be null";
-    private static final String ERR_TYPE = ": type cannot be null";
+    private static final String ERR_ID = "Service network ID cannot be null";
 
     @Override
     public ObjectNode encode(ServiceNetwork snet, CodecContext context) {
-        ObjectNode result = context.mapper().createObjectNode()
-                .put(ID, snet.id().id())
-                .put(TYPE, snet.type().name().toLowerCase());
-
+        ObjectNode result = context.mapper().createObjectNode().put(ID, snet.id().id());
+        if (!Strings.isNullOrEmpty(snet.name())) {
+            result.put(NAME, snet.name());
+        }
+        if (snet.type() != null) {
+            result.put(TYPE, snet.type().name());
+        }
+        if (snet.segmentId() != null) {
+            result.put(SEGMENT_ID, snet.segmentId().id());
+        }
+        if (snet.subnet() != null) {
+            result.put(SUBNET, snet.subnet().toString());
+        }
+        if (snet.serviceIp() != null) {
+            result.put(SERVICE_IP, snet.serviceIp().toString());
+        }
         ArrayNode providers = context.mapper().createArrayNode();
-        snet.providers().forEach(provider -> {
+        snet.providers().entrySet().forEach(provider -> {
             ObjectNode providerJson = context.mapper().createObjectNode()
-                    .put(ID, provider.id().id())
-                    .put(BIDIRECT, provider.type() == BIDIRECTIONAL ? TRUE : FALSE);
+                    .put(ID, provider.getKey().id())
+                    .put(DEP_TYPE, provider.getValue() == BIDIRECTIONAL ? TRUE : FALSE);
             providers.add(providerJson);
         });
-
-        result.set(PROVIDER_NETWORKS, providers);
+        result.set(PROVIDERS, providers);
         return result;
     }
 
     @Override
     public ServiceNetwork decode(ObjectNode json, CodecContext context) {
-        checkArgument(json != null && json.isObject(), ERR_JSON);
-        checkArgument(json.get(ID) != null &&
-                              json.get(ID) != NullNode.getInstance(),
-                      ERR_JSON + ERR_ID);
-        checkArgument(json.get(TYPE) != null &&
-                              json.get(TYPE) != NullNode.getInstance(),
-                      ERR_JSON + ERR_TYPE);
+        validateJson(json);
+        ServiceNetwork.Builder snetBuilder = DefaultServiceNetwork.builder()
+                .id(NetworkId.of(json.get(ID).asText()));
 
-        NetworkId netId = NetworkId.of(json.get(ID).asText());
-        ServiceNetworkType netType = valueOf(json.get(TYPE).asText().toUpperCase());
-        Set<ProviderNetwork> providers = Sets.newHashSet();
+        // TODO remove existing values when explicit null received
+        if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+            snetBuilder.name(json.get(NAME).asText());
+        }
+        if (json.get(TYPE) != null && !json.get(TYPE).isNull()) {
+            snetBuilder.type(valueOf(json.get(TYPE).asText().toUpperCase()));
+        }
+        if (json.get(SEGMENT_ID) != null && !json.get(SEGMENT_ID).isNull()) {
+            snetBuilder.segmentId(SegmentId.of(json.get(SEGMENT_ID).asLong()));
+        }
+        if (json.get(SUBNET) != null && !json.get(SUBNET).isNull()) {
+            snetBuilder.subnet(IpPrefix.valueOf(json.get(SUBNET).asText()));
+        }
+        if (json.get(SERVICE_IP) != null && !json.get(SERVICE_IP).isNull()) {
+            snetBuilder.serviceIp(IpAddress.valueOf(json.get(SERVICE_IP).asText()));
+        }
+        if (json.get(PROVIDERS) != null) {
+            if (json.get(PROVIDERS).isNull()) {
+                snetBuilder.providers(ImmutableMap.of());
+            } else {
+                Map<NetworkId, DependencyType> providers = Maps.newHashMap();
+                json.get(PROVIDERS).forEach(provider -> {
+                    DependencyType type = provider.get(DEP_TYPE).asBoolean() ?
+                            BIDIRECTIONAL : UNIDIRECTIONAL;
+                    providers.put(NetworkId.of(provider.get(ID).asText()), type);
+                });
+                snetBuilder.providers(providers);
+            }
+        }
         if (json.get(PROVIDER_NETWORKS) != null) {
-            json.get(PROVIDER_NETWORKS).forEach(provider -> {
-                NetworkId providerId = NetworkId.of(provider.get(ID).asText());
-                Dependency.Type type = provider.get(BIDIRECT).asBoolean() ?
-                        BIDIRECTIONAL : UNIDIRECTIONAL;
-                providers.add(ProviderNetwork.of(providerId, type));
+            if (json.get(PROVIDER_NETWORKS).isNull()) {
+                snetBuilder.providers(ImmutableMap.of());
+            } else {
+                Map<NetworkId, DependencyType> providers = Maps.newHashMap();
+                json.get(PROVIDER_NETWORKS).forEach(provider -> {
+                    DependencyType type = provider.get(DEP_TYPE).asBoolean() ?
+                            BIDIRECTIONAL : UNIDIRECTIONAL;
+                    providers.put(NetworkId.of(provider.get(ID).asText()), type);
+                });
+                snetBuilder.providers(providers);
+            }
+        }
+        return snetBuilder.build();
+    }
+
+    private void validateJson(ObjectNode json) {
+        checkArgument(json != null && json.isObject(), ERR_JSON);
+        checkArgument(json.get(ID) != null && !json.get(ID).isNull(), ERR_ID);
+        checkArgument(!Strings.isNullOrEmpty(json.get(ID).asText()), ERR_ID);
+
+        // allow explicit null for removing the existing value
+        if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+            if (Strings.isNullOrEmpty(json.get(NAME).asText())) {
+                final String error = "Null or empty ServiceNetwork name received";
+                throw new IllegalArgumentException(error);
+            }
+        }
+
+        if (json.get(TYPE) != null && !json.get(TYPE).isNull()) {
+            try {
+                valueOf(json.get(TYPE).asText().toUpperCase());
+            } catch (IllegalArgumentException e) {
+                final String error = "Invalid ServiceNetwork type received: ";
+                throw new IllegalArgumentException(error + json.get(TYPE).asText());
+            }
+        }
+
+        if (json.get(SEGMENT_ID) != null && !json.get(SEGMENT_ID).isNull()) {
+            if (json.get(SEGMENT_ID).asLong() == 0) {
+                final String error = "Invalid ServiecNetwork segment ID received: ";
+                throw new IllegalArgumentException(error + json.get(SEGMENT_ID).asText());
+            }
+        }
+
+        if (json.get(SUBNET) != null && !json.get(SUBNET).isNull()) {
+            try {
+                IpPrefix.valueOf(json.get(SUBNET).asText());
+            } catch (IllegalArgumentException e) {
+                final String error = "Invalid ServiceNetwork subnet received: ";
+                throw new IllegalArgumentException(error + json.get(SUBNET).asText());
+            }
+        }
+
+        if (json.get(SERVICE_IP) != null && !json.get(SERVICE_IP).isNull()) {
+            try {
+                IpAddress.valueOf(json.get(SERVICE_IP).asText());
+            } catch (IllegalArgumentException e) {
+                final String error = "Invalid ServiceNetwork service IP address received: ";
+                throw new IllegalArgumentException(error + json.get(SERVICE_IP).asText());
+            }
+        }
+
+        if (json.get(PROVIDERS) != null && !json.get(PROVIDERS).isNull()) {
+            json.get(PROVIDERS).forEach(provider -> {
+                if (provider.get(ID) == null || provider.get(ID).isNull() ||
+                        Strings.isNullOrEmpty(provider.get(ID).asText())) {
+                    final String error = "Null or empty provider network ID received";
+                    throw new IllegalArgumentException(error);
+                }
+
+                if (provider.get(DEP_TYPE) == null || provider.get(DEP_TYPE).isNull()
+                        || !provider.get(DEP_TYPE).isBoolean()) {
+                    final String error = "Non-boolean bidirectional received";
+                    throw new IllegalArgumentException(error);
+                }
             });
         }
-        return new ServiceNetwork(netId, netType, providers);
+
+        if (json.get(PROVIDER_NETWORKS) != null && !json.get(PROVIDER_NETWORKS).isNull()) {
+            json.get(PROVIDER_NETWORKS).forEach(provider -> {
+                if (provider.get(ID) == null || provider.get(ID).isNull() ||
+                        Strings.isNullOrEmpty(provider.get(ID).asText())) {
+                    final String error = "Null or empty provider network ID received";
+                    throw new IllegalArgumentException(error);
+                }
+
+                if (provider.get(DEP_TYPE) == null || provider.get(DEP_TYPE).isNull()
+                        || !provider.get(DEP_TYPE).isBoolean()) {
+                    final String error = "Non-boolean bidirectional received";
+                    throw new IllegalArgumentException(error);
+                }
+            });
+        }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java b/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
index bb4a82b..5292604 100644
--- a/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
+++ b/src/main/java/org/opencord/cordvtn/codec/ServicePortCodec.java
@@ -16,8 +16,9 @@
 package org.opencord.cordvtn.codec;
 
 import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+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.MacAddress;
@@ -27,6 +28,7 @@
 import org.opencord.cordvtn.api.net.AddressPair;
 import org.opencord.cordvtn.api.net.PortId;
 import org.opencord.cordvtn.api.net.ServicePort;
+import org.opencord.cordvtn.impl.DefaultServicePort;
 
 import java.util.Set;
 
@@ -38,22 +40,36 @@
 public final class ServicePortCodec extends JsonCodec<ServicePort> {
 
     private static final String ID = "id";
+    private static final String NAME = "name";
+    private static final String NETWORK_ID = "network_id";
+    private static final String MAC_ADDRESS = "mac_address";
+    private static final String IP_ADDRESS = "ip_address";
     private static final String VLAN_ID = "vlan_id";
     private static final String FLOATING_ADDRESS_PAIRS = "floating_address_pairs";
-    private static final String IP_ADDRESS = "ip_address";
-    private static final String MAC_ADDRESS = "mac_address";
 
     private static final String ERR_JSON = "Invalid ServicePort received";
-    private static final String ERR_ID = ": port ID cannot be null";
+    private static final String ERR_ID = "Service port ID cannot be null";
 
     @Override
     public ObjectNode encode(ServicePort sport, CodecContext context) {
         ObjectNode result = context.mapper().createObjectNode()
                 .put(ID, sport.id().id());
-        if (sport.vlanId().isPresent()) {
-            result.put(VLAN_ID, sport.vlanId().get().id());
-        }
 
+        if (sport.networkId() != null) {
+            result.put(NETWORK_ID, sport.networkId().id());
+        }
+        if (!Strings.isNullOrEmpty(sport.name())) {
+            result.put(NAME, sport.name());
+        }
+        if (sport.vlanId() != null) {
+            result.put(VLAN_ID, sport.vlanId().id());
+        }
+        if (sport.mac() != null) {
+            result.put(MAC_ADDRESS, sport.mac().toString());
+        }
+        if (sport.ip() != null) {
+            result.put(IP_ADDRESS, sport.ip().toString());
+        }
         ArrayNode addressPairs = context.mapper().createArrayNode();
         sport.addressPairs().forEach(pair -> {
             ObjectNode pairJson = context.mapper().createObjectNode()
@@ -62,34 +78,107 @@
             addressPairs.add(pairJson);
         });
         result.set(FLOATING_ADDRESS_PAIRS, addressPairs);
+
         return result;
     }
 
     @Override
     public ServicePort decode(ObjectNode json, CodecContext context) {
-        checkArgument(json != null && json.isObject(), ERR_JSON);
-        checkArgument(json.get(ID) != null &&
-                              json.get(ID) != NullNode.getInstance(),
-                      ERR_JSON + ERR_ID);
+        validateJson(json);
+        ServicePort.Builder sportBuilder = DefaultServicePort.builder()
+                .id(PortId.of(json.get(ID).asText()));
 
-        PortId portId = PortId.of(json.get(ID).asText());
-        VlanId vlanId = null;
-        if (json.get(VLAN_ID) != NullNode.getInstance()) {
-            try {
-                vlanId = VlanId.vlanId(json.get(VLAN_ID).asText());
-            } catch (Exception ignore) {
-            }
+        // TODO allow removing existing value when explicit null received
+        if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+            sportBuilder.name(json.get(NAME).asText());
         }
-
-        Set<AddressPair> addressPairs = Sets.newHashSet();
-        if (json.get(FLOATING_ADDRESS_PAIRS) != null) {
+        if (json.get(MAC_ADDRESS) != null && !json.get(MAC_ADDRESS).isNull()) {
+            sportBuilder.mac(MacAddress.valueOf(json.get(MAC_ADDRESS).asText()));
+        }
+        if (json.get(IP_ADDRESS) != null && !json.get(IP_ADDRESS).isNull()) {
+            sportBuilder.ip(IpAddress.valueOf(json.get(IP_ADDRESS).asText()));
+        }
+        if (json.get(VLAN_ID) != null && !json.get(VLAN_ID).isNull()) {
+            sportBuilder.vlanId(VlanId.vlanId(json.get(VLAN_ID).asText()));
+        }
+        if (json.get(FLOATING_ADDRESS_PAIRS).isNull()) {
+            sportBuilder.addressPairs(ImmutableSet.of());
+        } else if (json.get(FLOATING_ADDRESS_PAIRS) != null) {
+            Set<AddressPair> addressPairs = Sets.newHashSet();
             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()));
                 addressPairs.add(addrPair);
             });
+            sportBuilder.addressPairs(addressPairs);
         }
-        return new ServicePort(portId, vlanId, addressPairs);
+        return sportBuilder.build();
+    }
+
+    private void validateJson(ObjectNode json) {
+        checkArgument(json != null && json.isObject(), ERR_JSON);
+        checkArgument(json.get(ID) != null && !json.get(ID).isNull(), ERR_ID);
+
+        // allow explicit null for removing the existing value
+        if (json.get(NAME) != null && !json.get(NAME).isNull()) {
+            if (Strings.isNullOrEmpty(json.get(NAME).asText())) {
+                final String error = "Null or empty ServiceNetwork name received";
+                throw new IllegalArgumentException(error);
+            }
+        }
+
+        if (json.get(MAC_ADDRESS) != null && !json.get(MAC_ADDRESS).isNull()) {
+            try {
+                MacAddress.valueOf(json.get(MAC_ADDRESS).asText());
+            } catch (IllegalArgumentException e) {
+                final String error = "Invalid ServicePort MAC address received: ";
+                throw new IllegalArgumentException(error + json.get(MAC_ADDRESS).asText());
+            }
+        }
+
+        if (json.get(IP_ADDRESS) != null && !json.get(IP_ADDRESS).isNull()) {
+            try {
+                IpAddress.valueOf(json.get(IP_ADDRESS).asText());
+            } catch (IllegalArgumentException e) {
+                final String error = "Invalid ServicePort IP address received: ";
+                throw new IllegalArgumentException(error + json.get(IP_ADDRESS).asText());
+            }
+        }
+
+        if (json.get(VLAN_ID) != null && !json.get(VLAN_ID).isNull()) {
+            try {
+                VlanId.vlanId(json.get(VLAN_ID).asText());
+            } catch (IllegalArgumentException e) {
+                final String error = "Invalid VLAN ID is received: ";
+                throw new IllegalArgumentException(error + json.get(VLAN_ID).asText());
+            }
+        }
+
+        if (json.get(FLOATING_ADDRESS_PAIRS) != null &&
+                !json.get(FLOATING_ADDRESS_PAIRS).isNull()) {
+            json.get(FLOATING_ADDRESS_PAIRS).forEach(pair -> {
+                if (pair.get(IP_ADDRESS) == null ||
+                        pair.get(IP_ADDRESS).isNull() ||
+                        pair.get(MAC_ADDRESS) == null ||
+                        pair.get(MAC_ADDRESS).isNull()) {
+                    final String error = "Invalid floating address pair received";
+                    throw new IllegalArgumentException(error);
+                }
+                try {
+                    IpAddress.valueOf(pair.get(IP_ADDRESS).asText());
+                } catch (IllegalArgumentException e) {
+                    final String error = "Invalid floating address pair IP: ";
+                    throw new IllegalArgumentException(error + pair.get(IP_ADDRESS).asText());
+                }
+
+                try {
+                    MacAddress.valueOf(pair.get(MAC_ADDRESS).asText());
+                } catch (IllegalArgumentException e) {
+                    final String error = "Invalid floating address pair MAC: ";
+                    throw new IllegalArgumentException(error + pair.get(MAC_ADDRESS).asText());
+                }
+            });
+        }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
index c2e2a62..7b906e9 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
@@ -29,14 +29,11 @@
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.packet.PacketProcessor;
-import org.opencord.cordvtn.api.config.CordVtnConfig;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.onosproject.net.Host;
+import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -44,8 +41,11 @@
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
-import org.opencord.cordvtn.api.net.VtnNetwork;
+import org.opencord.cordvtn.api.CordVtnConfig;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
 import org.opencord.cordvtn.impl.handler.AbstractInstanceHandler;
 import org.slf4j.Logger;
 
@@ -55,7 +55,7 @@
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.*;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -72,7 +72,7 @@
     protected CordVtnNodeManager nodeManager;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
+    protected NetworkConfigService configService;
 
     private final PacketProcessor packetProcessor = new InternalPacketProcessor();
     private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
@@ -83,7 +83,7 @@
     @Activate
     protected void activate() {
         super.activate();
-        configRegistry.addListener(configListener);
+        configService.addListener(configListener);
         readConfiguration();
 
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
@@ -93,7 +93,7 @@
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
-        configRegistry.removeListener(configListener);
+        configService.removeListener(configListener);
         super.deactivate();
     }
 
@@ -354,13 +354,13 @@
             return;
         }
 
-        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);
+        ServiceNetwork snet = getServiceNetwork(instance);
+        if (snet.type() != PUBLIC && snet.type() != MANAGEMENT_HOST &&
+                snet.type() != MANAGEMENT_LOCAL) {
+            log.trace("Added IP:{} MAC:{}", snet.serviceIp(), privateGatewayMac);
+            addGateway(snet.serviceIp(), privateGatewayMac);
             // send gratuitous ARP for the existing VMs when ONOS is restarted
-            sendGratuitousArp(vtnNet.serviceIp(), ImmutableSet.of(instance));
+            sendGratuitousArp(snet.serviceIp(), ImmutableSet.of(instance));
         }
     }
 
@@ -372,7 +372,7 @@
             return;
         }
 
-        VtnNetwork vtnNet = getVtnNetwork(instance);
+        ServiceNetwork vtnNet = getServiceNetwork(instance);
         // remove this network gateway from proxy ARP if no instance presents
         if (vtnNet.type() == PRIVATE &&
                 getInstances(vtnNet.id()).isEmpty()) {
@@ -381,7 +381,7 @@
     }
 
     private void readConfiguration() {
-        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
         if (config == null) {
             log.debug("No configuration found");
             return;
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
index 607e3b0..e1f8fbf 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnDhcpProxy.java
@@ -39,7 +39,7 @@
 import org.onosproject.net.HostId;
 import org.onosproject.net.config.NetworkConfigEvent;
 import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
@@ -51,10 +51,11 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.opencord.cordvtn.api.Constants;
-import org.opencord.cordvtn.api.config.CordVtnConfig;
-import org.opencord.cordvtn.api.core.CordVtnService;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.net.VtnNetwork;
+import org.opencord.cordvtn.api.CordVtnConfig;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.ServiceNetworkService;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
@@ -68,8 +69,9 @@
 import static org.onlab.packet.DHCP.DHCPOptionCode.*;
 import static org.onlab.packet.DHCPPacketType.DHCPACK;
 import static org.onlab.packet.DHCPPacketType.DHCPOFFER;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.MANAGEMENT_HOST;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.MANAGEMENT_LOCAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.MANAGEMENT_HOST;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.MANAGEMENT_LOCAL;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -94,7 +96,7 @@
     protected CoreService coreService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
+    protected NetworkConfigService configService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
@@ -103,7 +105,7 @@
     protected HostService hostService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CordVtnService cordVtnService;
+    protected ServiceNetworkService snetService;
 
     private final PacketProcessor packetProcessor = new InternalPacketProcessor();
     private final NetworkConfigListener configListener = new InternalConfigListener();
@@ -114,7 +116,7 @@
     @Activate
     protected void activate() {
         appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
-        configRegistry.addListener(configListener);
+        configService.addListener(configListener);
         readConfiguration();
 
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
@@ -124,7 +126,7 @@
 
     @Deactivate
     protected void deactivate() {
-        configRegistry.removeListener(configListener);
+        configService.removeListener(configListener);
         packetService.removeProcessor(packetProcessor);
         cancelPackets();
         log.info("Stopped");
@@ -246,8 +248,8 @@
             checkArgument(!dhcpServerMac.equals(MacAddress.NONE),
                           "DHCP server MAC is not set");
 
-            VtnNetwork vtnNet = cordVtnService.vtnNetwork(reqInstance.netId());
-            Ip4Address serverIp = vtnNet.serviceIp().getIp4Address();
+            ServiceNetwork snet = snetService.serviceNetwork(reqInstance.netId());
+            Ip4Address serverIp = snet.serviceIp().getIp4Address();
 
             Ethernet ethReply = new Ethernet();
             ethReply.setSourceMACAddress(dhcpServerMac);
@@ -267,7 +269,7 @@
 
             DHCP dhcpRequest = (DHCP) udpRequest.getPayload();
             DHCP dhcpReply = buildDhcpReply(
-                    dhcpRequest, packetType, reqInstance.ipAddress(), vtnNet);
+                    dhcpRequest, packetType, reqInstance.ipAddress(), snet);
 
             udpReply.setPayload(dhcpReply);
             ipv4Reply.setPayload(udpReply);
@@ -294,9 +296,9 @@
         }
 
         private DHCP buildDhcpReply(DHCP request, byte msgType, Ip4Address yourIp,
-                                    VtnNetwork vtnNet) {
-            Ip4Address serverIp = vtnNet.serviceIp().getIp4Address();
-            int subnetPrefixLen = vtnNet.subnet().prefixLength();
+                                    ServiceNetwork snet) {
+            Ip4Address serverIp = snet.serviceIp().getIp4Address();
+            int subnetPrefixLen = snet.subnet().prefixLength();
 
             DHCP dhcpReply = new DHCP();
             dhcpReply.setOpCode(DHCP.OPCODE_REPLY);
@@ -362,7 +364,7 @@
             options.add(option);
 
             // router address
-            if (vtnNet.type() != MANAGEMENT_LOCAL && vtnNet.type() != MANAGEMENT_HOST) {
+            if (snet.type() != MANAGEMENT_LOCAL && snet.type() != MANAGEMENT_HOST) {
                 option = new DHCPOption();
                 option.setCode(OptionCode_RouterAddress.getValue());
                 option.setLength((byte) 4);
@@ -371,7 +373,7 @@
             }
 
             // classless static routes
-            byte[] data = getClasslessStaticRoutesData(vtnNet);
+            byte[] data = getClasslessStaticRoutesData(snet);
             if (data.length >= 5) {
                 option = new DHCPOption();
                 option.setCode(DHCP_OPTION_CLASSLESS_STATIC_ROUTE);
@@ -390,13 +392,13 @@
             return dhcpReply;
         }
 
-        private byte[] getClasslessStaticRoutesData(VtnNetwork vtnNet) {
+        private byte[] getClasslessStaticRoutesData(ServiceNetwork snet) {
             List<Byte> result = Lists.newArrayList();
-            List<Byte> router = Bytes.asList(vtnNet.serviceIp().toOctets());
+            List<Byte> router = Bytes.asList(snet.serviceIp().toOctets());
 
             // static routes for the providers
-            Set<VtnNetwork> providers = vtnNet.providers().stream()
-                    .map(provider -> cordVtnService.vtnNetwork(provider.id()))
+            Set<ServiceNetwork> providers = snet.providers().keySet().stream()
+                    .map(provider -> snetService.serviceNetwork(provider))
                     .filter(Objects::nonNull)
                     .collect(Collectors.toSet());
 
@@ -407,8 +409,8 @@
             });
 
             // static routes for the bidirectional subscribers
-            Set<VtnNetwork> subscribers = cordVtnService.vtnNetworks().stream()
-                    .filter(net -> net.isBidirectionalProvider(vtnNet.id()))
+            Set<ServiceNetwork> subscribers = snetService.serviceNetworks().stream()
+                    .filter(net -> isBidirectionalProvider(net, snet.id()))
                     .collect(Collectors.toSet());
 
             subscribers.stream().forEach(subscriber -> {
@@ -420,6 +422,13 @@
             return Bytes.toArray(result);
         }
 
+        private boolean isBidirectionalProvider(ServiceNetwork snet, NetworkId targetNetId) {
+            return snet.providers().entrySet().stream()
+                    .filter(p -> Objects.equals(p.getKey(), targetNetId))
+                    .filter(p -> p.getValue() == BIDIRECTIONAL)
+                    .findAny().isPresent();
+        }
+
         private List<Byte> getSignificantOctets(IpPrefix ipPrefix) {
             int numOfOctets = ipPrefix.prefixLength() / 8;
             if (ipPrefix.prefixLength() % 8 != 0) {
@@ -431,7 +440,7 @@
     }
 
     private void readConfiguration() {
-        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
         if (config == null) {
             log.debug("No configuration found");
             return;
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
deleted file mode 100644
index f3dd1f4..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnManager.java
+++ /dev/null
@@ -1,550 +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 com.google.common.collect.ImmutableSet;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.event.ListenerRegistry;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.config.ConfigFactory;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.config.basics.SubjectFactories;
-import org.onosproject.net.host.HostService;
-import org.opencord.cordvtn.api.Constants;
-import org.opencord.cordvtn.api.config.CordVtnConfig;
-import org.opencord.cordvtn.api.config.OpenStackConfig;
-import org.opencord.cordvtn.api.config.XosConfig;
-import org.opencord.cordvtn.api.core.CordVtnAdminService;
-import org.opencord.cordvtn.api.core.CordVtnService;
-import org.opencord.cordvtn.api.core.CordVtnStore;
-import org.opencord.cordvtn.api.core.CordVtnStoreDelegate;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.NetworkService;
-import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.ServiceNetwork;
-import org.opencord.cordvtn.api.net.ServiceNetworkService;
-import org.opencord.cordvtn.api.net.ServicePort;
-import org.opencord.cordvtn.api.net.SubnetId;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-import org.opencord.cordvtn.api.net.VtnNetworkEvent;
-import org.opencord.cordvtn.api.net.VtnNetworkListener;
-import org.opencord.cordvtn.api.net.VtnPort;
-import org.opencord.cordvtn.impl.external.OpenStackNetworking;
-import org.opencord.cordvtn.impl.external.XosServiceNetworking;
-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.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, NetworkService,
-        ServiceNetworkService {
-
-    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_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 NetworkConfigService configService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CordVtnStore store;
-
-    // TODO add cordvtn config service and move this
-    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 CordVtnStoreDelegate delegate = new InternalCordVtnStoreDelegate();
-    private ApplicationId appId;
-
-    @Activate
-    protected void activate() {
-        appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
-        configRegistry.registerConfigFactory(configFactory);
-        store.setDelegate(delegate);
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        configRegistry.unregisterConfigFactory(configFactory);
-        store.unsetDelegate(delegate);
-        log.info("Stopped");
-    }
-
-    @Override
-    public void purgeStates() {
-        store.clear();
-    }
-
-    @Override
-    public void syncStates() {
-        syncNetwork();
-        syncServiceNetwork();
-    }
-
-    @Override
-    public void createServiceNetwork(ServiceNetwork serviceNet) {
-        checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
-        synchronized (this) {
-            Network network = store.network(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.network(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 updateServiceNetwork(ServiceNetwork serviceNet) {
-        checkNotNull(serviceNet, ERR_NULL_SERVICE_NET);
-        synchronized (this) {
-            VtnNetwork existing = store.vtnNetwork(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 removeServiceNetwork(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 createServicePort(ServicePort servicePort) {
-        checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
-        synchronized (this) {
-            Port port = store.port(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 updateServicePort(ServicePort servicePort) {
-        checkNotNull(servicePort, ERR_NULL_SERVICE_PORT);
-        synchronized (this) {
-            VtnPort vtnPort = store.vtnPort(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 removeServicePort(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.network(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.network(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);
-            }
-            removeServicePort(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.network(NetworkId.of(subnet.getNetworkId())) == null) {
-                final String error = ERR_SYNC + subnet.getNetworkId() + ERR_NOT_FOUND;
-                throw new IllegalStateException(error);
-            }
-
-            Subnet existing = getSubnet(NetworkId.of(subnet.getNetworkId()));
-            if (existing != null && !Objects.equals(existing.getId(), subnet.getId())) {
-                // 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);
-            // FIXME update the network as well with the new 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.network(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) {
-            removeServiceNetwork(NetworkId.of(store.subnet(subnetId).getNetworkId()));
-            store.removeSubnet(subnetId);
-            log.info(String.format(MSG_SUBNET, REMOVED, subnetId));
-        }
-    }
-
-    @Override
-    public VtnNetwork vtnNetwork(NetworkId netId) {
-        checkNotNull(netId, ERR_NULL_NET_ID);
-
-        // return default VTN network if the network and subnet exist
-        VtnNetwork vtnNet = store.vtnNetwork(netId);
-        return vtnNet == null ? getDefaultVtnNetwork(netId) : vtnNet;
-    }
-
-    @Override
-    public Set<VtnNetwork> vtnNetworks() {
-        Set<VtnNetwork> vtnNetworks = networks().stream()
-                .filter(net -> vtnNetwork(NetworkId.of(net.getId())) != null)
-                .map(net -> vtnNetwork(NetworkId.of(net.getId())))
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(vtnNetworks);
-    }
-
-    @Override
-    public VtnPort vtnPort(PortId portId) {
-        checkNotNull(portId, ERR_NULL_PORT_ID);
-
-        // return default VTN port if the port exists
-        VtnPort vtnPort = store.vtnPort(portId);
-        return vtnPort == null ? getDefaultPort(portId) : vtnPort;
-    }
-
-    @Override
-    public VtnPort vtnPort(String portName) {
-        Optional<Port> port = store.ports()
-                .stream()
-                .filter(p -> p.getId().contains(portName.substring(3)))
-                .findFirst();
-        if (!port.isPresent()) {
-            return null;
-        }
-        return vtnPort(PortId.of(port.get().getId()));
-    }
-
-    @Override
-    public Set<VtnPort> vtnPorts() {
-        Set<VtnPort> vtnPorts = ports().stream()
-                .filter(port -> vtnPort(PortId.of(port.getId())) != null)
-                .map(port -> vtnPort(PortId.of(port.getId())))
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(vtnPorts);
-    }
-
-    @Override
-    public ServiceNetwork serviceNetwork(NetworkId netId) {
-        checkNotNull(netId, ERR_NULL_NET_ID);
-        return store.vtnNetwork(netId);
-    }
-
-    @Override
-    public Set<ServiceNetwork> serviceNetworks() {
-        return ImmutableSet.copyOf(store.vtnNetworks());
-    }
-
-    @Override
-    public ServicePort servicePort(PortId portId) {
-        checkNotNull(portId, ERR_NULL_PORT_ID);
-        return store.vtnPort(portId);
-    }
-
-    @Override
-    public Set<ServicePort> servicePorts() {
-        return ImmutableSet.copyOf(store.vtnPorts());
-    }
-
-    @Override
-    public Network network(NetworkId netId) {
-        checkNotNull(netId, ERR_NULL_NET_ID);
-        return store.network(netId);
-    }
-
-    @Override
-    public Set<Network> networks() {
-        return ImmutableSet.copyOf(store.networks());
-    }
-
-    @Override
-    public Port port(PortId portId) {
-        checkNotNull(portId, ERR_NULL_PORT_ID);
-        return store.port(portId);
-    }
-
-    @Override
-    public Set<Port> ports() {
-        return ImmutableSet.copyOf(store.ports());
-    }
-
-    @Override
-    public Subnet subnet(SubnetId subnetId) {
-        checkNotNull(subnetId, ERR_NULL_SUBNET_ID);
-        return store.subnet(subnetId);
-    }
-
-    @Override
-    public Set<Subnet> subnets() {
-        return ImmutableSet.copyOf(store.subnets());
-    }
-
-    private void syncNetwork() {
-        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
-        if (config == null) {
-            final String error = "Failed to read network configurations";
-            throw new IllegalArgumentException(error);
-        }
-
-        OpenStackConfig osConfig = config.openStackConfig();
-        NetworkService netService = OpenStackNetworking.builder()
-                .endpoint(osConfig.endpoint())
-                .tenant(osConfig.tenant())
-                .user(osConfig.user())
-                .password(osConfig.password())
-                .build();
-
-        netService.networks().forEach(this::createNetwork);
-        netService.subnets().forEach(this::createSubnet);
-        netService.ports().forEach(this::createPort);
-    }
-
-    private void syncServiceNetwork() {
-        CordVtnConfig config = configService.getConfig(appId, CordVtnConfig.class);
-        if (config == null) {
-            final String error = "Failed to read network configurations";
-            throw new IllegalArgumentException(error);
-        }
-
-        XosConfig xosConfig = config.xosConfig();
-        ServiceNetworkService snetService = XosServiceNetworking.builder()
-                .endpoint(xosConfig.endpoint())
-                .user(xosConfig.user())
-                .password(xosConfig.password())
-                .build();
-
-        snetService.serviceNetworks().forEach(this::createServiceNetwork);
-        snetService.servicePorts().forEach(this::createServicePort);
-    }
-
-    private Instance getInstance(PortId portId) {
-        VtnPort vtnPort = vtnPort(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);
-    }
-
-    private VtnNetwork getDefaultVtnNetwork(NetworkId netId) {
-        Network network = network(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 = port(portId);
-        if (port == null) {
-            return null;
-        }
-        return VtnPort.of(port, null);
-    }
-
-    private Subnet getSubnet(NetworkId netId) {
-        Optional<Subnet> subnet = subnets().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 f7f9c2d..44b70ac 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
@@ -65,12 +65,12 @@
 import org.onosproject.store.service.Serializer;
 import org.onosproject.store.service.StorageService;
 import org.onosproject.store.service.Versioned;
-import org.opencord.cordvtn.api.config.CordVtnConfig;
-import org.opencord.cordvtn.api.instance.InstanceService;
+import org.opencord.cordvtn.api.CordVtnConfig;
+import org.opencord.cordvtn.api.core.InstanceService;
+import org.opencord.cordvtn.api.net.CidrAddr;
 import org.opencord.cordvtn.api.node.ConnectionHandler;
 import org.opencord.cordvtn.api.node.CordVtnNode;
 import org.opencord.cordvtn.api.node.CordVtnNodeState;
-import org.opencord.cordvtn.api.node.NetworkAddress;
 import org.opencord.cordvtn.api.node.SshAccessInfo;
 import org.slf4j.Logger;
 
@@ -107,7 +107,7 @@
             .register(CordVtnNode.class)
             .register(NodeState.class)
             .register(SshAccessInfo.class)
-            .register(NetworkAddress.class);
+            .register(CidrAddr.class);
 
     private static final int DPID_BEGIN = 3;
 
@@ -751,6 +751,7 @@
             if (node.systemIfaces().contains(portName)) {
                 setNodeState(node, getNodeState(node));
             } else if (isNodeStateComplete(node)) {
+                // TODO move this logic to InstanceManager
                 instanceService.addInstance(connectPoint(port));
             } else {
                 log.warn("Instance is detected on incomplete node, ignore it.", portName);
@@ -777,6 +778,7 @@
             if (node.systemIfaces().contains(portName)) {
                 setNodeState(node, NodeState.INCOMPLETE);
             } else if (isNodeStateComplete(node)) {
+                // TODO move this logic to InstanceManager
                 instanceService.removeInstance(connectPoint(port));
             } else {
                 log.warn("VM is vanished from incomplete node, ignore it.", portName);
diff --git a/src/main/java/org/opencord/cordvtn/impl/DefaultServiceNetwork.java b/src/main/java/org/opencord/cordvtn/impl/DefaultServiceNetwork.java
new file mode 100644
index 0000000..68a8d4a
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/DefaultServiceNetwork.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.impl;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.SegmentId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+
+import java.util.Map;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Collections.EMPTY_MAP;
+
+/**
+ * Implementation of {@link ServiceNetwork}.
+ */
+public final class DefaultServiceNetwork implements ServiceNetwork {
+
+    private static final String ERR_ID_MISSING = "Service network ID cannot be null";
+
+    private final NetworkId id;
+    private final String name;
+    private final NetworkType type;
+    private final SegmentId segmentId;
+    private final IpPrefix subnet;
+    private final IpAddress serviceIp;
+    private final Map<NetworkId, DependencyType> providers;
+
+    private DefaultServiceNetwork(NetworkId id,
+                                  String name,
+                                  NetworkType type,
+                                  SegmentId segmentId,
+                                  IpPrefix subnet,
+                                  IpAddress serviceIp,
+                                  Map<NetworkId, DependencyType> providers) {
+        this.id = id;
+        this.name = name;
+        this.type = type;
+        this.segmentId = segmentId;
+        this.subnet = subnet;
+        this.serviceIp = serviceIp;
+        this.providers = providers;
+    }
+
+    @Override
+    public NetworkId id() {
+        return id;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+
+    @Override
+    public NetworkType type() {
+        return type;
+    }
+
+    @Override
+    public SegmentId segmentId() {
+        return segmentId;
+    }
+
+    @Override
+    public IpPrefix subnet() {
+        return subnet;
+    }
+
+    @Override
+    public IpAddress serviceIp() {
+        return serviceIp;
+    }
+
+    @Override
+    public Map<NetworkId, DependencyType> providers() {
+        return providers;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof DefaultServiceNetwork) {
+            DefaultServiceNetwork that = (DefaultServiceNetwork) obj;
+            if (Objects.equals(id, that.id) &&
+                    Objects.equals(name, that.name) &&
+                    Objects.equals(type, that.type) &&
+                    Objects.equals(segmentId, that.segmentId) &&
+                    Objects.equals(subnet, that.subnet) &&
+                    Objects.equals(serviceIp, that.serviceIp) &&
+                    Objects.equals(providers, that.providers)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, name, segmentId, subnet, serviceIp, type, providers);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id)
+                .add("name", name)
+                .add("type", type)
+                .add("segmentId", segmentId)
+                .add("subnet", subnet)
+                .add("serviceIp", serviceIp)
+                .add("providers", providers)
+                .toString();
+    }
+
+    /**
+     * Returns new service network builder instance.
+     *
+     * @return service network builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns new service network builder instance with copy of the given service network.
+     *
+     * @param snet service network
+     * @return service network builder
+     */
+    public static Builder builder(ServiceNetwork snet) {
+        return new Builder()
+                .id(snet.id())
+                .name(snet.name())
+                .type(snet.type())
+                .segmentId(snet.segmentId())
+                .subnet(snet.subnet())
+                .serviceIp(snet.serviceIp())
+                .providers(snet.providers());
+    }
+
+    /**
+     * Returns service network builder instance with updated values. Any value
+     * not specified in the updated but in the existing remains in the updated
+     * builder.
+     *
+     * @param existing existing service network
+     * @param updated  updated service network
+     * @return service network builder
+     */
+    public static Builder builder(ServiceNetwork existing, ServiceNetwork updated) {
+        checkArgument(Objects.equals(existing.id(), updated.id()));
+        // FIXME allow removing existing values
+        return new Builder()
+                .id(existing.id())
+                .name(updated.name() != null ? updated.name() : existing.name())
+                .type(updated.type() != null ? updated.type() : existing.type())
+                .segmentId(updated.segmentId() != null ?
+                                   updated.segmentId() : existing.segmentId())
+                .subnet(updated.subnet() != null ? updated.subnet() : existing.subnet())
+                .serviceIp(updated.serviceIp() != null ?
+                                   updated.serviceIp() : existing.serviceIp())
+                .providers(updated.providers() != EMPTY_MAP ?
+                                   updated.providers() : existing.providers());
+    }
+
+    public static final class Builder implements ServiceNetwork.Builder {
+
+        private NetworkId id;
+        private String name;
+        private NetworkType type;
+        private SegmentId segmentId;
+        private IpPrefix subnet;
+        private IpAddress serviceIp;
+        private Map<NetworkId, DependencyType> providers;
+
+        private Builder() {
+        }
+
+        @Override
+        public ServiceNetwork build() {
+            checkNotNull(id, ERR_ID_MISSING);
+            providers = providers != null ? providers : EMPTY_MAP;
+            return new DefaultServiceNetwork(
+                    id, name, type,
+                    segmentId,
+                    subnet, serviceIp,
+                    providers);
+        }
+
+        @Override
+        public Builder id(NetworkId id) {
+            this.id = id;
+            return this;
+        }
+
+        @Override
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        @Override
+        public Builder type(NetworkType type) {
+            this.type = type;
+            return this;
+        }
+
+        @Override
+        public Builder segmentId(SegmentId segmentId) {
+            this.segmentId = segmentId;
+            return this;
+        }
+
+        @Override
+        public Builder subnet(IpPrefix subnet) {
+            this.subnet = subnet;
+            return this;
+        }
+
+        @Override
+        public Builder serviceIp(IpAddress serviceIp) {
+            this.serviceIp = serviceIp;
+            return this;
+        }
+
+        @Override
+        public Builder providers(Map<NetworkId, DependencyType> providers) {
+            this.providers = providers;
+            return this;
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/impl/DefaultServicePort.java b/src/main/java/org/opencord/cordvtn/impl/DefaultServicePort.java
new file mode 100644
index 0000000..3ce8b78
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/DefaultServicePort.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.impl;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.opencord.cordvtn.api.net.AddressPair;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.ServicePort;
+
+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 java.util.Collections.EMPTY_SET;
+
+/**
+ * Implementation of {@link ServicePort}.
+ */
+public final class DefaultServicePort implements ServicePort {
+
+    private static final String ERR_ID_MISSING = "Service port ID is missing";
+
+    private final PortId id;
+    private final String name;
+    private final NetworkId networkId;
+    private final MacAddress mac;
+    private final IpAddress ip;
+    private final VlanId vlanId;
+    private final Set<AddressPair> addressPairs;
+
+    private DefaultServicePort(PortId id,
+                               String name,
+                               NetworkId networkId,
+                               MacAddress mac,
+                               IpAddress ip,
+                               VlanId vlanId,
+                               Set<AddressPair> addressPairs) {
+        this.id = id;
+        this.name = name;
+        this.networkId = networkId;
+        this.mac = mac;
+        this.ip = ip;
+        this.vlanId = vlanId;
+        this.addressPairs = addressPairs;
+    }
+
+    @Override
+    public PortId id() {
+        return id;
+    }
+
+    @Override
+    public String name() {
+        return name;
+    }
+
+    @Override
+    public NetworkId networkId() {
+        return networkId;
+    }
+
+    @Override
+    public MacAddress mac() {
+        return mac;
+    }
+
+    @Override
+    public IpAddress ip() {
+        return ip;
+    }
+
+    @Override
+    public VlanId vlanId() {
+        return vlanId;
+    }
+
+    @Override
+    public Set<AddressPair> addressPairs() {
+        return addressPairs;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+
+        if (obj instanceof DefaultServicePort) {
+            DefaultServicePort that = (DefaultServicePort) obj;
+            if (Objects.equals(id, that.id) &&
+                    Objects.equals(name, that.name) &&
+                    Objects.equals(networkId, that.networkId) &&
+                    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, name, networkId, mac, ip, vlanId, addressPairs);
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("id", id)
+                .add("name", name)
+                .add("networkId", networkId)
+                .add("mac", mac)
+                .add("ip", ip)
+                .add("vlanId", vlanId)
+                .add("addressPairs", addressPairs)
+                .toString();
+    }
+
+    /**
+     * Returns new service port builder instance.
+     *
+     * @return vtn port builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Returns new builder instance with copy of the supplied service port.
+     *
+     * @param sport vtn port
+     * @return vtn port builder
+     */
+    public static Builder builder(ServicePort sport) {
+        return new Builder()
+                .id(sport.id())
+                .name(sport.name())
+                .networkId(sport.networkId())
+                .mac(sport.mac())
+                .ip(sport.ip())
+                .vlanId(sport.vlanId())
+                .addressPairs(sport.addressPairs());
+    }
+
+    /**
+     * Returns service port builder instance with updated values. Any value
+     * not specified in the updated but in the existing remains in the updated
+     * builder.
+     *
+     * @param existing existing service port
+     * @param updated  updated service port
+     * @return service network builder
+     */
+    public static Builder builder(ServicePort existing, ServicePort updated) {
+        checkArgument(Objects.equals(existing.id(), updated.id()));
+        // FIXME allow removing existing values
+        return new Builder()
+                .id(existing.id())
+                .name(updated.name() != null ? updated.name() : existing.name())
+                .networkId(updated.networkId() != null ?
+                                   updated.networkId() : existing.networkId())
+                .mac(updated.mac() != null ? updated.mac() : existing.mac())
+                .ip(updated.ip() != null ? updated.ip() : existing.ip())
+                .vlanId(updated.vlanId() != null ? updated.vlanId() : existing.vlanId())
+                .addressPairs(updated.addressPairs() != EMPTY_SET ?
+                                      updated.addressPairs() : existing.addressPairs());
+    }
+
+    public static final class Builder implements ServicePort.Builder {
+
+        private PortId id;
+        private String name;
+        private NetworkId networkId;
+        private MacAddress mac;
+        private IpAddress ip;
+        private VlanId vlanId;
+        private Set<AddressPair> addressPairs;
+
+        private Builder() {
+        }
+
+        @Override
+        public ServicePort build() {
+            checkNotNull(id, ERR_ID_MISSING);
+            addressPairs = addressPairs != null ? addressPairs : EMPTY_SET;
+            return new DefaultServicePort(
+                    id, name,
+                    networkId, mac, ip, vlanId,
+                    addressPairs);
+        }
+
+        @Override
+        public Builder id(PortId id) {
+            this.id = id;
+            return this;
+        }
+
+        @Override
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        @Override
+        public Builder networkId(NetworkId networkId) {
+            this.networkId = networkId;
+            return this;
+        }
+
+        @Override
+        public Builder mac(MacAddress mac) {
+            this.mac = mac;
+            return this;
+        }
+
+        @Override
+        public Builder ip(IpAddress ip) {
+            this.ip = ip;
+            return this;
+        }
+
+        @Override
+        public Builder vlanId(VlanId vlanId) {
+            this.vlanId = vlanId;
+            return this;
+        }
+
+        @Override
+        public Builder addressPairs(Set<AddressPair> addressPairs) {
+            this.addressPairs = addressPairs;
+            return this;
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java b/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
deleted file mode 100644
index fa9c36a..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
+++ /dev/null
@@ -1,609 +0,0 @@
-/*
- * Copyright 2015-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 com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip4Prefix;
-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.onosproject.store.service.Versioned;
-import org.opencord.cordvtn.api.core.CordVtnAdminService;
-import org.opencord.cordvtn.api.node.CordVtnNode;
-import org.opencord.cordvtn.api.dependency.Dependency;
-import org.opencord.cordvtn.api.dependency.Dependency.Type;
-import org.opencord.cordvtn.api.dependency.DependencyService;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.onosproject.core.DefaultGroupId;
-import org.onosproject.core.GroupId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.group.DefaultGroupDescription;
-import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.Group;
-import org.onosproject.net.group.GroupBucket;
-import org.onosproject.net.group.GroupBuckets;
-import org.onosproject.net.group.GroupDescription;
-import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.GroupService;
-import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.ProviderNetwork;
-import org.opencord.cordvtn.api.net.SegmentId;
-import org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-import org.opencord.cordvtn.api.net.VtnNetworkEvent;
-import org.opencord.cordvtn.api.net.VtnNetworkListener;
-import org.opencord.cordvtn.impl.handler.AbstractInstanceHandler;
-import org.slf4j.Logger;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.BIDIRECTIONAL;
-import static org.opencord.cordvtn.api.net.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;
-
-/**
- * Provisions service dependency capabilities between network services.
- */
-@Component(immediate = true)
-@Service
-public class DependencyManager extends AbstractInstanceHandler implements DependencyService {
-
-    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(NetworkId subNetId, NetworkId proNetId, Type type) {
-        // FIXME this is not safe
-        VtnNetwork existing = vtnService.vtnNetwork(subNetId);
-        if (existing == null) {
-            log.warn("Failed to create dependency between {} and {}", subNetId, proNetId);
-            return;
-        }
-        vtnService.createServiceNetwork(existing);
-        VtnNetwork updated = VtnNetwork.builder(existing)
-                .addProvider(proNetId, type)
-                .build();
-        vtnService.updateServiceNetwork(updated);
-    }
-
-    @Override
-    public void removeDependency(NetworkId subNetId, NetworkId proNetId) {
-        // FIXME this is not safe
-        VtnNetwork subNet = vtnService.vtnNetwork(subNetId);
-        if (subNet == null) {
-            log.warn("No dependency exists between {} and {}", subNetId, proNetId);
-            return;
-        }
-        VtnNetwork updated = VtnNetwork.builder(subNet)
-                .delProvider(proNetId)
-                .build();
-        vtnService.updateServiceNetwork(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.netType() == ACCESS_AGENT) {
-            return;
-        }
-
-        VtnNetwork vtnNet = vtnService.vtnNetwork(instance.netId());
-        if (vtnNet == null) {
-            final String error = ERR_NET_FAIL + instance.netId();
-            throw new IllegalStateException(error);
-        }
-
-        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.netType() == ACCESS_AGENT) {
-            return;
-        }
-
-        VtnNetwork vtnNet = vtnService.vtnNetwork(instance.netId());
-        if (vtnNet == null) {
-            final String error = ERR_NET_FAIL + instance.netId();
-            throw new IllegalStateException(error);
-        }
-
-        if (!vtnNet.providers().isEmpty()) {
-            updateSubscriberInstances(vtnNet, instance, false);
-        }
-        // TODO check if subscribers on this network
-        updateProviderInstances(vtnNet);
-    }
-
-    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);
-        if (getSubscribers(dependency.provider().id()).isEmpty()) {
-            removeProviderGroup(dependency.provider().id());
-        }
-        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) {
-                continue;
-            }
-
-            List<GroupBucket> oldBuckets = group.buckets().buckets();
-            List<GroupBucket> newBuckets = getProviderGroupBuckets(
-                    deviceId,
-                    provider.segmentId().id(),
-                    getInstances(provider.id())).buckets();
-
-            if (oldBuckets.equals(newBuckets)) {
-                continue;
-            }
-
-            List<GroupBucket> bucketsToRemove = Lists.newArrayList(oldBuckets);
-            bucketsToRemove.removeAll(newBuckets);
-            if (!bucketsToRemove.isEmpty()) {
-                groupService.removeBucketsFromGroup(
-                        deviceId,
-                        groupKey,
-                        new GroupBuckets(bucketsToRemove),
-                        groupKey, appId);
-                log.debug("Removed buckets from provider({}) group on {}: {}",
-                          provider.id(), deviceId, bucketsToRemove);
-            }
-
-            List<GroupBucket> bucketsToAdd = Lists.newArrayList(newBuckets);
-            bucketsToAdd.removeAll(oldBuckets);
-            if (!bucketsToAdd.isEmpty()) {
-                groupService.addBucketsToGroup(
-                        deviceId,
-                        groupKey,
-                        new GroupBuckets(bucketsToAdd),
-                        groupKey, appId);
-                log.debug("Added buckets to provider({}) group on {}: {}",
-                          provider.id(), deviceId, bucketsToAdd);
-            }
-        }
-    }
-
-    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);
-
-            log.info(isAdded + "subscriber instance({}) for provider({})",
-                     instance.host().id(), provider.id());
-        });
-    }
-
-    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 = getProviderGroup(provider, deviceId);
-            providerGroups.put(deviceId, groupId);
-
-            Set<PortNumber> ports = getInstances(subscriber.id())
-                    .stream()
-                    .filter(instance -> instance.deviceId().equals(deviceId))
-                    .map(Instance::portNumber)
-                    .collect(Collectors.toSet());
-            subscriberPorts.put(deviceId, ports);
-        });
-
-        Ip4Prefix subscriberIp = subscriber.subnet().getIp4Prefix();
-        Ip4Prefix providerIp = provider.subnet().getIp4Prefix();
-
-        populateInPortRule(subscriberPorts, providerGroups, install);
-        populateIndirectAccessRule(
-                subscriberIp,
-                provider.serviceIp().getIp4Address(),
-                providerGroups,
-                install);
-        populateDirectAccessRule(subscriberIp, providerIp, install);
-        if (type == BIDIRECTIONAL) {
-            populateDirectAccessRule(providerIp, subscriberIp, install);
-        }
-    }
-
-    private void populateIndirectAccessRule(Ip4Prefix srcIp, Ip4Address serviceIp,
-                                            Map<DeviceId, GroupId> outGroups,
-                                            boolean install) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(srcIp)
-                .matchIPDst(serviceIp.toIpPrefix())
-                .build();
-
-        for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .group(outGroup.getValue())
-                    .build();
-
-            FlowRule flowRule = DefaultFlowRule.builder()
-                    .fromApp(appId)
-                    .withSelector(selector)
-                    .withTreatment(treatment)
-                    .withPriority(PRIORITY_HIGH)
-                    .forDevice(outGroup.getKey())
-                    .forTable(TABLE_ACCESS)
-                    .makePermanent()
-                    .build();
-
-            pipeline.processFlowRule(install, flowRule);
-        }
-    }
-
-    private void populateDirectAccessRule(Ip4Prefix srcIp, Ip4Prefix dstIp, boolean install) {
-        TrafficSelector selector = DefaultTrafficSelector.builder()
-                .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPSrc(srcIp)
-                .matchIPDst(dstIp)
-                .build();
-
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                .transition(TABLE_DST)
-                .build();
-
-        nodeManager.completeNodes().stream().forEach(node -> {
-            DeviceId deviceId = node.integrationBridgeId();
-            FlowRule flowRuleDirect = DefaultFlowRule.builder()
-                    .fromApp(appId)
-                    .withSelector(selector)
-                    .withTreatment(treatment)
-                    .withPriority(PRIORITY_DEFAULT)
-                    .forDevice(deviceId)
-                    .forTable(TABLE_ACCESS)
-                    .makePermanent()
-                    .build();
-
-            pipeline.processFlowRule(install, flowRuleDirect);
-        });
-    }
-
-    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 = providerGroups.get(deviceId);
-            if (groupId == null) {
-                continue;
-            }
-
-            ports.stream().forEach(port -> {
-                TrafficSelector selector = DefaultTrafficSelector.builder()
-                        .matchInPort(port)
-                        .build();
-
-                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                        .group(groupId)
-                        .build();
-
-                FlowRule flowRule = DefaultFlowRule.builder()
-                        .fromApp(appId)
-                        .withSelector(selector)
-                        .withTreatment(treatment)
-                        .withPriority(PRIORITY_DEFAULT)
-                        .forDevice(deviceId)
-                        .forTable(TABLE_IN_SERVICE)
-                        .makePermanent()
-                        .build();
-
-                pipeline.processFlowRule(install, flowRule);
-            });
-        }
-    }
-
-    private GroupId getGroupId(NetworkId netId, DeviceId deviceId) {
-        return new DefaultGroupId(Objects.hash(netId, deviceId));
-    }
-
-    private GroupKey getGroupKey(NetworkId netId) {
-        return new DefaultGroupKey(netId.id().getBytes());
-    }
-
-    private GroupId getProviderGroup(VtnNetwork provider, DeviceId deviceId) {
-        GroupKey groupKey = getGroupKey(provider.id());
-        Group group = groupService.getGroup(deviceId, groupKey);
-        GroupId groupId = getGroupId(provider.id(), deviceId);
-
-        if (group != null) {
-            return groupId;
-        }
-
-        GroupBuckets buckets = getProviderGroupBuckets(
-                deviceId, provider.segmentId().id(), getInstances(provider.id()));
-        GroupDescription groupDescription = new DefaultGroupDescription(
-                deviceId,
-                GroupDescription.Type.SELECT,
-                buckets,
-                groupKey,
-                groupId.id(),
-                appId);
-
-        groupService.addGroup(groupDescription);
-        return groupId;
-    }
-
-    private Set<Dependency> getSubscribers(NetworkId netId) {
-        return dependencyStore.values().stream().map(Versioned::value)
-                .flatMap(Collection::stream)
-                .filter(dependency -> dependency.provider().id().equals(netId))
-                .collect(Collectors.toSet());
-    }
-
-    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();
-
-            if (deviceId.equals(instance.deviceId())) {
-                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                        .setEthDst(instance.mac())
-                        .setOutput(instance.portNumber())
-                        .build();
-                buckets.add(createSelectGroupBucket(treatment));
-            } else {
-                ExtensionTreatment tunnelDst =
-                        pipeline.tunnelDstTreatment(deviceId, tunnelIp);
-                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                        .setEthDst(instance.mac())
-                        .extension(tunnelDst, deviceId)
-                        .setTunnelId(tunnelId)
-                        .setOutput(nodeManager.tunnelPort(instance.deviceId()))
-                        .build();
-                buckets.add(createSelectGroupBucket(treatment));
-            }
-        });
-        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.vtnNetwork(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);
-        }
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/impl/DistributedCordVtnStore.java b/src/main/java/org/opencord/cordvtn/impl/DistributedCordVtnStore.java
deleted file mode 100644
index beb6475..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/DistributedCordVtnStore.java
+++ /dev/null
@@ -1,528 +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 com.google.common.collect.ImmutableSet;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.util.Tools;
-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.core.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.net.AddressPair;
-import org.opencord.cordvtn.api.core.CordVtnStoreDelegate;
-import org.opencord.cordvtn.api.dependency.Dependency;
-import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.ProviderNetwork;
-import org.opencord.cordvtn.api.net.SegmentId;
-import org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType;
-import org.opencord.cordvtn.api.net.SubnetId;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-import org.opencord.cordvtn.api.net.VtnNetworkEvent;
-import org.opencord.cordvtn.api.net.VtnPort;
-import org.openstack4j.model.network.IP;
-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.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.concurrent.Executors.newSingleThreadExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.opencord.cordvtn.api.Constants.CORDVTN_APP_ID;
-import static org.opencord.cordvtn.api.net.VtnNetworkEvent.Type.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Manages the inventory of VTN networks using a {@code ConsistentMap}.
- */
-@Component(immediate = true)
-@Service
-public class DistributedCordVtnStore extends AbstractStore<VtnNetworkEvent, CordVtnStoreDelegate>
-        implements CordVtnStore {
-
-    protected final Logger log = getLogger(getClass());
-
-    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 clear() {
-        synchronized (this) {
-            vtnNetworkStore.clear();
-            vtnPortStore.clear();
-            networkStore.clear();
-            portStore.clear();
-            subnetStore.clear();
-        }
-    }
-
-    @Override
-    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 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 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 VtnNetwork vtnNetwork(NetworkId netId) {
-        Versioned<VtnNetwork> versioned = vtnNetworkStore.get(netId);
-        return versioned == null ? null : versioned.value();
-    }
-
-    @Override
-    public Set<VtnNetwork> vtnNetworks() {
-        Set<VtnNetwork> vtnNetworks = vtnNetworkStore.values().stream()
-                .map(Versioned::value)
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(vtnNetworks);
-    }
-
-    @Override
-    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 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 void removeVtnPort(PortId portId) {
-        vtnPortStore.remove(portId);
-    }
-
-    @Override
-    public VtnPort vtnPort(PortId portId) {
-        Versioned<VtnPort> versioned = vtnPortStore.get(portId);
-        return versioned == null ? null : versioned.value();
-    }
-
-    @Override
-    public Set<VtnPort> vtnPorts() {
-        Set<VtnPort> vtnPorts = vtnPortStore.values().stream()
-                .map(Versioned::value)
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(vtnPorts);
-    }
-
-    @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 || equalNetworks(net, 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 network(NetworkId netId) {
-        Versioned<Network> versioned = networkStore.get(netId);
-        return versioned == null ? null : versioned.value();
-    }
-
-    @Override
-    public Set<Network> networks() {
-        Set<Network> networks = networkStore.values().stream()
-                .map(Versioned::value)
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(networks);
-    }
-
-    @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 || equalPorts(port, 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 port(PortId portId) {
-        Versioned<Port> versioned = portStore.get(portId);
-        return versioned == null ? null : versioned.value();
-    }
-
-    @Override
-    public Set<Port> ports() {
-        Set<Port> ports = portStore.values().stream()
-                .map(Versioned::value)
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(ports);
-    }
-
-    @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 || equalSubnets(subnet, 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 subnet(SubnetId subnetId) {
-        Versioned<Subnet> versioned = subnetStore.get(subnetId);
-        return versioned == null ? null : versioned.value();
-    }
-
-    @Override
-    public Set<Subnet> subnets() {
-        Set<Subnet> subnets = subnetStore.values().stream()
-                .map(Versioned::value)
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(subnets);
-    }
-
-    private Set<VtnNetwork> getSubscribers(NetworkId netId) {
-        Set<VtnNetwork> subscribers = vtnNetworks().stream()
-                .filter(net -> net.isProvider(netId))
-                .collect(Collectors.toSet());
-        return ImmutableSet.copyOf(subscribers);
-    }
-
-    private boolean equalNetworks(Network netA, Network netB) {
-        if (netA == netB) {
-            return true;
-        }
-        // FIXME compare subnet here when CordVtnManager.createSubnet is fixed
-        if (Objects.equals(netA.getId(), netB.getId()) &&
-                Objects.equals(netA.getProviderSegID(), netB.getProviderSegID())) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean equalSubnets(Subnet subnetA, Subnet subnetB) {
-        if (subnetA == subnetB) {
-            return true;
-        }
-        if (Objects.equals(subnetA.getId(), subnetB.getId()) &&
-                Objects.equals(subnetA.getNetworkId(), subnetB.getNetworkId()) &&
-                Objects.equals(subnetA.getCidr(), subnetB.getCidr()) &&
-                Objects.equals(subnetA.getGateway(), subnetB.getGateway())) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean equalPorts(Port portA, Port portB) {
-        if (portA == portB) {
-            return true;
-        }
-
-        List<String> portAIps = Tools.stream(portA.getFixedIps())
-                .map(IP::getIpAddress)
-                .collect(Collectors.toList());
-        List<String> portBIps = Tools.stream(portB.getFixedIps())
-                .map(IP::getIpAddress)
-                .collect(Collectors.toList());
-
-        if (Objects.equals(portA.getId(), portB.getId()) &&
-                Objects.equals(portA.getNetworkId(), portB.getNetworkId()) &&
-                Objects.equals(portA.getMacAddress(), portB.getMacAddress()) &&
-                Objects.equals(portAIps, portBIps)) {
-            return true;
-        }
-        return false;
-    }
-
-    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,
-                                vtnNetwork(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,
-                                vtnNetwork(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,
-                                vtnNetwork(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/DistributedServiceNetworkStore.java b/src/main/java/org/opencord/cordvtn/impl/DistributedServiceNetworkStore.java
new file mode 100644
index 0000000..dc98ced
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/DistributedServiceNetworkStore.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.impl;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.MapEvent;
+import org.onosproject.store.service.MapEventListener;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.Versioned;
+import org.opencord.cordvtn.api.core.ServiceNetworkEvent;
+import org.opencord.cordvtn.api.core.ServiceNetworkStore;
+import org.opencord.cordvtn.api.core.ServiceNetworkStoreDelegate;
+import org.opencord.cordvtn.api.net.AddressPair;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.Provider;
+import org.opencord.cordvtn.api.net.SegmentId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
+import org.opencord.cordvtn.api.net.ServicePort;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Map;
+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.core.ServiceNetworkEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Manages the inventory of VTN networks using a {@code ConsistentMap}.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedServiceNetworkStore extends AbstractStore<ServiceNetworkEvent, ServiceNetworkStoreDelegate>
+        implements ServiceNetworkStore {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_DUPLICATE = " already exists";
+
+    private static final KryoNamespace SERIALIZER_SERVICE = KryoNamespace.newBuilder()
+            .register(KryoNamespaces.API)
+            .register(ServiceNetwork.class)
+            .register(DefaultServiceNetwork.class)
+            .register(NetworkId.class)
+            .register(SegmentId.class)
+            .register(ServiceNetwork.NetworkType.class)
+            .register(DependencyType.class)
+            .register(ServicePort.class)
+            .register(DefaultServicePort.class)
+            .register(PortId.class)
+            .register(AddressPair.class)
+            .register(Collections.EMPTY_MAP.getClass())
+            .register(Collections.EMPTY_SET.getClass())
+            .build();
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected StorageService storageService;
+
+   private final ExecutorService eventExecutor = newSingleThreadExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
+    private final MapEventListener<PortId, ServicePort> servicePortListener =
+            new ServicePortMapListener();
+    private final MapEventListener<NetworkId, ServiceNetwork> serviceNetworkListener =
+            new ServiceNetworkMapListener();
+
+    private ConsistentMap<NetworkId, ServiceNetwork> serviceNetworkStore;
+    private ConsistentMap<PortId, ServicePort> servicePortStore;
+
+    @Activate
+    protected void activate() {
+        ApplicationId appId = coreService.registerApplication(CORDVTN_APP_ID);
+
+        serviceNetworkStore = storageService.<NetworkId, ServiceNetwork>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_SERVICE))
+                .withName("cordvtn-servicenetstore")
+                .withApplicationId(appId)
+                .build();
+        serviceNetworkStore.addListener(serviceNetworkListener);
+
+        servicePortStore = storageService.<PortId, ServicePort>consistentMapBuilder()
+                .withSerializer(Serializer.using(SERIALIZER_SERVICE))
+                .withName("cordvtn-serviceportstore")
+                .withApplicationId(appId)
+                .build();
+        servicePortStore.addListener(servicePortListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        serviceNetworkStore.removeListener(serviceNetworkListener);
+        servicePortStore.removeListener(servicePortListener);
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void clear() {
+        synchronized (this) {
+            serviceNetworkStore.clear();
+            servicePortStore.clear();
+        }
+    }
+
+    @Override
+    public void createServiceNetwork(ServiceNetwork snet) {
+        serviceNetworkStore.compute(snet.id(), (id, existing) -> {
+            final String error = snet.name() + ERR_DUPLICATE;
+            checkArgument(existing == null || existing.equals(snet), error);
+            return snet;
+        });
+    }
+
+    @Override
+    public void updateServiceNetwork(ServiceNetwork snet) {
+        serviceNetworkStore.compute(snet.id(), (id, existing) -> {
+            final String error = snet.name() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return snet;
+        });
+    }
+
+    @Override
+    public ServiceNetwork removeServiceNetwork(NetworkId netId) {
+        synchronized (this) {
+            Versioned<ServiceNetwork> snet = serviceNetworkStore.remove(netId);
+            return snet == null ? null : snet.value();
+        }
+    }
+
+    @Override
+    public ServiceNetwork serviceNetwork(NetworkId netId) {
+        Versioned<ServiceNetwork> versioned = serviceNetworkStore.get(netId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<ServiceNetwork> serviceNetworks() {
+        Set<ServiceNetwork> snets = serviceNetworkStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(snets);
+    }
+
+    @Override
+    public void createServicePort(ServicePort sport) {
+        servicePortStore.compute(sport.id(), (id, existing) -> {
+            final String error = sport.id().id() + ERR_DUPLICATE;
+            checkArgument(existing == null || existing.equals(sport), error);
+            return sport;
+        });
+    }
+
+    @Override
+    public void updateServicePort(ServicePort sport) {
+        servicePortStore.compute(sport.id(), (id, existing) -> {
+            final String error = sport.id().id() + ERR_NOT_FOUND;
+            checkArgument(existing != null, error);
+            return sport;
+        });
+    }
+
+    @Override
+    public ServicePort removeServicePort(PortId portId) {
+        Versioned<ServicePort> sport = servicePortStore.remove(portId);
+        return sport.value();
+    }
+
+    @Override
+    public ServicePort servicePort(PortId portId) {
+        Versioned<ServicePort> versioned = servicePortStore.get(portId);
+        return versioned == null ? null : versioned.value();
+    }
+
+    @Override
+    public Set<ServicePort> servicePorts() {
+        Set<ServicePort> sports = servicePortStore.values().stream()
+                .map(Versioned::value)
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(sports);
+    }
+
+    private class ServiceNetworkMapListener implements MapEventListener<NetworkId, ServiceNetwork> {
+
+        @Override
+        public void event(MapEvent<NetworkId, ServiceNetwork> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("Service network updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_NETWORK_UPDATED,
+                                event.newValue().value()));
+                        notifyProviderUpdate(
+                                event.oldValue().value(),
+                                event.newValue().value());
+                    });
+                    break;
+                case INSERT:
+                    log.debug("Service network created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_NETWORK_CREATED,
+                                event.newValue().value()));
+                        notifyProviderUpdate(null, event.newValue().value());
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("Service network removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyProviderUpdate(event.oldValue().value(), null);
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_NETWORK_REMOVED,
+                                event.oldValue().value()));
+                    });
+                    break;
+                default:
+                    log.error("Unsupported event type");
+                    break;
+            }
+        }
+
+        private void notifyProviderUpdate(ServiceNetwork oldValue, ServiceNetwork newValue) {
+            Map<NetworkId, DependencyType> oldp =
+                    oldValue != null ? oldValue.providers() : ImmutableMap.of();
+            Map<NetworkId, DependencyType> newp =
+                    newValue != null ? newValue.providers() : ImmutableMap.of();
+
+            oldp.entrySet().stream().filter(p -> !newp.keySet().contains(p.getKey()))
+                    .forEach(p -> {
+                        Provider providerNet = Provider.builder()
+                                .provider(serviceNetwork(p.getKey()))
+                                .type(p.getValue())
+                                .build();
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_NETWORK_PROVIDER_REMOVED,
+                                oldValue,
+                                providerNet
+                        ));
+            });
+
+            newp.entrySet().stream().filter(p -> !oldp.keySet().contains(p.getKey()))
+                    .forEach(p -> {
+                        Provider providerNet = Provider.builder()
+                                .provider(serviceNetwork(p.getKey()))
+                                .type(p.getValue())
+                                .build();
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_NETWORK_PROVIDER_ADDED,
+                                newValue,
+                                providerNet));
+                    });
+        }
+    }
+
+    private class ServicePortMapListener implements MapEventListener<PortId, ServicePort> {
+
+        @Override
+        public void event(MapEvent<PortId, ServicePort> event) {
+            switch (event.type()) {
+                case UPDATE:
+                    log.debug("Service port updated {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_PORT_UPDATED,
+                                serviceNetwork(event.newValue().value().networkId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case INSERT:
+                    log.debug("Service port created {}", event.newValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_PORT_CREATED,
+                                serviceNetwork(event.newValue().value().networkId()),
+                                event.newValue().value()));
+                    });
+                    break;
+                case REMOVE:
+                    log.debug("Service port removed {}", event.oldValue());
+                    eventExecutor.execute(() -> {
+                        notifyDelegate(new ServiceNetworkEvent(
+                                SERVICE_PORT_REMOVED,
+                                serviceNetwork(event.oldValue().value().networkId()),
+                                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 4d68b8d..2011342 100644
--- a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
@@ -45,14 +45,14 @@
 import org.onosproject.net.provider.ProviderId;
 import org.opencord.cordconfig.CordConfigService;
 import org.opencord.cordconfig.access.AccessAgentData;
-import org.opencord.cordvtn.api.core.CordVtnService;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.instance.InstanceService;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.InstanceService;
+import org.opencord.cordvtn.api.core.ServiceNetworkEvent;
+import org.opencord.cordvtn.api.core.ServiceNetworkListener;
+import org.opencord.cordvtn.api.core.ServiceNetworkService;
 import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-import org.opencord.cordvtn.api.net.VtnNetworkEvent;
-import org.opencord.cordvtn.api.net.VtnNetworkListener;
-import org.opencord.cordvtn.api.net.VtnPort;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
 import org.slf4j.Logger;
 
 import java.util.Objects;
@@ -64,7 +64,7 @@
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.opencord.cordvtn.api.Constants.CORDVTN_APP_ID;
 import static org.opencord.cordvtn.api.Constants.NOT_APPLICABLE;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.ACCESS_AGENT;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.ACCESS_AGENT;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -76,8 +76,8 @@
         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";
+    private static final String ERR_SERVICE_NETWORK = "Failed to get service network for %s";
+    private static final String ERR_SERVICE_PORT = "Failed to get service port for %s";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
@@ -102,11 +102,11 @@
     protected CordConfigService cordConfig;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CordVtnService vtnService;
+    protected ServiceNetworkService snetService;
 
     private final ExecutorService eventExecutor =
             newSingleThreadExecutor(groupedThreads(this.getClass().getSimpleName(), "event-handler"));
-    private final VtnNetworkListener vtnNetListener = new InternalVtnNetworkListener();
+    private final ServiceNetworkListener snetListener = new InternalServiceNetworkListener();
 
     private ApplicationId appId;
     private NodeId localNodeId;
@@ -126,14 +126,14 @@
         leadershipService.runForLeadership(appId.name());
 
         hostProvider = hostProviderRegistry.register(this);
-        vtnService.addListener(vtnNetListener);
+        snetService.addListener(snetListener);
 
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
-        vtnService.removeListener(vtnNetListener);
+        snetService.removeListener(snetListener);
         hostProviderRegistry.unregister(this);
         eventExecutor.shutdown();
         leadershipService.withdraw(appId.name());
@@ -164,34 +164,35 @@
             return;
         }
 
-        VtnPort vtnPort = vtnService.vtnPort(port.annotations().value(PORT_NAME));
-        if (vtnPort == null) {
-            log.warn(String.format(ERR_VTN_PORT, port));
+        ServicePort sport = getServicePortByPortName(port.annotations().value(PORT_NAME));
+        if (sport == null) {
+            log.warn(String.format(ERR_SERVICE_PORT, port));
             return;
         }
 
-        VtnNetwork vtnNet = vtnService.vtnNetwork(vtnPort.netId());
-        if (vtnNet == null) {
-            log.warn(String.format(ERR_VTN_NETWORK, vtnPort));
-            return;
+        ServiceNetwork snet = snetService.serviceNetwork(sport.networkId());
+        if (snet == null) {
+            final String error = String.format(ERR_SERVICE_NETWORK, sport);
+            throw new IllegalStateException(error);
         }
 
         // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
-        // existing instances.
+        // existing instances. Fix this after adding a method to update/reinstall
+        // flow rules for the existing service instances.
         DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                .set(Instance.NETWORK_TYPE, vtnNet.type().name())
-                .set(Instance.NETWORK_ID, vtnNet.id().id())
-                .set(Instance.PORT_ID, vtnPort.id().id())
+                .set(Instance.NETWORK_TYPE, snet.type().name())
+                .set(Instance.NETWORK_ID, snet.id().id())
+                .set(Instance.PORT_ID, sport.id().id())
                 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
 
         HostDescription hostDesc = new DefaultHostDescription(
-                vtnPort.mac(),
+                sport.mac(),
                 VlanId.NONE,
                 new HostLocation(connectPoint, System.currentTimeMillis()),
-                Sets.newHashSet(vtnPort.ip()),
+                Sets.newHashSet(sport.ip()),
                 annotations.build());
 
-        HostId hostId = HostId.hostId(vtnPort.mac());
+        HostId hostId = HostId.hostId(sport.mac());
         hostProvider.hostDetected(hostId, hostDesc, false);
     }
 
@@ -225,6 +226,14 @@
         hostProvider.hostVanished(hostId);
     }
 
+    private ServicePort getServicePortByPortName(String portName) {
+        Optional<ServicePort> sport = snetService.servicePorts()
+                .stream()
+                .filter(p -> p.id().id().contains(portName.substring(3)))
+                .findFirst();
+        return sport.isPresent() ? sport.get() : null;
+    }
+
     // TODO remove this when XOS provides access agent information
     private boolean isAccessAgent(ConnectPoint connectPoint) {
         Optional<AccessAgentData> agent = cordConfig.getAccessAgent(connectPoint.deviceId());
@@ -255,22 +264,23 @@
     }
 
     private Instance getInstance(PortId portId) {
-        VtnPort vtnPort = vtnService.vtnPort(portId);
-        if (vtnPort == null) {
-            final String error = "Failed to build VTN port for " + portId.id();
+        // TODO use instance service instead
+        ServicePort sport = snetService.servicePort(portId);
+        if (sport == null) {
+            final String error = String.format(ERR_SERVICE_PORT, portId);
             throw new IllegalStateException(error);
         }
-        Host host = hostService.getHost(HostId.hostId(vtnPort.mac()));
+        Host host = hostService.getHost(HostId.hostId(sport.mac()));
         if (host == null) {
             return null;
         }
         return Instance.of(host);
     }
 
-    private class InternalVtnNetworkListener implements VtnNetworkListener {
+    private class InternalServiceNetworkListener implements ServiceNetworkListener {
 
         @Override
-        public void event(VtnNetworkEvent event) {
+        public void event(ServiceNetworkEvent event) {
             NodeId leader = leadershipService.getLeader(appId.name());
             if (!Objects.equals(localNodeId, leader)) {
                 // do not allow to proceed without leadership
@@ -278,10 +288,10 @@
             }
 
             switch (event.type()) {
-                case VTN_PORT_CREATED:
-                case VTN_PORT_UPDATED:
-                    log.debug("Processing service port {}", event.vtnPort());
-                    PortId portId = event.vtnPort().id();
+                case SERVICE_PORT_CREATED:
+                case SERVICE_PORT_UPDATED:
+                    log.debug("Processing service port {}", event.servicePort());
+                    PortId portId = event.servicePort().id();
                     eventExecutor.execute(() -> {
                         Instance instance = getInstance(portId);
                         if (instance != null) {
@@ -289,10 +299,10 @@
                         }
                     });
                     break;
-                case VTN_PORT_REMOVED:
-                case VTN_NETWORK_CREATED:
-                case VTN_NETWORK_UPDATED:
-                case VTN_NETWORK_REMOVED:
+                case SERVICE_PORT_REMOVED:
+                case SERVICE_NETWORK_CREATED:
+                case SERVICE_NETWORK_UPDATED:
+                case SERVICE_NETWORK_REMOVED:
                 default:
                     // do nothing for the other events
                     break;
diff --git a/src/main/java/org/opencord/cordvtn/impl/RemoteIpCommandUtil.java b/src/main/java/org/opencord/cordvtn/impl/RemoteIpCommandUtil.java
index e357a27..0821e83 100644
--- a/src/main/java/org/opencord/cordvtn/impl/RemoteIpCommandUtil.java
+++ b/src/main/java/org/opencord/cordvtn/impl/RemoteIpCommandUtil.java
@@ -23,7 +23,7 @@
 import com.jcraft.jsch.JSchException;
 import com.jcraft.jsch.Session;
 import org.onlab.packet.IpAddress;
-import org.opencord.cordvtn.api.node.NetworkAddress;
+import org.opencord.cordvtn.api.net.CidrAddr;
 import org.opencord.cordvtn.api.node.SshAccessInfo;
 import org.slf4j.Logger;
 
@@ -76,7 +76,7 @@
      * @param device device name to assign the ip address
      * @return true if the command succeeds, or false
      */
-    public static boolean addIp(Session session, NetworkAddress ip, String device) {
+    public static boolean addIp(Session session, CidrAddr ip, String device) {
         if (session == null || !session.isConnected()) {
             return false;
         }
diff --git a/src/main/java/org/opencord/cordvtn/impl/ServiceNetworkManager.java b/src/main/java/org/opencord/cordvtn/impl/ServiceNetworkManager.java
new file mode 100644
index 0000000..aaa74e5
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/ServiceNetworkManager.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.net.host.HostService;
+import org.opencord.cordvtn.api.Constants;
+import org.opencord.cordvtn.api.CordVtnConfig;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
+import org.opencord.cordvtn.api.core.ServiceNetworkEvent;
+import org.opencord.cordvtn.api.core.ServiceNetworkListener;
+import org.opencord.cordvtn.api.core.ServiceNetworkService;
+import org.opencord.cordvtn.api.core.ServiceNetworkStore;
+import org.opencord.cordvtn.api.core.ServiceNetworkStoreDelegate;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
+import org.opencord.cordvtn.api.net.ServicePort;
+import org.slf4j.Logger;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides implementation of administering and interfacing service network and port.
+ */
+@Component(immediate = true)
+@Service
+public class ServiceNetworkManager extends ListenerRegistry<ServiceNetworkEvent, ServiceNetworkListener>
+        implements ServiceNetworkAdminService, ServiceNetworkService {
+
+    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 MSG_PROVIDER_NET = "Provider network %s %s";
+    private static final String MSG_CREATED = "created";
+    private static final String MSG_UPDATED = "updated";
+    private static final String MSG_REMOVED = "removed";
+
+    private static final String ERR_NULL_SERVICE_NET  = "Service network cannot be null";
+    private static final String ERR_NULL_SERVICE_NET_ID  = "Service network ID cannot be null";
+    private static final String ERR_NULL_SERVICE_NET_TYPE  = "Service network type cannot be null";
+    private static final String ERR_NULL_SERVICE_PORT = "Service port cannot be null";
+    private static final String ERR_NULL_SERVICE_PORT_ID = "Service port ID cannot be null";
+    private static final String ERR_NULL_SERVICE_PORT_NET_ID = "Service port network ID cannot be null";
+
+    private static final String ERR_NOT_FOUND = " does not exist";
+    private static final String ERR_IN_USE = " still in use";
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ServiceNetworkStore snetStore;
+
+    // TODO add cordvtn config service and move this
+    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 ServiceNetworkStoreDelegate delegate = new InternalServiceNetworkStoreDelegate();
+
+    @Activate
+    protected void activate() {
+        coreService.registerApplication(Constants.CORDVTN_APP_ID);
+        configRegistry.registerConfigFactory(configFactory);
+        snetStore.setDelegate(delegate);
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        configRegistry.unregisterConfigFactory(configFactory);
+        snetStore.unsetDelegate(delegate);
+        log.info("Stopped");
+    }
+
+    @Override
+    public void purgeStates() {
+        snetStore.clear();
+    }
+
+    @Override
+    public ServiceNetwork serviceNetwork(NetworkId netId) {
+        checkNotNull(netId, ERR_NULL_SERVICE_NET_ID);
+        return snetStore.serviceNetwork(netId);
+    }
+
+    @Override
+    public Set<ServiceNetwork> serviceNetworks() {
+        return snetStore.serviceNetworks();
+    }
+
+    @Override
+    public void createServiceNetwork(ServiceNetwork snet) {
+        checkNotNull(snet, ERR_NULL_SERVICE_NET);
+        checkNotNull(snet.id(), ERR_NULL_SERVICE_NET_ID);
+        checkNotNull(snet.type(), ERR_NULL_SERVICE_NET_TYPE);
+        synchronized (this) {
+            snet.providers().keySet().forEach(provider -> {
+                if (snetStore.serviceNetwork(provider) == null) {
+                    final String error = String.format(
+                            MSG_PROVIDER_NET, provider.id(), ERR_NOT_FOUND);
+                    throw new IllegalStateException(error);
+                }
+            });
+            snetStore.createServiceNetwork(snet);
+            log.info(String.format(MSG_SERVICE_NET, snet.name(), MSG_CREATED));
+        }
+    }
+
+    @Override
+    public void updateServiceNetwork(ServiceNetwork snet) {
+        checkNotNull(snet, ERR_NULL_SERVICE_NET);
+        checkNotNull(snet.id(), ERR_NULL_SERVICE_NET_ID);
+        synchronized (this) {
+            ServiceNetwork existing = snetStore.serviceNetwork(snet.id());
+            if (existing == null) {
+                final String error = String.format(
+                        MSG_SERVICE_NET, snet.id(), ERR_NOT_FOUND);
+                throw new IllegalStateException(error);
+            }
+            // TODO do not allow service type update if the network in use
+            snetStore.updateServiceNetwork(DefaultServiceNetwork.builder(existing, snet).build());
+            log.info(String.format(MSG_SERVICE_NET, existing.name(), MSG_UPDATED));
+        }
+    }
+
+    @Override
+    public void removeServiceNetwork(NetworkId netId) {
+        checkNotNull(netId, ERR_NULL_SERVICE_NET_ID);
+        synchronized (this) {
+            if (isNetworkInUse(netId)) {
+                final String error = String.format(MSG_SERVICE_NET, netId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            // remove dependencies on this network first
+            serviceNetworks().stream().filter(n -> isProvider(n, netId)).forEach(n -> {
+                Map<NetworkId, DependencyType> newProviders = Maps.newHashMap(n.providers());
+                newProviders.remove(netId);
+                ServiceNetwork updated = DefaultServiceNetwork.builder(n)
+                        .providers(newProviders)
+                        .build();
+                snetStore.updateServiceNetwork(updated);
+            });
+            ServiceNetwork snet = snetStore.removeServiceNetwork(netId);
+            log.info(String.format(MSG_SERVICE_NET, snet.name(), MSG_REMOVED));
+        }
+    }
+
+    @Override
+    public ServicePort servicePort(PortId portId) {
+        checkNotNull(portId, ERR_NULL_SERVICE_PORT_ID);
+        return snetStore.servicePort(portId);
+    }
+
+    @Override
+    public Set<ServicePort> servicePorts() {
+        return snetStore.servicePorts();
+    }
+
+    @Override
+    public Set<ServicePort> servicePorts(NetworkId netId) {
+        Set<ServicePort> sports = snetStore.servicePorts().stream()
+                .filter(p -> Objects.equals(p.networkId(), netId))
+                .collect(Collectors.toSet());
+        return ImmutableSet.copyOf(sports);
+    }
+
+    @Override
+    public void createServicePort(ServicePort sport) {
+        checkNotNull(sport, ERR_NULL_SERVICE_PORT);
+        checkNotNull(sport.id(), ERR_NULL_SERVICE_PORT_ID);
+        checkNotNull(sport.networkId(), ERR_NULL_SERVICE_PORT_NET_ID);
+        synchronized (this) {
+            ServiceNetwork existing = snetStore.serviceNetwork(sport.networkId());
+            if (existing == null) {
+                final String error = String.format(
+                        MSG_SERVICE_NET, sport.networkId(), ERR_NOT_FOUND);
+                throw new IllegalStateException(error);
+            }
+            snetStore.createServicePort(sport);
+            log.info(String.format(MSG_SERVICE_PORT, sport.id(), MSG_CREATED));
+        }
+    }
+
+    @Override
+    public void updateServicePort(ServicePort sport) {
+        checkNotNull(sport, ERR_NULL_SERVICE_PORT);
+        checkNotNull(sport.id(), ERR_NULL_SERVICE_PORT_ID);
+        synchronized (this) {
+            ServicePort existing = snetStore.servicePort(sport.id());
+            if (existing == null) {
+                final String error = String.format(
+                        MSG_SERVICE_PORT, sport.id(), ERR_NOT_FOUND);
+                throw new IllegalStateException(error);
+            }
+            snetStore.updateServicePort(DefaultServicePort.builder(existing, sport).build());
+            log.info(String.format(MSG_SERVICE_PORT, sport.id(), MSG_UPDATED));
+        }
+    }
+
+    @Override
+    public void removeServicePort(PortId portId) {
+        checkNotNull(portId, ERR_NULL_SERVICE_PORT_ID);
+        synchronized (this) {
+            if (isPortInUse(portId)) {
+                final String error = String.format(MSG_SERVICE_PORT, portId, ERR_IN_USE);
+                throw new IllegalStateException(error);
+            }
+            snetStore.removeServicePort(portId);
+            log.info(String.format(MSG_SERVICE_PORT, portId, MSG_REMOVED));
+        }
+    }
+
+    /**
+     * Returns if the given target network is a provider of the given network.
+     *
+     * @param snet     service network
+     * @param targetId target network
+     * @return true if the service network is a provider of the target network
+     */
+    private boolean isProvider(ServiceNetwork snet, NetworkId targetId) {
+        checkNotNull(snet);
+        return snet.providers().keySet().contains(targetId);
+    }
+
+    private boolean isNetworkInUse(NetworkId netId) {
+        // TODO use instance service to see if there's running instance for the network
+        return !servicePorts(netId).isEmpty();
+    }
+
+    private boolean isPortInUse(PortId portId) {
+        ServicePort sport = servicePort(portId);
+        if (sport == null) {
+            final String error = String.format(MSG_SERVICE_PORT, portId, ERR_NOT_FOUND);
+            throw new IllegalStateException(error);
+        }
+        // TODO use instance service to see if there's running instance for the port
+        Host host = hostService.getHost(HostId.hostId(sport.mac()));
+        return host != null;
+    }
+
+    private class InternalServiceNetworkStoreDelegate implements ServiceNetworkStoreDelegate {
+
+        @Override
+        public void notify(ServiceNetworkEvent event) {
+            if (event != null) {
+                log.trace("send service network event {}", event);
+                process(event);
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/impl/external/OpenStackNetworking.java b/src/main/java/org/opencord/cordvtn/impl/external/OpenStackNetworking.java
deleted file mode 100644
index 59a080d..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/external/OpenStackNetworking.java
+++ /dev/null
@@ -1,225 +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.external;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableSet;
-import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.NetworkService;
-import org.opencord.cordvtn.api.net.PortId;
-import org.opencord.cordvtn.api.net.SubnetId;
-import org.openstack4j.api.OSClient;
-import org.openstack4j.api.exceptions.AuthenticationException;
-import org.openstack4j.model.identity.Access;
-import org.openstack4j.model.network.Network;
-import org.openstack4j.model.network.Port;
-import org.openstack4j.model.network.Subnet;
-import org.openstack4j.openstack.OSFactory;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Implementation of {@link NetworkService} with OpenStack networking service.
- */
-public final class OpenStackNetworking implements NetworkService {
-
-    protected final Logger log = LoggerFactory.getLogger(getClass());
-
-    private static final String ERR_AUTH = "OpenStack authentication failure";
-
-    private final String endpoint;
-    private final String tenant;
-    private final String user;
-    private final String password;
-    private final Access osAcess;
-
-    private OpenStackNetworking(String endpoint,
-                                String tenant,
-                                String user,
-                                String password) {
-        this.endpoint = endpoint;
-        this.tenant = tenant;
-        this.user = user;
-        this.password = password;
-
-        try {
-            this.osAcess = OSFactory.builder()
-                    .endpoint(this.endpoint)
-                    .tenantName(this.tenant)
-                    .credentials(this.user, this.password)
-                    .authenticate()
-                    .getAccess();
-        } catch (AuthenticationException e) {
-            throw new IllegalArgumentException(ERR_AUTH);
-        }
-    }
-
-    @Override
-    public Network network(NetworkId netId) {
-        OSClient osClient = OSFactory.clientFromAccess(osAcess);
-        return osClient.networking().network().get(netId.id());
-    }
-
-    @Override
-    public Set<Network> networks() {
-        OSClient osClient = OSFactory.clientFromAccess(osAcess);
-        return ImmutableSet.copyOf(osClient.networking().network().list());
-    }
-
-    @Override
-    public Port port(PortId portId) {
-        OSClient osClient = OSFactory.clientFromAccess(osAcess);
-        return osClient.networking().port().get(portId.id());
-    }
-
-    @Override
-    public Set<Port> ports() {
-        OSClient osClient = OSFactory.clientFromAccess(osAcess);
-        return ImmutableSet.copyOf(osClient.networking().port().list());
-    }
-
-    @Override
-    public Subnet subnet(SubnetId subnetId) {
-        OSClient osClient = OSFactory.clientFromAccess(osAcess);
-        return osClient.networking().subnet().get(subnetId.id());
-    }
-
-    @Override
-    public Set<Subnet> subnets() {
-        OSClient osClient = OSFactory.clientFromAccess(osAcess);
-        return ImmutableSet.copyOf(osClient.networking().subnet().list());
-    }
-
-    /**
-     * Returns endpoint url for the OpenStack networking API access.
-     *
-     * @return endpoint url as a string
-     */
-    public String endpoint() {
-        return endpoint;
-    }
-
-    /**
-     * Returns tenant for the OpenStack networking API access.
-     *
-     * @return tenant name as a string
-     */
-    public String tenant() {
-        return tenant;
-    }
-
-    /**
-     * Returns user name for the OpenStack networking API access.
-     *
-     * @return user name as a string
-     */
-    public String user() {
-        return this.user;
-    }
-
-    /**
-     * Returns password for the OpenStack networking API access.
-     *
-     * @return password as a string
-     */
-    public String password() {
-        return this.password;
-    }
-
-    /**
-     * Returns new OpenStack networking builder instance.
-     * @return openstack networking builder instance
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
-     * Builder of the OpenStack networking entities.
-     */
-    public static final class Builder {
-
-        private String endpoint;
-        private String tenant;
-        private String user;
-        private String password;
-
-        private Builder() {
-        }
-
-        /**
-         * Builds immutable OpenStack networking instance.
-         *
-         * @return openstack networking instance
-         */
-        public OpenStackNetworking build() {
-            checkArgument(!Strings.isNullOrEmpty(endpoint));
-            checkArgument(!Strings.isNullOrEmpty(tenant));
-            checkArgument(!Strings.isNullOrEmpty(user));
-            checkArgument(!Strings.isNullOrEmpty(password));
-
-            return new OpenStackNetworking(endpoint, tenant, user, password);
-        }
-
-        /**
-         * Returns OpenStack networking builder with the supplied endpoint.
-         *
-         * @param endpoint endpoint url as a string
-         * @return openstack networking builder
-         */
-        public Builder endpoint(String endpoint) {
-            this.endpoint = endpoint;
-            return this;
-        }
-
-        /**
-         * Returns OpenStack networking builder with the supplied tenant name.
-         *
-         * @param tenant tenant name as a string
-         * @return openstack networking builder
-         */
-        public Builder tenant(String tenant) {
-            this.tenant = tenant;
-            return this;
-        }
-
-        /**
-         * Returns OpenStack networking builder with the supplied user name.
-         *
-         * @param user user name as a string
-         * @return openstack networking builder
-         */
-        public Builder user(String user) {
-            this.user = user;
-            return this;
-        }
-
-        /**
-         * Returns OpenStack networking builder with the supplied password.
-         *
-         * @param password password as a string
-         * @return openstack networking builder
-         */
-        public Builder password(String password) {
-            this.password = password;
-            return this;
-        }
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/impl/external/package-info.java b/src/main/java/org/opencord/cordvtn/impl/external/package-info.java
deleted file mode 100644
index a096589..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/external/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2015-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.
- */
-
-/**
- * Implementation of NetworkService and ServiceNetworkService for external services.
- */
-package org.opencord.cordvtn.impl.external;
\ No newline at end of file
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
index 01ccb91..0471ee6 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/AbstractInstanceHandler.java
@@ -18,6 +18,7 @@
 import com.google.common.collect.ImmutableSet;
 import org.onlab.osgi.DefaultServiceDirectory;
 import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.Tools;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.mastership.MastershipService;
@@ -26,20 +27,18 @@
 import org.onosproject.net.host.HostListener;
 import org.onosproject.net.host.HostService;
 import org.opencord.cordvtn.api.Constants;
-import org.opencord.cordvtn.api.core.CordVtnService;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.instance.InstanceHandler;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.InstanceHandler;
+import org.opencord.cordvtn.api.core.ServiceNetworkService;
 import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType;
-import org.opencord.cordvtn.api.net.VtnNetwork;
-import org.opencord.cordvtn.api.net.VtnPort;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServicePort;
 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;
@@ -58,9 +57,9 @@
     protected CoreService coreService;
     protected MastershipService mastershipService;
     protected HostService hostService;
-    protected CordVtnService vtnService;
+    protected ServiceNetworkService snetService;
     protected ApplicationId appId;
-    protected Set<ServiceNetworkType> netTypes = ImmutableSet.of();
+    protected Set<ServiceNetwork.NetworkType> netTypes = ImmutableSet.of();
 
     protected HostListener hostListener = new InternalHostListener();
 
@@ -72,7 +71,7 @@
         coreService = services.get(CoreService.class);
         mastershipService = services.get(MastershipService.class);
         hostService = services.get(HostService.class);
-        vtnService = services.get(CordVtnService.class);
+        snetService = services.get(ServiceNetworkService.class);
 
         appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
         hostService.addListener(hostListener);
@@ -93,7 +92,7 @@
     }
 
     protected Set<Instance> getInstances(NetworkId netId) {
-        return StreamSupport.stream(hostService.getHosts().spliterator(), false)
+        return Tools.stream(hostService.getHosts())
                 .filter(host -> Objects.equals(
                         netId.id(),
                         host.annotations().value(Instance.NETWORK_ID)))
@@ -101,8 +100,8 @@
                 .collect(Collectors.toSet());
     }
 
-    protected VtnNetwork getVtnNetwork(Instance instance) {
-        VtnNetwork vtnNet = vtnService.vtnNetwork(instance.netId());
+    protected ServiceNetwork getServiceNetwork(Instance instance) {
+        ServiceNetwork vtnNet = snetService.serviceNetwork(instance.netId());
         if (vtnNet == null) {
             final String error = String.format(ERR_VTN_NETWORK, instance);
             throw new IllegalStateException(error);
@@ -110,8 +109,8 @@
         return vtnNet;
     }
 
-    protected VtnPort getVtnPort(Instance instance) {
-        VtnPort vtnPort = vtnService.vtnPort(instance.portId());
+    protected ServicePort getServicePort(Instance instance) {
+        ServicePort vtnPort = snetService.servicePort(instance.portId());
         if (vtnPort == null) {
             final String error = String.format(ERR_VTN_PORT, instance);
             throw new IllegalStateException(error);
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 aee4d13..460ba25 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/AccessAgentInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/AccessAgentInstanceHandler.java
@@ -28,12 +28,12 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.instance.InstanceHandler;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.InstanceHandler;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.ACCESS_AGENT;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.ACCESS_AGENT;
 
 /**
  * Provides network connectivity for access agent instances.
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 c33bb78..1684ce4 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
@@ -18,7 +18,6 @@
 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;
@@ -32,16 +31,14 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.flow.instructions.ExtensionTreatment;
-import org.opencord.cordvtn.api.net.VtnNetwork;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.InstanceHandler;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
 import org.opencord.cordvtn.api.node.CordVtnNode;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.instance.InstanceHandler;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.PRIVATE;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.PUBLIC;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.VSG;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.*;
 
 /**
  * Provides network connectivity for default service instances.
@@ -73,8 +70,8 @@
         }
         log.info("Instance is detected {}", instance);
 
-        VtnNetwork vtnNet = getVtnNetwork(instance);
-        populateDefaultRules(instance, vtnNet, true);
+        ServiceNetwork snet = getServiceNetwork(instance);
+        populateDefaultRules(instance, snet, true);
     }
 
     @Override
@@ -84,13 +81,13 @@
         }
         log.info("Instance is removed {}", instance);
 
-        VtnNetwork vtnNet = getVtnNetwork(instance);
-        populateDefaultRules(instance, vtnNet, false);
+        ServiceNetwork snet = getServiceNetwork(instance);
+        populateDefaultRules(instance, snet, false);
     }
 
-    private void populateDefaultRules(Instance instance, VtnNetwork vtnNet, boolean install) {
-        long vni = vtnNet.segmentId().id();
-        Ip4Prefix serviceIpRange = vtnNet.subnet().getIp4Prefix();
+    private void populateDefaultRules(Instance instance, ServiceNetwork snet, boolean install) {
+        long vni = snet.segmentId().id();
+        Ip4Prefix serviceIpRange = snet.subnet().getIp4Prefix();
 
         populateInPortRule(instance, install);
         populateDstIpRule(instance, vni, install);
@@ -99,7 +96,7 @@
         if (install) {
             populateDirectAccessRule(serviceIpRange, serviceIpRange, true);
             populateServiceIsolationRule(serviceIpRange, true);
-        } else if (getInstances(vtnNet.id()).isEmpty()) {
+        } else if (getInstances(snet.id()).isEmpty()) {
             populateDirectAccessRule(serviceIpRange, serviceIpRange, false);
             populateServiceIsolationRule(serviceIpRange, false);
         }
diff --git a/src/main/java/org/opencord/cordvtn/impl/handler/DependencyHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/DependencyHandler.java
new file mode 100644
index 0000000..684bc6e
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/DependencyHandler.java
@@ -0,0 +1,481 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.opencord.cordvtn.impl.handler;
+
+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 org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.group.DefaultGroupDescription;
+import org.onosproject.net.group.DefaultGroupKey;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupService;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
+import org.opencord.cordvtn.api.core.ServiceNetworkEvent;
+import org.opencord.cordvtn.api.core.ServiceNetworkListener;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
+import org.opencord.cordvtn.api.node.CordVtnNode;
+import org.opencord.cordvtn.impl.CordVtnNodeManager;
+import org.opencord.cordvtn.impl.CordVtnPipeline;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.*;
+import static org.opencord.cordvtn.impl.CordVtnPipeline.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provisions service dependencies between service networks.
+ */
+@Component(immediate = true)
+@Service
+public class DependencyHandler extends AbstractInstanceHandler {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final String ERR_NET_FAIL = "Failed to get VTN network ";
+    private static final String ADDED = "Added ";
+    private static final String REMOVED = "Removed ";
+
+    @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 CordVtnPipeline pipeline;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnNodeManager nodeManager;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ServiceNetworkAdminService snetService;
+
+    private final ServiceNetworkListener snetListener = new InternalServiceNetworkListener();
+    private NodeId localNodeId;
+
+    @Activate
+    protected void activate() {
+        netTypes = ImmutableSet.of(PRIVATE, PUBLIC, VSG);
+        super.activate();
+        localNodeId = clusterService.getLocalNode().id();
+        leadershipService.runForLeadership(appId.name());
+        snetService.addListener(snetListener);
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        super.deactivate();
+        snetService.removeListener(snetListener);
+        leadershipService.withdraw(appId.name());
+    }
+
+    @Override
+    public void instanceDetected(Instance instance) {
+        ServiceNetwork snet = snetService.serviceNetwork(instance.netId());
+        if (snet == null) {
+            final String error = ERR_NET_FAIL + instance.netId();
+            throw new IllegalStateException(error);
+        }
+        if (!snet.providers().isEmpty()) {
+            updateSubscriberInstances(snet, instance, true);
+        }
+        // TODO check if subscribers on this network
+        updateProviderInstances(snet);
+    }
+
+    @Override
+    public void instanceRemoved(Instance instance) {
+        ServiceNetwork snet = snetService.serviceNetwork(instance.netId());
+        if (snet == null) {
+            final String error = ERR_NET_FAIL + instance.netId();
+            throw new IllegalStateException(error);
+        }
+        if (!snet.providers().isEmpty()) {
+            updateSubscriberInstances(snet, instance, false);
+        }
+        // TODO check if subscribers on this network and remove group if unused
+        updateProviderInstances(snet);
+    }
+
+    private void dependencyAdded(ServiceNetwork subscriber, ServiceNetwork provider,
+                                 DependencyType type) {
+        populateDependencyRules(subscriber, provider, type, true);
+        log.info("Dependency is created subscriber:{}, provider:{}, type: {}",
+                 subscriber.name(),
+                 provider.name(), type.name());
+    }
+
+    private void dependencyRemoved(ServiceNetwork subscriber, ServiceNetwork provider,
+                                   DependencyType type) {
+        populateDependencyRules(subscriber, provider, type, false);
+        if (!isProviderInUse(provider.id())) {
+            removeGroup(provider.id());
+        }
+        log.info("Dependency is removed subscriber:{}, provider:{}, type: {}",
+                 subscriber.name(),
+                 provider.name(), type.name());
+    }
+
+    private void updateProviderInstances(ServiceNetwork 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) {
+                continue;
+            }
+            List<GroupBucket> oldBuckets = group.buckets().buckets();
+            List<GroupBucket> newBuckets = getProviderGroupBuckets(
+                    deviceId,
+                    provider.segmentId().id(),
+                    getInstances(provider.id())).buckets();
+            if (oldBuckets.equals(newBuckets)) {
+                continue;
+            }
+
+            List<GroupBucket> bucketsToRemove = Lists.newArrayList(oldBuckets);
+            bucketsToRemove.removeAll(newBuckets);
+            if (!bucketsToRemove.isEmpty()) {
+                groupService.removeBucketsFromGroup(
+                        deviceId,
+                        groupKey,
+                        new GroupBuckets(bucketsToRemove),
+                        groupKey, appId);
+                log.debug("Removed buckets from provider({}) group on {}: {}",
+                          provider.id(), deviceId, bucketsToRemove);
+            }
+
+            List<GroupBucket> bucketsToAdd = Lists.newArrayList(newBuckets);
+            bucketsToAdd.removeAll(oldBuckets);
+            if (!bucketsToAdd.isEmpty()) {
+                groupService.addBucketsToGroup(
+                        deviceId,
+                        groupKey,
+                        new GroupBuckets(bucketsToAdd),
+                        groupKey, appId);
+                log.debug("Added buckets to provider({}) group on {}: {}",
+                          provider.id(), deviceId, bucketsToAdd);
+            }
+        }
+    }
+
+    private void updateSubscriberInstances(ServiceNetwork subscriber, Instance instance,
+                                           boolean isDetected) {
+        DeviceId deviceId = instance.deviceId();
+        final String isAdded = isDetected ? ADDED : REMOVED;
+        subscriber.providers().keySet().forEach(providerId -> {
+            populateInPortRule(
+                    ImmutableMap.of(deviceId, ImmutableSet.of(instance.portNumber())),
+                    ImmutableMap.of(deviceId, getGroupId(providerId, deviceId)),
+                    isDetected);
+            log.info(isAdded + "subscriber instance({}) for provider({})",
+                     instance.host().id(), providerId.id());
+        });
+    }
+
+    private boolean isProviderInUse(NetworkId providerId) {
+        return snetService.serviceNetworks().stream()
+                .flatMap(net -> net.providers().keySet().stream())
+                .filter(provider -> Objects.equals(provider, providerId))
+                .findAny().isPresent();
+    }
+
+    private void removeGroup(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 GroupId getGroupId(NetworkId netId, DeviceId deviceId) {
+        return new GroupId(Objects.hash(netId, deviceId));
+    }
+
+    private GroupKey getGroupKey(NetworkId netId) {
+        return new DefaultGroupKey(netId.id().getBytes());
+    }
+
+    private GroupId getProviderGroup(ServiceNetwork provider, DeviceId deviceId) {
+        GroupKey groupKey = getGroupKey(provider.id());
+        Group group = groupService.getGroup(deviceId, groupKey);
+        GroupId groupId = getGroupId(provider.id(), deviceId);
+
+        if (group != null) {
+            return groupId;
+        }
+
+        GroupBuckets buckets = getProviderGroupBuckets(
+                deviceId, provider.segmentId().id(), getInstances(provider.id()));
+        GroupDescription groupDescription = new DefaultGroupDescription(
+                deviceId,
+                GroupDescription.Type.SELECT,
+                buckets,
+                groupKey,
+                groupId.id(),
+                appId);
+
+        groupService.addGroup(groupDescription);
+        return groupId;
+    }
+
+    private void populateDependencyRules(ServiceNetwork subscriber, ServiceNetwork provider,
+                                         DependencyType 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 = getProviderGroup(provider, deviceId);
+            providerGroups.put(deviceId, groupId);
+
+            Set<PortNumber> ports = getInstances(subscriber.id())
+                    .stream()
+                    .filter(instance -> instance.deviceId().equals(deviceId))
+                    .map(Instance::portNumber)
+                    .collect(Collectors.toSet());
+            subscriberPorts.put(deviceId, ports);
+        });
+
+        // TODO support IPv6
+        IpPrefix sSubnet = subscriber.subnet().getIp4Prefix();
+        IpPrefix pSubnet = provider.subnet().getIp4Prefix();
+
+        populateInPortRule(subscriberPorts, providerGroups, install);
+        populateIndirectAccessRule(
+                sSubnet,
+                provider.serviceIp().getIp4Address(),
+                providerGroups,
+                install);
+        populateDirectAccessRule(sSubnet, pSubnet, install);
+        if (type == BIDIRECTIONAL) {
+            populateDirectAccessRule(pSubnet, sSubnet, install);
+        }
+    }
+
+    private void populateIndirectAccessRule(IpPrefix srcSubnet, IpAddress serviceIp,
+                                            Map<DeviceId, GroupId> outGroups,
+                                            boolean install) {
+        // TODO support IPv6
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(srcSubnet)
+                .matchIPDst(serviceIp.toIpPrefix())
+                .build();
+
+        for (Map.Entry<DeviceId, GroupId> outGroup : outGroups.entrySet()) {
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .group(outGroup.getValue())
+                    .build();
+
+            FlowRule flowRule = DefaultFlowRule.builder()
+                    .fromApp(appId)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .withPriority(PRIORITY_HIGH)
+                    .forDevice(outGroup.getKey())
+                    .forTable(TABLE_ACCESS)
+                    .makePermanent()
+                    .build();
+
+            pipeline.processFlowRule(install, flowRule);
+        }
+    }
+
+    private void populateDirectAccessRule(IpPrefix srcIp, IpPrefix dstIp, boolean install) {
+        // TODO support IPv6
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPSrc(srcIp)
+                .matchIPDst(dstIp)
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .transition(TABLE_DST)
+                .build();
+
+        nodeManager.completeNodes().stream().forEach(node -> {
+            DeviceId deviceId = node.integrationBridgeId();
+            FlowRule flowRuleDirect = DefaultFlowRule.builder()
+                    .fromApp(appId)
+                    .withSelector(selector)
+                    .withTreatment(treatment)
+                    .withPriority(PRIORITY_DEFAULT)
+                    .forDevice(deviceId)
+                    .forTable(TABLE_ACCESS)
+                    .makePermanent()
+                    .build();
+
+            pipeline.processFlowRule(install, flowRuleDirect);
+        });
+    }
+
+    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 = providerGroups.get(deviceId);
+            if (groupId == null) {
+                continue;
+            }
+            ports.stream().forEach(port -> {
+                TrafficSelector selector = DefaultTrafficSelector.builder()
+                        .matchInPort(port)
+                        .build();
+
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .group(groupId)
+                        .build();
+
+                FlowRule flowRule = DefaultFlowRule.builder()
+                        .fromApp(appId)
+                        .withSelector(selector)
+                        .withTreatment(treatment)
+                        .withPriority(PRIORITY_DEFAULT)
+                        .forDevice(deviceId)
+                        .forTable(TABLE_IN_SERVICE)
+                        .makePermanent()
+                        .build();
+
+                pipeline.processFlowRule(install, flowRule);
+            });
+        }
+    }
+
+    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();
+
+            if (deviceId.equals(instance.deviceId())) {
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setEthDst(instance.mac())
+                        .setOutput(instance.portNumber())
+                        .build();
+                buckets.add(createSelectGroupBucket(treatment));
+            } else {
+                ExtensionTreatment tunnelDst =
+                        pipeline.tunnelDstTreatment(deviceId, tunnelIp);
+                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                        .setEthDst(instance.mac())
+                        .extension(tunnelDst, deviceId)
+                        .setTunnelId(tunnelId)
+                        .setOutput(nodeManager.tunnelPort(instance.deviceId()))
+                        .build();
+                buckets.add(createSelectGroupBucket(treatment));
+            }
+        });
+        return new GroupBuckets(buckets);
+    }
+
+    private class InternalServiceNetworkListener implements ServiceNetworkListener {
+
+        @Override
+        public boolean isRelevant(ServiceNetworkEvent event) {
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNodeId, leader);
+        }
+
+        @Override
+        public void event(ServiceNetworkEvent event) {
+
+            switch (event.type()) {
+                case SERVICE_NETWORK_PROVIDER_ADDED:
+                    log.debug("Dependency added: {}", event);
+                    eventExecutor.execute(() -> {
+                        dependencyAdded(
+                                event.subject(),
+                                event.provider().provider(),
+                                event.provider().type());
+                    });
+                    break;
+                case SERVICE_NETWORK_PROVIDER_REMOVED:
+                    log.debug("Dependency removed: {}", event);
+                    eventExecutor.execute(() -> {
+                        dependencyRemoved(
+                                event.subject(),
+                                event.provider().provider(),
+                                event.provider().type());
+                    });
+                    break;
+                case SERVICE_NETWORK_CREATED:
+                case SERVICE_NETWORK_UPDATED:
+                    // TODO handle dependency rule related element updates
+                case SERVICE_NETWORK_REMOVED:
+                case SERVICE_PORT_CREATED:
+                case SERVICE_PORT_UPDATED:
+                case SERVICE_PORT_REMOVED:
+                default:
+                    // do nothing for the other events
+                    break;
+            }
+        }
+    }
+}
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 dd3eb81..0475f07 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
@@ -29,14 +29,14 @@
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.instance.InstanceHandler;
-import org.opencord.cordvtn.api.net.VtnNetwork;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.InstanceHandler;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.MANAGEMENT_HOST;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.MANAGEMENT_LOCAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.MANAGEMENT_HOST;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.MANAGEMENT_LOCAL;
 
 /**
  * Provides local management network connectivity to the instance. The instance
@@ -64,7 +64,7 @@
 
     @Override
     public void instanceDetected(Instance instance) {
-        VtnNetwork vtnNet = getVtnNetwork(instance);
+        ServiceNetwork vtnNet = getServiceNetwork(instance);
 
         switch (vtnNet.type()) {
             case MANAGEMENT_LOCAL:
@@ -82,7 +82,7 @@
 
     @Override
     public void instanceRemoved(Instance instance) {
-        VtnNetwork vtnNet = getVtnNetwork(instance);
+        ServiceNetwork vtnNet = getServiceNetwork(instance);
 
         switch (vtnNet.type()) {
             case MANAGEMENT_LOCAL:
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 694d53a..f01b0e3 100644
--- a/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
@@ -28,12 +28,8 @@
 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.net.AddressPair;
-import org.opencord.cordvtn.api.instance.InstanceService;
-import org.opencord.cordvtn.api.instance.Instance;
-import org.opencord.cordvtn.api.instance.InstanceHandler;
 import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultFlowRule;
@@ -50,7 +46,11 @@
 import org.onosproject.net.flow.instructions.L2ModificationInstruction;
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
-import org.opencord.cordvtn.api.net.VtnPort;
+import org.opencord.cordvtn.api.core.Instance;
+import org.opencord.cordvtn.api.core.InstanceHandler;
+import org.opencord.cordvtn.api.core.InstanceService;
+import org.opencord.cordvtn.api.net.AddressPair;
+import org.opencord.cordvtn.api.net.ServicePort;
 import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
@@ -59,7 +59,7 @@
 
 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.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.VSG;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.VSG;
 
 /**
  * Provides network connectivity for vSG instances.
@@ -108,34 +108,34 @@
                 throw new IllegalStateException(error);
             }
 
-            VtnPort vtnPort = getVtnPort(vsgVm);
-            Set<IpAddress> wanIps = vtnPort.addressPairs().stream()
+            ServicePort sport = getServicePort(vsgVm);
+            Set<IpAddress> wanIps = sport.addressPairs().stream()
                     .map(AddressPair::ip).collect(Collectors.toSet());
             populateVsgRules(
-                    vsgVm, vtnPort.vlanId().get(),
+                    vsgVm, sport.vlanId(),
                     nodeManager.dataPort(vsgVm.deviceId()),
                     wanIps, true);
         } else {
             log.info(String.format(MSG_VSG_VM, MSG_DETECTED, instance));
-            VtnPort vtnPort = vtnService.vtnPort(instance.portId());
-            if (vtnPort == null || !vtnPort.vlanId().isPresent()) {
+            ServicePort sport = snetService.servicePort(instance.portId());
+            if (sport == null || sport.vlanId() == null) {
                 // service port can be updated after instance is created
                 return;
             }
 
             // insert vSG containers inside the vSG VM as a host
-            vtnPort.addressPairs().stream().forEach(pair -> addVsgContainer(
+            sport.addressPairs().stream().forEach(pair -> addVsgContainer(
                     instance,
                     pair.ip(),
                     pair.mac(),
-                    vtnPort.vlanId().get()));
+                    sport.vlanId()));
         }
     }
 
     @Override
     public void instanceUpdated(Instance instance) {
         if (!isVsgContainer(instance)) {
-            Set<MacAddress> vsgMacs = getVtnPort(instance).addressPairs().stream()
+            Set<MacAddress> vsgMacs = getServicePort(instance).addressPairs().stream()
                     .map(AddressPair::mac)
                     .collect(Collectors.toSet());
             hostService.getConnectedHosts(instance.host().location()).stream()
@@ -159,24 +159,25 @@
             return;
         }
 
-        VtnPort vtnPort = getVtnPort(instance);
-        Set<IpAddress> wanIps = vtnPort.addressPairs().stream()
+        // FIXME service port can be removed already
+        ServicePort sport = getServicePort(instance);
+        Set<IpAddress> wanIps = sport.addressPairs().stream()
                 .map(AddressPair::ip).collect(Collectors.toSet());
         populateVsgRules(
-                vsgVm, vtnPort.vlanId().get(),
+                vsgVm, sport.vlanId(),
                 nodeManager.dataPort(vsgVm.deviceId()),
                 isVsgContainer ? wanIps : ImmutableSet.of(),
                 false);
     }
 
     @Override
-    protected VtnPort getVtnPort(Instance instance) {
-        VtnPort vtnPort = vtnService.vtnPort(instance.portId());
-        if (vtnPort == null || !vtnPort.vlanId().isPresent()) {
+    protected ServicePort getServicePort(Instance instance) {
+        ServicePort sport = snetService.servicePort(instance.portId());
+        if (sport == null || sport.vlanId() == null) {
             final String error = String.format(ERR_VTN_PORT, instance);
             throw new IllegalStateException(error);
         }
-        return vtnPort;
+        return sport;
     }
 
     private boolean isVsgContainer(Instance instance) {
diff --git a/src/main/java/org/opencord/cordvtn/rest/CordVtnWebApplication.java b/src/main/java/org/opencord/cordvtn/rest/CordVtnWebApplication.java
index 1f282cb..8dbdbba 100644
--- a/src/main/java/org/opencord/cordvtn/rest/CordVtnWebApplication.java
+++ b/src/main/java/org/opencord/cordvtn/rest/CordVtnWebApplication.java
@@ -28,7 +28,6 @@
     public Set<Class<?>> getClasses() {
         return getClasses(ServiceNetworkWebResource.class,
                           ServicePortWebResource.class,
-                          ServiceDependencyWebResource.class,
                           NeutronMl2NetworksWebResource.class,
                           NeutronMl2SubnetsWebResource.class,
                           NeutronMl2PortsWebResource.class,
diff --git a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java
index 5baf99b..939b373 100644
--- a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2NetworksWebResource.java
@@ -16,21 +16,21 @@
 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.core.CordVtnAdminService;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
 import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.SegmentId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType;
+import org.opencord.cordvtn.impl.DefaultServiceNetwork;
 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;
@@ -42,11 +42,12 @@
 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;
+import static javax.ws.rs.core.Response.Status.OK;
+import static javax.ws.rs.core.Response.created;
+import static javax.ws.rs.core.Response.noContent;
+import static javax.ws.rs.core.Response.status;
 
 /**
  * Neutron ML2 mechanism driver implementation for the network resource.
@@ -56,11 +57,10 @@
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
     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);
+    private final ServiceNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(ServiceNetworkAdminService.class);
 
     @Context
     private UriInfo uriInfo;
@@ -78,12 +78,12 @@
     public Response createNetwork(InputStream input) {
         log.trace(String.format(MESSAGE, "CREATE"));
 
-        final NeutronNetwork net = readNetwork(input);
-        adminService.createNetwork(net);
+        final ServiceNetwork snet = readNetwork(input);
+        adminService.createServiceNetwork(snet);
 
         UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
                 .path(NETWORKS)
-                .path(net.getId());
+                .path(snet.id().id());
 
         return created(locationBuilder.build()).build();
     }
@@ -103,54 +103,10 @@
     public Response updateNetwork(@PathParam("id") String id, InputStream input) {
         log.trace(String.format(MESSAGE, "UPDATE " + id));
 
-        final NeutronNetwork net = readNetwork(input);
-        adminService.updateNetwork(net);
+        final ServiceNetwork snet = readNetwork(input);
+        adminService.updateServiceNetwork(snet);
 
-        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.networks();
-        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.network(NetworkId.of(id));
-        if (net == null) {
-            return status(NOT_FOUND).build();
-        }
-
-        ObjectNode result = mapper().createObjectNode();
-        return ok(result.set(NETWORK, writeNetwork(net))).build();
+        return status(OK).build();
     }
 
     /**
@@ -166,31 +122,26 @@
     public Response deleteNetwork(@PathParam("id") String id) {
         log.trace(String.format(MESSAGE, "DELETE " + id));
 
-        adminService.removeNetwork(NetworkId.of(id));
+        adminService.removeServiceNetwork(NetworkId.of(id));
         return noContent().build();
     }
 
-    private NeutronNetwork readNetwork(InputStream input) {
+    private ServiceNetwork readNetwork(InputStream input) {
         try {
             JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
             log.trace(mapper().writeValueAsString(jsonTree));
-            return ObjectMapperSingleton.getContext(NeutronNetwork.class)
+            NeutronNetwork osNet = ObjectMapperSingleton.getContext(NeutronNetwork.class)
                     .readerFor(NeutronNetwork.class)
                     .readValue(jsonTree);
+
+            return DefaultServiceNetwork.builder()
+                    .id(NetworkId.of(osNet.getId()))
+                    .name(osNet.getName())
+                    .type(NetworkType.PRIVATE)
+                    .segmentId(SegmentId.of(Long.valueOf(osNet.getProviderSegID())))
+                    .build();
         } 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 d1b52ce..8776e39 100644
--- a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2PortsWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2PortsWebResource.java
@@ -16,21 +16,23 @@
 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 com.google.common.base.Strings;
 import org.onlab.osgi.DefaultServiceDirectory;
-import org.opencord.cordvtn.api.core.CordVtnAdminService;
-import org.opencord.cordvtn.api.net.PortId;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
 import org.onosproject.rest.AbstractWebResource;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.PortId;
+import org.opencord.cordvtn.api.net.ServicePort;
+import org.opencord.cordvtn.impl.DefaultServicePort;
 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;
@@ -42,10 +44,9 @@
 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.Status.OK;
 import static javax.ws.rs.core.Response.created;
 import static javax.ws.rs.core.Response.noContent;
 import static javax.ws.rs.core.Response.status;
@@ -59,11 +60,10 @@
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String MESSAGE = "Received ports %s request";
-    private static final String PORT  = "port";
     private static final String PORTS = "ports";
 
-    private final CordVtnAdminService adminService =
-            DefaultServiceDirectory.getService(CordVtnAdminService.class);
+    private final ServiceNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(ServiceNetworkAdminService.class);
 
     @Context
     private UriInfo uriInfo;
@@ -81,11 +81,11 @@
     public Response createPorts(InputStream input) {
         log.trace(String.format(MESSAGE, "CREATE"));
 
-        final NeutronPort port = readPort(input);
-        adminService.createPort(port);
+        final ServicePort sport = readPort(input);
+        adminService.createServicePort(sport);
         UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
                 .path(PORTS)
-                .path(port.getId());
+                .path(sport.id().id());
 
         return created(locationBuilder.build()).build();
     }
@@ -105,54 +105,10 @@
     public Response updatePort(@PathParam("id") String id, InputStream input) {
         log.trace(String.format(MESSAGE, "UPDATE " + id));
 
-        final NeutronPort port = readPort(input);
-        adminService.updatePort(port);
+        final ServicePort port = readPort(input);
+        adminService.updateServicePort(port);
 
-        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.ports();
-        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.port(PortId.of(id));
-        if (port == null) {
-            return status(NOT_FOUND).build();
-        }
-
-        ObjectNode result = this.mapper().createObjectNode();
-        return ok(result.set(PORT, writePort(port))).build();
+        return status(OK).build();
     }
 
     /**
@@ -168,31 +124,36 @@
     public Response deletePorts(@PathParam("id") String id) {
         log.trace(String.format(MESSAGE, "DELETE " + id));
 
-        adminService.removePort(PortId.of(id));
+        adminService.removeServicePort(PortId.of(id));
         return noContent().build();
     }
 
-    private NeutronPort readPort(InputStream input) {
+    private ServicePort readPort(InputStream input) {
         try {
             JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
             log.trace(mapper().writeValueAsString(jsonTree));
-            return ObjectMapperSingleton.getContext(NeutronPort.class)
+            NeutronPort osPort = ObjectMapperSingleton.getContext(NeutronPort.class)
                     .readerFor(NeutronPort.class)
                     .readValue(jsonTree);
+
+            ServicePort.Builder sportBuilder = DefaultServicePort.builder()
+                    .id(PortId.of(osPort.getId()))
+                    .networkId(NetworkId.of(osPort.getNetworkId()));
+
+            if (!Strings.isNullOrEmpty(osPort.getName())) {
+                sportBuilder.name(osPort.getName());
+            }
+            if (osPort.getMacAddress() != null) {
+                sportBuilder.mac(MacAddress.valueOf(osPort.getMacAddress()));
+            }
+            if (!osPort.getFixedIps().isEmpty()) {
+                sportBuilder.ip(IpAddress.valueOf(
+                        osPort.getFixedIps().iterator().next().getIpAddress()));
+            }
+
+            return sportBuilder.build();
         } 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 aa374db..fc7ee65 100644
--- a/src/main/java/org/opencord/cordvtn/rest/NeutronMl2SubnetsWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/NeutronMl2SubnetsWebResource.java
@@ -16,21 +16,21 @@
 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.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onosproject.rest.AbstractWebResource;
-import org.opencord.cordvtn.api.core.CordVtnAdminService;
-import org.opencord.cordvtn.api.net.SubnetId;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
+import org.opencord.cordvtn.api.net.NetworkId;
+import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.impl.DefaultServiceNetwork;
 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;
@@ -42,10 +42,9 @@
 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.Status.OK;
 import static javax.ws.rs.core.Response.created;
 import static javax.ws.rs.core.Response.noContent;
 import static javax.ws.rs.core.Response.status;
@@ -58,11 +57,10 @@
     protected final Logger log = LoggerFactory.getLogger(getClass());
 
     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);
+    private final ServiceNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(ServiceNetworkAdminService.class);
 
     @Context
     private UriInfo uriInfo;
@@ -80,13 +78,14 @@
     public Response createSubnet(InputStream input) {
         log.trace(String.format(MESSAGE, "CREATE"));
 
-        final NeutronSubnet subnet = readSubnet(input);
-        adminService.createSubnet(subnet);
+        // FIXME do not allow more than one subnet per network
+        final ServiceNetwork snet = readSubnet(input);
+        adminService.updateServiceNetwork(snet);
         UriBuilder locationBuilder = uriInfo.getBaseUriBuilder()
                 .path(SUBNETS)
-                .path(subnet.getId());
+                .path(snet.id().id());
 
-        // TODO fix networking-onos to send Network UPDATE when subnet created
+        // TODO fix Neutron networking-onos to send Network UPDATE when subnet created
         return created(locationBuilder.build()).build();
     }
 
@@ -105,54 +104,10 @@
     public Response updateSubnet(@PathParam("id") String id, InputStream input) {
         log.trace(String.format(MESSAGE, "UPDATE " + id));
 
-        final NeutronSubnet subnet = readSubnet(input);
-        adminService.updateSubnet(subnet);
+        final ServiceNetwork snet = readSubnet(input);
+        adminService.updateServiceNetwork(snet);
 
-        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.subnets();
-        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.subnet(SubnetId.of(id));
-        if (subnet == null) {
-            return status(NOT_FOUND).build();
-        }
-
-        ObjectNode result = this.mapper().createObjectNode();
-        return ok(result.set(SUBNET, writeSubnet(subnet))).build();
+        return status(OK).build();
     }
 
     /**
@@ -167,32 +122,24 @@
     @Produces(MediaType.APPLICATION_JSON)
     public Response deleteSubnet(@PathParam("id") String id) {
         log.trace(String.format(MESSAGE, "DELETE " + id));
-
-        adminService.removeSubnet(SubnetId.of(id));
         return noContent().build();
     }
 
-    private NeutronSubnet readSubnet(InputStream input) {
+    private ServiceNetwork readSubnet(InputStream input) {
         try {
             JsonNode jsonTree = mapper().enable(INDENT_OUTPUT).readTree(input);
             log.trace(mapper().writeValueAsString(jsonTree));
-            return ObjectMapperSingleton.getContext(NeutronSubnet.class)
+            NeutronSubnet osSubnet = 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());
+            return DefaultServiceNetwork.builder()
+                    .id(NetworkId.of(osSubnet.getNetworkId()))
+                    .subnet(IpPrefix.valueOf(osSubnet.getCidr()))
+                    .serviceIp(IpAddress.valueOf(osSubnet.getGateway()))
+                    .build();
         } catch (Exception e) {
-            throw new IllegalStateException();
+            throw new IllegalArgumentException(e);
         }
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/rest/ServiceDependencyWebResource.java b/src/main/java/org/opencord/cordvtn/rest/ServiceDependencyWebResource.java
deleted file mode 100644
index be4a94b..0000000
--- a/src/main/java/org/opencord/cordvtn/rest/ServiceDependencyWebResource.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright 2015-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.rest;
-
-import org.onosproject.rest.AbstractWebResource;
-import org.opencord.cordvtn.api.dependency.Dependency.Type;
-import org.opencord.cordvtn.api.dependency.DependencyService;
-import org.opencord.cordvtn.api.net.NetworkId;
-
-import javax.ws.rs.DELETE;
-import javax.ws.rs.POST;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-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.Dependency.Type.BIDIRECTIONAL;
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.UNIDIRECTIONAL;
-
-/**
- * Manages service dependency.
- */
-@Deprecated
-@Path("service-dependency")
-public class ServiceDependencyWebResource extends AbstractWebResource {
-
-    private final DependencyService service = get(DependencyService.class);
-    private static final String BIDIRECTION = "b";
-
-    /**
-     * Creates service dependencies with unidirectional access between the services.
-     *
-     * @param tServiceId tenant service id
-     * @param pServiceId provider service id
-     * @return 200 OK
-     */
-    @POST
-    @Path("{tenantServiceId}/{providerServiceId}")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response createServiceDependency(@PathParam("tenantServiceId") String tServiceId,
-                                            @PathParam("providerServiceId") String pServiceId) {
-        service.createDependency(NetworkId.of(tServiceId),
-                                 NetworkId.of(pServiceId),
-                                 UNIDIRECTIONAL);
-        return Response.status(Response.Status.OK).build();
-    }
-
-    /**
-     * Creates service dependencies with an access type extension between the services.
-     *
-     * @param tServiceId tenant service id
-     * @param pServiceId provider service id
-     * @param direction b for bidirectional access, otherwise unidirectional access
-     * @return 200 OK
-     */
-    @POST
-    @Path("{tenantServiceId}/{providerServiceId}/{direction}")
-    @Produces(MediaType.APPLICATION_JSON)
-    public Response createServiceDependency(@PathParam("tenantServiceId") String tServiceId,
-                                            @PathParam("providerServiceId") String pServiceId,
-                                            @PathParam("direction") String direction) {
-        Type type = Objects.equals(direction, BIDIRECTION) ? BIDIRECTIONAL : UNIDIRECTIONAL;
-        service.createDependency(NetworkId.of(tServiceId),
-                                 NetworkId.of(pServiceId),
-                                 type);
-        return Response.status(Response.Status.OK).build();
-    }
-
-    /**
-     * Removes service dependencies.
-     *
-     * @param tServiceId tenant service id
-     * @param pServiceId provider service id
-     * @return 204 NO CONTENT
-     */
-    @DELETE
-    @Path("{tenantServiceId}/{providerServiceId}")
-    public Response removeServiceDependency(@PathParam("tenantServiceId") String tServiceId,
-                                            @PathParam("providerServiceId") String 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 6a468a6..551d6af 100644
--- a/src/main/java/org/opencord/cordvtn/rest/ServiceNetworkWebResource.java
+++ b/src/main/java/org/opencord/cordvtn/rest/ServiceNetworkWebResource.java
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present Open Networking Laboratory
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -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.core.CordVtnAdminService;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
 import org.opencord.cordvtn.api.net.NetworkId;
 import org.opencord.cordvtn.api.net.ServiceNetwork;
 import org.slf4j.Logger;
@@ -44,9 +44,7 @@
 
 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;
+import static javax.ws.rs.core.Response.*;
 
 /**
  * Query and manage service networks.
@@ -59,8 +57,8 @@
     private static final String SERVICE_NETWORK  = "ServiceNetwork";
     private static final String SERVICE_NETWORKS = "ServiceNetworks";
 
-    private final CordVtnAdminService adminService =
-            DefaultServiceDirectory.getService(CordVtnAdminService.class);
+    private final ServiceNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(ServiceNetworkAdminService.class);
 
     @Context
     private UriInfo uriInfo;
diff --git a/src/main/java/org/opencord/cordvtn/rest/ServicePortWebResource.java b/src/main/java/org/opencord/cordvtn/rest/ServicePortWebResource.java
index b3a0580..88cc665 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.core.CordVtnAdminService;
+import org.opencord.cordvtn.api.core.ServiceNetworkAdminService;
 import org.opencord.cordvtn.api.net.PortId;
 import org.opencord.cordvtn.api.net.ServicePort;
 import org.slf4j.Logger;
@@ -44,9 +44,7 @@
 
 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;
+import static javax.ws.rs.core.Response.*;
 
 /**
  * Query and manage service ports.
@@ -59,8 +57,8 @@
     private static final String SERVICE_PORT  = "ServicePort";
     private static final String SERVICE_PORTS = "ServicePorts";
 
-    private final CordVtnAdminService adminService =
-            DefaultServiceDirectory.getService(CordVtnAdminService.class);
+    private final ServiceNetworkAdminService adminService =
+            DefaultServiceDirectory.getService(ServiceNetworkAdminService.class);
 
     @Context
     private UriInfo uriInfo;
diff --git a/src/main/java/org/opencord/cordvtn/impl/external/XosServiceNetworking.java b/src/main/java/org/opencord/cordvtn/rest/XosVtnNetworkingClient.java
similarity index 93%
rename from src/main/java/org/opencord/cordvtn/impl/external/XosServiceNetworking.java
rename to src/main/java/org/opencord/cordvtn/rest/XosVtnNetworkingClient.java
index 1c232cf..fed5e91 100644
--- a/src/main/java/org/opencord/cordvtn/impl/external/XosServiceNetworking.java
+++ b/src/main/java/org/opencord/cordvtn/rest/XosVtnNetworkingClient.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.impl.external;
+package org.opencord.cordvtn.rest;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.SerializationFeature;
@@ -26,7 +26,6 @@
 import org.opencord.cordvtn.api.net.NetworkId;
 import org.opencord.cordvtn.api.net.PortId;
 import org.opencord.cordvtn.api.net.ServiceNetwork;
-import org.opencord.cordvtn.api.net.ServiceNetworkService;
 import org.opencord.cordvtn.api.net.ServicePort;
 import org.slf4j.Logger;
 
@@ -45,10 +44,9 @@
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Implementation of {@link ServiceNetworkService} with XOS VTN service.
+ * Implementation of REST client for XOS VTN networking service.
  */
-public final class XosServiceNetworking extends AbstractWebResource
-        implements ServiceNetworkService {
+public final class XosVtnNetworkingClient extends AbstractWebResource {
 
     protected final Logger log = getLogger(getClass());
 
@@ -72,7 +70,7 @@
     private final String password;
     private final Client client = ClientBuilder.newClient();
 
-    private XosServiceNetworking(String endpoint, String user, String password) {
+    private XosVtnNetworkingClient(String endpoint, String user, String password) {
         this.endpoint = endpoint;
         this.user = user;
         this.password = password;
@@ -82,7 +80,6 @@
         mapper().enable(SerializationFeature.INDENT_OUTPUT);
     }
 
-    @Override
     public Set<ServiceNetwork> serviceNetworks() {
         String response = restGet(URL_SERVICE_NETWORKS);
         final String error = String.format(ERR_LOG, SERVICE_NETWORKS, response);
@@ -101,7 +98,6 @@
         }
     }
 
-    @Override
     public ServiceNetwork serviceNetwork(NetworkId netId) {
         String response = restGet(URL_SERVICE_NETWORKS + netId.id());
         final String error = String.format(ERR_LOG, SERVICE_NETWORK, response);
@@ -118,7 +114,6 @@
         }
     }
 
-    @Override
     public Set<ServicePort> servicePorts() {
         String response = restGet(URL_SERVICE_PORTS);
         final String error = String.format(ERR_LOG, SERVICE_PORTS, response);
@@ -137,7 +132,6 @@
         }
     }
 
-    @Override
     public ServicePort servicePort(PortId portId) {
         String response = restGet(URL_SERVICE_PORTS + portId.id());
         final String error = String.format(ERR_LOG, SERVICE_PORT, response);
@@ -222,13 +216,13 @@
          *
          * @return xos service networking instance
          */
-        public XosServiceNetworking build() {
+        public XosVtnNetworkingClient build() {
             checkArgument(!Strings.isNullOrEmpty(endpoint));
             checkArgument(!Strings.isNullOrEmpty(user));
             checkArgument(!Strings.isNullOrEmpty(password));
 
             // TODO perform authentication when XOS provides it
-            return new XosServiceNetworking(endpoint, user, password);
+            return new XosVtnNetworkingClient(endpoint, user, password);
         }
 
         /**
diff --git a/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index bdb9335..ad95240 100644
--- a/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -35,7 +35,10 @@
             <action class="org.opencord.cordvtn.cli.CordVtnPurgeStatesCommand"/>
         </command>
         <command>
-            <action class="org.opencord.cordvtn.cli.CordVtnSyncStatesCommand"/>
+            <action class="org.opencord.cordvtn.cli.CordVtnSyncNeutronStatesCommand"/>
+        </command>
+        <command>
+            <action class="org.opencord.cordvtn.cli.CordVtnSyncXosStatesCommand"/>
         </command>
         <command>
             <action class="org.opencord.cordvtn.cli.CordVtnNetworkListCommand"/>
diff --git a/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkCodecTest.java b/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkCodecTest.java
index 0886a4a..35d9a1b 100644
--- a/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkCodecTest.java
+++ b/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkCodecTest.java
@@ -18,25 +18,28 @@
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.NullNode;
 import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableMap;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.onosproject.codec.JsonCodec;
 import org.opencord.cordvtn.api.net.NetworkId;
-import org.opencord.cordvtn.api.net.ProviderNetwork;
 import org.opencord.cordvtn.api.net.ServiceNetwork;
+import org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType;
+import org.opencord.cordvtn.impl.DefaultServiceNetwork;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.BIDIRECTIONAL;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.MANAGEMENT_LOCAL;
-import static org.opencord.cordvtn.api.net.ServiceNetwork.ServiceNetworkType.PRIVATE;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.MANAGEMENT_LOCAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.NetworkType.PRIVATE;
 import static org.opencord.cordvtn.codec.ServiceNetworkJsonMatcher.matchesServiceNetwork;
 
 /**
@@ -45,21 +48,29 @@
 public final class ServiceNetworkCodecTest {
     private static final String SERVICE_NETWORK = "serviceNetwork";
     private static final String ID = "id";
+    private static final String NAME = "name";
     private static final String TYPE = "type";
-    private static final String PROVIDER_NETWORKS = "providerNetworks";
+    private static final String PROVIDERS = "providers";
 
-    private final ProviderNetwork providerA =
-            ProviderNetwork.of(NetworkId.of("A"), BIDIRECTIONAL);
+    private final Map<NetworkId, DependencyType> providerA =
+            new HashMap<NetworkId, DependencyType>() {
+                {
+                    put(NetworkId.of("A"), BIDIRECTIONAL);
+                }
+            };
 
-    private final ServiceNetwork networkB = new ServiceNetwork(
-            NetworkId.of("A"),
-            MANAGEMENT_LOCAL,
-            ImmutableSet.of());
+    private final ServiceNetwork networkA = DefaultServiceNetwork.builder()
+            .id(NetworkId.of("A"))
+            .name("A")
+            .type(MANAGEMENT_LOCAL)
+            .build();
 
-    private final ServiceNetwork networkA = new ServiceNetwork(
-            NetworkId.of("B"),
-            PRIVATE,
-            ImmutableSet.of(providerA));
+    private final ServiceNetwork networkB = DefaultServiceNetwork.builder()
+            .id(NetworkId.of("B"))
+            .name("B")
+            .type(PRIVATE)
+            .providers(providerA)
+            .build();
 
     @Rule
     public ExpectedException exception = ExpectedException.none();
@@ -93,20 +104,23 @@
     public void testServiceNetworkDecode() throws IOException {
         ServiceNetwork snet = getServiceNetwork("service-network.json");
         assertThat(snet.id(), is(NetworkId.of("A")));
+        assertThat(snet.name(), is("A"));
         assertThat(snet.type(), is(MANAGEMENT_LOCAL));
-        assertThat(snet.providers(), is(ImmutableSet.of()));
+        assertThat(snet.providers(), is(ImmutableMap.of()));
 
         snet = getServiceNetwork("service-network-with-provider.json");
         assertThat(snet.id(), is(NetworkId.of("B")));
+        assertThat(snet.name(), is("B"));
         assertThat(snet.type(), is(PRIVATE));
-        assertThat(snet.providers(), is(ImmutableSet.of(providerA)));
+        assertThat(snet.providers(), is(providerA));
     }
 
     @Test
     public void testServiceNetworkDecodeMissingId() throws IllegalArgumentException {
         final JsonNode jsonMissingId = context.mapper().createObjectNode()
+                .put(NAME, "A")
                 .put(TYPE, PRIVATE.name())
-                .put(PROVIDER_NETWORKS, "");
+                .put(PROVIDERS, "");
         exception.expect(IllegalArgumentException.class);
         codec.decode((ObjectNode) jsonMissingId, context);
     }
@@ -115,8 +129,9 @@
     public void testServiceNetworkDecodeEmptyId() throws IllegalArgumentException {
         final JsonNode jsonEmptyId = context.mapper().createObjectNode()
                 .put(ID, "")
+                .put(NAME, "A")
                 .put(TYPE, PRIVATE.name())
-                .put(PROVIDER_NETWORKS, "");
+                .put(PROVIDERS, "");
         exception.expect(IllegalArgumentException.class);
         codec.decode((ObjectNode) jsonEmptyId, context);
     }
@@ -124,61 +139,24 @@
     @Test
     public void testServiceNetworkDecodeNullId() throws NullPointerException {
         final JsonNode jsonNullId = context.mapper().createObjectNode()
+                .put(NAME, "A")
                 .put(TYPE, PRIVATE.name())
-                .put(PROVIDER_NETWORKS, "")
+                .put(PROVIDERS, "")
                 .set(ID, NullNode.getInstance());
         exception.expect(IllegalArgumentException.class);
         codec.decode((ObjectNode) jsonNullId, context);
     }
 
     @Test
-    public void testServiceNetworkDecodeMissingType() throws IllegalArgumentException {
-        final JsonNode jsonMissingType = context.mapper().createObjectNode()
-                .put(ID, "A")
-                .put(PROVIDER_NETWORKS, "");
-        exception.expect(IllegalArgumentException.class);
-        codec.decode((ObjectNode) jsonMissingType, context);
-    }
-
-    @Test
-    public void testServiceNetworkDecodeEmptyType() throws IllegalArgumentException {
-        final JsonNode jsonEmptyType = context.mapper().createObjectNode()
-                .put(ID, "A")
-                .put(TYPE, "")
-                .put(PROVIDER_NETWORKS, "");
-        exception.expect(IllegalArgumentException.class);
-        codec.decode((ObjectNode) jsonEmptyType, context);
-    }
-
-    @Test
-    public void testServiceNetworkDecodeNullType() throws IllegalArgumentException {
-        final JsonNode jsonNullType = context.mapper().createObjectNode()
-                .put(ID, "A")
-                .put(PROVIDER_NETWORKS, "")
-                .set(TYPE, NullNode.getInstance());
-        exception.expect(IllegalArgumentException.class);
-        codec.decode((ObjectNode) jsonNullType, context);
-    }
-
-    @Test
-    public void testServiceNetworkDecodeWrongType() throws IllegalArgumentException {
-        final JsonNode jsonNoneType = context.mapper().createObjectNode()
-                .put(ID, "A")
-                .put(TYPE, "none")
-                .put(PROVIDER_NETWORKS, "");
-        exception.expect(IllegalArgumentException.class);
-        codec.decode((ObjectNode) jsonNoneType, context);
-    }
-
-    @Test
     public void testServiceNetworkDecodeWithMissingProviderId() throws NullPointerException {
         final JsonNode jsonMissingProviderId = context.mapper().createObjectNode()
                 .put(TYPE, "B");
         final JsonNode jsonWithProvider = context.mapper().createObjectNode()
                 .put(ID, "A")
+                .put(NAME, "A")
                 .put(TYPE, PRIVATE.name())
-                .set(PROVIDER_NETWORKS, jsonMissingProviderId);
-        exception.expect(NullPointerException.class);
+                .set(PROVIDERS, jsonMissingProviderId);
+        exception.expect(IllegalArgumentException.class);
         codec.decode((ObjectNode) jsonWithProvider, context);
     }
 
@@ -189,9 +167,10 @@
                 .put(TYPE, "none");
         final JsonNode jsonWithProvider = context.mapper().createObjectNode()
                 .put(ID, "A")
+                .put(NAME, "A")
                 .put(TYPE, PRIVATE.name())
-                .set(PROVIDER_NETWORKS, jsonWrongProviderType);
-        exception.expect(NullPointerException.class);
+                .set(PROVIDERS, jsonWrongProviderType);
+        exception.expect(IllegalArgumentException.class);
         codec.decode((ObjectNode) jsonWithProvider, context);
     }
 
diff --git a/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkJsonMatcher.java b/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkJsonMatcher.java
index 0544a42..da321e3 100644
--- a/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkJsonMatcher.java
+++ b/src/test/java/org/opencord/cordvtn/codec/ServiceNetworkJsonMatcher.java
@@ -19,12 +19,12 @@
 import com.fasterxml.jackson.databind.node.NullNode;
 import org.hamcrest.Description;
 import org.hamcrest.TypeSafeDiagnosingMatcher;
-import org.opencord.cordvtn.api.net.ProviderNetwork;
+import org.opencord.cordvtn.api.net.NetworkId;
 import org.opencord.cordvtn.api.net.ServiceNetwork;
 
 import java.util.Objects;
 
-import static org.opencord.cordvtn.api.dependency.Dependency.Type.BIDIRECTIONAL;
+import static org.opencord.cordvtn.api.net.ServiceNetwork.DependencyType.BIDIRECTIONAL;
 
 /**
  * Json matcher for ServiceNetwork.
@@ -61,11 +61,11 @@
             return false;
         }
 
-        if (network.providers().isEmpty()) {
+        if (network.providers() == null || network.providers().isEmpty()) {
             return true;
         }
 
-        JsonNode jsonProviders = jsonNet.get("providerNetworks");
+        JsonNode jsonProviders = jsonNet.get("providers");
         if (jsonProviders == null || jsonProviders == NullNode.getInstance()) {
             description.appendText("provider networks were empty");
             return false;
@@ -76,19 +76,16 @@
         }
 
         for (JsonNode provider : jsonProviders) {
-            String id = provider.get("id").asText();
+            NetworkId id = NetworkId.of(provider.get("id").asText());
             boolean bidirectional = provider.get("bidirectional").asBoolean();
-            ProviderNetwork proNet = network.providers().stream()
-                    .filter(p -> p.id().id().equals(id))
-                    .findAny().orElse(null);
 
-            if (proNet == null) {
+            if (!network.providers().containsKey(id)) {
                 final String msg = String.format("provider id:%s couldn't find", id);
                 description.appendText(msg);
                 return false;
             }
 
-            if (proNet.type().equals(BIDIRECTIONAL) != bidirectional) {
+            if (network.providers().get(id).equals(BIDIRECTIONAL) != bidirectional) {
                 final String msg = String.format(
                         "mismatch provider id:%s, bidirectional: %s",
                         id, bidirectional);
diff --git a/src/test/resources/org/opencord/cordvtn/codec/service-network-with-provider.json b/src/test/resources/org/opencord/cordvtn/codec/service-network-with-provider.json
index a5dbb88..b017c07 100644
--- a/src/test/resources/org/opencord/cordvtn/codec/service-network-with-provider.json
+++ b/src/test/resources/org/opencord/cordvtn/codec/service-network-with-provider.json
@@ -1,8 +1,9 @@
 {
   "serviceNetwork": {
     "id": "B",
+    "name": "B",
     "type": "private",
-    "providerNetworks": [
+    "providers": [
       {
         "id": "A",
         "bidirectional": true
diff --git a/src/test/resources/org/opencord/cordvtn/codec/service-network.json b/src/test/resources/org/opencord/cordvtn/codec/service-network.json
index 857cca6..9235f99 100644
--- a/src/test/resources/org/opencord/cordvtn/codec/service-network.json
+++ b/src/test/resources/org/opencord/cordvtn/codec/service-network.json
@@ -1,6 +1,7 @@
 {
   "serviceNetwork": {
     "id": "A",
+    "name": "A",
     "type": "management_local",
     "providerNetworks": []
   }