Separated ARP proxy as an independent component and some renaming

- Renamed CordVtnService to DependencyService
- Renamed CordVtnInstanceManager to InstanceManager and added
  InstanceService as an interface of the manager implementation
- Renamed package name impl.service to impl.handler
- Added Constants class

Change-Id: I249708c008d5105957aa1d1a796f0ca32025e75c
diff --git a/src/main/java/org/opencord/cordvtn/impl/AbstractInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/AbstractInstanceHandler.java
index 29868b4..d714e3e 100644
--- a/src/main/java/org/opencord/cordvtn/impl/AbstractInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/AbstractInstanceHandler.java
@@ -37,8 +37,8 @@
 import org.onosproject.xosclient.api.VtnServiceId;
 import org.onosproject.xosclient.api.XosAccess;
 import org.onosproject.xosclient.api.XosClientService;
+import org.opencord.cordvtn.api.Constants;
 import org.opencord.cordvtn.api.CordVtnConfig;
-import org.opencord.cordvtn.api.CordVtnService;
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
 import org.slf4j.Logger;
@@ -51,6 +51,10 @@
 import java.util.stream.StreamSupport;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.opencord.cordvtn.api.Constants.ERROR_OPENSTACK_ACCESS;
+import static org.opencord.cordvtn.api.Constants.ERROR_XOS_ACCESS;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -65,19 +69,16 @@
     protected NetworkConfigRegistry configRegistry;
     protected HostService hostService;
     protected XosClientService xosClient;
-    protected CordVtnPipeline pipeline;
-    protected CordVtnNodeManager nodeManager;
 
     protected ApplicationId appId;
-    protected ExecutorService eventExecutor;
     protected Optional<ServiceType> serviceType = Optional.empty();
     protected NetworkConfigListener configListener = new InternalConfigListener();
     protected HostListener hostListener = new InternalHostListener();
 
-    private static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
-    private static final String XOS_ACCESS_ERROR = "XOS access is not configured";
     private XosAccess xosAccess = null;
     private OpenStackAccess osAccess = null;
+    private final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler"));
 
     protected void activate() {
         ServiceDirectory services = new DefaultServiceDirectory();
@@ -86,10 +87,8 @@
         mastershipService = services.get(MastershipService.class);
         hostService = services.get(HostService.class);
         xosClient = services.get(XosClientService.class);
-        pipeline = services.get(CordVtnPipeline.class);
-        nodeManager = services.get(CordVtnNodeManager.class);
 
-        appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
+        appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
         hostService.addListener(hostListener);
         configRegistry.addListener(configListener);
 
@@ -105,8 +104,8 @@
     }
 
     protected VtnService getVtnService(VtnServiceId serviceId) {
-        checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
-        checkNotNull(xosAccess, XOS_ACCESS_ERROR);
+        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
+        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
 
         // TODO remove openstack access when XOS provides all information
         VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
@@ -118,8 +117,8 @@
     }
 
     protected VtnPort getVtnPort(Instance instance) {
-        checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
-        checkNotNull(xosAccess, XOS_ACCESS_ERROR);
+        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
+        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
 
         VtnPortId vtnPortId = instance.portId();
         VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
index 6d29a7d..9e8d313 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnArpProxy.java
@@ -16,20 +16,27 @@
 package org.opencord.cordvtn.impl;
 
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.ARP;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.xosclient.api.VtnService;
+import org.opencord.cordvtn.api.CordVtnConfig;
 import org.opencord.cordvtn.api.Instance;
-import org.onosproject.core.ApplicationId;
 import org.onosproject.net.Host;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketPriority;
@@ -42,37 +49,41 @@
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.PRIVATE;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
  * Handles ARP requests for virtual network service IPs.
  */
-public class CordVtnArpProxy {
+@Component(immediate = true)
+public class CordVtnArpProxy extends AbstractInstanceHandler {
     protected final Logger log = getLogger(getClass());
 
-    private final ApplicationId appId;
-    private final PacketService packetService;
-    private final HostService hostService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
 
+    private final PacketProcessor packetProcessor = new InternalPacketProcessor();
     private final Map<Ip4Address, MacAddress> gateways = Maps.newConcurrentMap();
 
-    /**
-     * Default constructor.
-     *
-     * @param appId application id
-     * @param packetService packet service
-     * @param hostService host service reference
-     */
-    public CordVtnArpProxy(ApplicationId appId, PacketService packetService, HostService hostService) {
-        this.appId = appId;
-        this.packetService = packetService;
-        this.hostService = hostService;
+    private MacAddress privateGatewayMac = MacAddress.NONE;
+
+    @Activate
+    protected void activate() {
+        super.activate();
+        packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
+        requestPacket();
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        packetService.removeProcessor(packetProcessor);
+        super.deactivate();
     }
 
     /**
      * Requests ARP packet.
      */
-    public void requestPacket() {
+    private void requestPacket() {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
                 .build();
@@ -86,7 +97,7 @@
     /**
      * Cancels ARP packet.
      */
-    public void cancelPacket() {
+    private void cancelPacket() {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(EthType.EtherType.ARP.ethType().toShort())
                 .build();
@@ -103,7 +114,7 @@
      * @param gatewayIp gateway ip address
      * @param gatewayMac gateway mac address
      */
-    public void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
+    private void addGateway(IpAddress gatewayIp, MacAddress gatewayMac) {
         checkNotNull(gatewayIp);
         checkNotNull(gatewayMac);
         gateways.put(gatewayIp.getIp4Address(), gatewayMac);
@@ -114,7 +125,7 @@
      *
      * @param gatewayIp gateway ip address
      */
-    public void removeGateway(IpAddress gatewayIp) {
+    private void removeGateway(IpAddress gatewayIp) {
         checkNotNull(gatewayIp);
         gateways.remove(gatewayIp.getIp4Address());
     }
@@ -128,7 +139,7 @@
      * @param context packet context
      * @param ethPacket ethernet packet
      */
-    public void processArpPacket(PacketContext context, Ethernet ethPacket) {
+    private void processArpPacket(PacketContext context, Ethernet ethPacket) {
         ARP arpPacket = (ARP) ethPacket.getPayload();
         if (arpPacket.getOpCode() != ARP.OP_REQUEST) {
            return;
@@ -169,7 +180,7 @@
      * @param gatewayIp gateway ip address to update MAC
      * @param instances set of instances to send gratuitous ARP packet
      */
-    public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Instance> instances) {
+    private void sendGratuitousArp(IpAddress gatewayIp, Set<Instance> instances) {
         MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
         if (gatewayMac == null) {
             log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
@@ -241,4 +252,69 @@
             return MacAddress.NONE;
         }
     }
+
+    private class InternalPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            if (context.isHandled()) {
+                return;
+            }
+            Ethernet ethPacket = context.inPacket().parsed();
+            if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
+                return;
+            }
+            processArpPacket(context, ethPacket);
+        }
+    }
+
+    @Override
+    public void instanceDetected(Instance instance) {
+        VtnService service = getVtnService(instance.serviceId());
+        if (service == null) {
+            return;
+        }
+
+        if (service.networkType().equals(PRIVATE)) {
+            log.trace("Added IP:{} MAC:{}", service.serviceIp(), privateGatewayMac);
+            addGateway(service.serviceIp(), privateGatewayMac);
+            // send gratuitous ARP for the existing VMs when ONOS is restarted
+            sendGratuitousArp(service.serviceIp(), Sets.newHashSet(instance));
+        }
+    }
+
+    @Override
+    public void instanceRemoved(Instance instance) {
+        VtnService service = getVtnService(instance.serviceId());
+        if (service == null) {
+            return;
+        }
+
+        // remove this network gateway from proxy ARP if no instance presents
+        if (service.networkType().equals(PRIVATE) &&
+                getInstances(service.id()).isEmpty()) {
+            removeGateway(service.serviceIp());
+        }
+    }
+
+    @Override
+    protected void readConfiguration() {
+        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+        if (config == null) {
+            log.debug("No configuration found");
+            return;
+        }
+
+        privateGatewayMac = config.privateGatewayMac();
+        log.debug("Added private gateway MAC {}", privateGatewayMac);
+
+        config.publicGateways().entrySet().stream().forEach(entry -> {
+            addGateway(entry.getKey(), entry.getValue());
+            log.debug("Added public gateway IP {}, MAC {}",
+                      entry.getKey(), entry.getValue());
+        });
+        // TODO send gratuitous arp in case the MAC is changed
+
+        super.readConfiguration();
+    }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnInstanceManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnInstanceManager.java
deleted file mode 100644
index bb5e067..0000000
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnInstanceManager.java
+++ /dev/null
@@ -1,437 +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.Sets;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.opencord.cordvtn.api.CordVtnConfig;
-import org.opencord.cordvtn.api.CordVtnService;
-import org.opencord.cordvtn.api.Instance;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.dhcp.DhcpService;
-import org.onosproject.dhcp.IpAssignment;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.Port;
-import org.onosproject.net.config.ConfigFactory;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.basics.SubjectFactories;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.host.DefaultHostDescription;
-import org.onosproject.net.host.HostDescription;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostProvider;
-import org.onosproject.net.host.HostProviderRegistry;
-import org.onosproject.net.host.HostProviderService;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketService;
-import org.onosproject.net.provider.AbstractProvider;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.xosclient.api.OpenStackAccess;
-import org.onosproject.xosclient.api.VtnPort;
-import org.onosproject.xosclient.api.VtnPortApi;
-import org.onosproject.xosclient.api.VtnService;
-import org.onosproject.xosclient.api.VtnServiceApi;
-import org.onosproject.xosclient.api.VtnServiceId;
-import org.onosproject.xosclient.api.XosAccess;
-import org.onosproject.xosclient.api.XosClientService;
-import org.slf4j.Logger;
-
-import java.util.Date;
-import java.util.Map;
-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 com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
-import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.MANAGEMENT;
-import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.PRIVATE;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Adds or removes instances to network services.
- */
-@Component(immediate = true)
-@Service(value = CordVtnInstanceManager.class)
-public class CordVtnInstanceManager extends AbstractProvider implements HostProvider {
-
-    protected final Logger log = getLogger(getClass());
-
-    private static final String XOS_ACCESS_ERROR = "XOS access is not configured";
-    private static final String OPENSTACK_ACCESS_ERROR = "OpenStack access is not configured";
-    private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
-    private static final int DHCP_INFINITE_LEASE = -1;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry configRegistry;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostProviderRegistry hostProviderRegistry;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected PacketService packetService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DhcpService dhcpService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected XosClientService xosClient;
-
-    private final ConfigFactory configFactory =
-            new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
-                @Override
-                public CordVtnConfig createConfig() {
-                    return new CordVtnConfig();
-                }
-            };
-
-    private final ExecutorService eventExecutor =
-            newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-instance", "event-handler"));
-    private final PacketProcessor packetProcessor = new InternalPacketProcessor();
-    private final HostListener hostListener = new InternalHostListener();
-    private final NetworkConfigListener configListener = new InternalConfigListener();
-
-    private ApplicationId appId;
-    private HostProviderService hostProvider;
-    private CordVtnArpProxy arpProxy; // TODO make it a component service
-    private MacAddress privateGatewayMac = MacAddress.NONE;
-    private XosAccess xosAccess = null;
-    private OpenStackAccess osAccess = null;
-
-    /**
-     * Creates an cordvtn host location provider.
-     */
-    public CordVtnInstanceManager() {
-        super(new ProviderId("host", CordVtnService.CORDVTN_APP_ID));
-    }
-
-    @Activate
-    protected void activate() {
-        appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
-
-        arpProxy = new CordVtnArpProxy(appId, packetService, hostService);
-        packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
-        arpProxy.requestPacket();
-
-        hostService.addListener(hostListener);
-        hostProvider = hostProviderRegistry.register(this);
-
-        configRegistry.registerConfigFactory(configFactory);
-        configRegistry.addListener(configListener);
-
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        hostProviderRegistry.unregister(this);
-        hostService.removeListener(hostListener);
-
-        packetService.removeProcessor(packetProcessor);
-
-        configRegistry.unregisterConfigFactory(configFactory);
-        configRegistry.removeListener(configListener);
-
-        eventExecutor.shutdown();
-        log.info("Stopped");
-    }
-
-    @Override
-    public void triggerProbe(Host host) {
-        /*
-         * Note: In CORD deployment, we assume that all hosts are configured.
-         * Therefore no probe is required.
-         */
-    }
-
-    /**
-     * Adds a service instance at a given connect point.
-     *
-     * @param connectPoint connect point of the instance
-     */
-    public void addInstance(ConnectPoint connectPoint) {
-        Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
-        if (port == null) {
-            log.debug("No port found from {}", connectPoint);
-            return;
-        }
-
-        VtnPort vtnPort = getVtnPort(port.annotations().value("portName"));
-        if (vtnPort == null) {
-            return;
-        }
-
-        VtnService vtnService = getVtnService(vtnPort.serviceId());
-        if (vtnService == null) {
-            return;
-        }
-
-        // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
-        // existing instances.
-        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                .set(Instance.SERVICE_TYPE, vtnService.serviceType().toString())
-                .set(Instance.SERVICE_ID, vtnPort.serviceId().id())
-                .set(Instance.PORT_ID, vtnPort.id().id())
-                .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
-
-        HostDescription hostDesc = new DefaultHostDescription(
-                vtnPort.mac(),
-                VlanId.NONE,
-                new HostLocation(connectPoint, System.currentTimeMillis()),
-                Sets.newHashSet(vtnPort.ip()),
-                annotations.build());
-
-        HostId hostId = HostId.hostId(vtnPort.mac());
-        hostProvider.hostDetected(hostId, hostDesc, false);
-    }
-
-    /**
-     * Adds a service instance with given host ID and host description.
-     *
-     * @param hostId host id
-     * @param description host description
-     */
-    public void addInstance(HostId hostId, HostDescription description) {
-        hostProvider.hostDetected(hostId, description, false);
-    }
-
-    /**
-     * Removes a service instance from a given connect point.
-     *
-     * @param connectPoint connect point
-     */
-    public void removeInstance(ConnectPoint connectPoint) {
-        hostService.getConnectedHosts(connectPoint)
-                .stream()
-                .forEach(host -> hostProvider.hostVanished(host.id()));
-    }
-
-    /**
-     * Removes service instance with given host ID.
-     *
-     * @param hostId host id
-     */
-    public void removeInstance(HostId hostId) {
-        hostProvider.hostVanished(hostId);
-    }
-
-    private void instanceDetected(Instance instance) {
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            return;
-        }
-
-        if (service.networkType().equals(PRIVATE)) {
-            arpProxy.addGateway(service.serviceIp(), privateGatewayMac);
-            arpProxy.sendGratuitousArpForGateway(service.serviceIp(), Sets.newHashSet(instance));
-        }
-        if (!instance.isNestedInstance()) {
-            registerDhcpLease(instance, service);
-        }
-    }
-
-    private void instanceRemoved(Instance instance) {
-        VtnService service = getVtnService(instance.serviceId());
-        if (service == null) {
-            return;
-        }
-
-        if (service.networkType().equals(PRIVATE) && getInstances(service.id()).isEmpty()) {
-            arpProxy.removeGateway(service.serviceIp());
-        }
-
-        if (!instance.isNestedInstance()) {
-            dhcpService.removeStaticMapping(instance.mac());
-        }
-    }
-
-    private void registerDhcpLease(Instance instance, VtnService service) {
-        Ip4Address broadcast = Ip4Address.makeMaskedAddress(
-                instance.ipAddress(),
-                service.subnet().prefixLength());
-
-        IpAssignment.Builder ipBuilder = IpAssignment.builder()
-                .ipAddress(instance.ipAddress())
-                .leasePeriod(DHCP_INFINITE_LEASE)
-                .timestamp(new Date())
-                .subnetMask(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()))
-                .broadcast(broadcast)
-                .domainServer(DEFAULT_DNS)
-                .assignmentStatus(Option_RangeNotEnforced);
-
-        if (service.networkType() != MANAGEMENT) {
-            ipBuilder = ipBuilder.routerAddress(service.serviceIp().getIp4Address());
-        }
-
-        log.debug("Set static DHCP mapping for {} {}", instance.mac(), instance.ipAddress());
-        dhcpService.setStaticMapping(instance.mac(), ipBuilder.build());
-    }
-
-    private VtnService getVtnService(VtnServiceId serviceId) {
-        checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
-        checkNotNull(xosAccess, XOS_ACCESS_ERROR);
-
-        // TODO remove openstack access when XOS provides all information
-        VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
-        VtnService service = serviceApi.service(serviceId, osAccess);
-        if (service == null) {
-            log.warn("Failed to get VtnService for {}", serviceId);
-        }
-        return service;
-    }
-
-    private VtnPort getVtnPort(String portName) {
-        checkNotNull(osAccess, OPENSTACK_ACCESS_ERROR);
-        checkNotNull(xosAccess, XOS_ACCESS_ERROR);
-
-        // TODO remove openstack access when XOS provides all information
-        VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
-        VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
-        if (vtnPort == null) {
-            log.warn("Failed to get port information of {}", portName);
-        }
-        return vtnPort;
-    }
-
-    private Set<Instance> getInstances(VtnServiceId serviceId) {
-        return StreamSupport.stream(hostService.getHosts().spliterator(), false)
-                .filter(host -> Objects.equals(
-                        serviceId.id(),
-                        host.annotations().value(Instance.SERVICE_ID)))
-                .map(Instance::of)
-                .collect(Collectors.toSet());
-    }
-
-    private void readConfiguration() {
-        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
-        if (config == null) {
-            log.debug("No configuration found");
-            return;
-        }
-
-        log.info("Load CORD-VTN configurations");
-
-        xosAccess = config.xosAccess();
-        osAccess = config.openstackAccess();
-        privateGatewayMac = config.privateGatewayMac();
-
-        Map<IpAddress, MacAddress> publicGateways = config.publicGateways();
-        publicGateways.entrySet()
-                .stream()
-                .forEach(entry -> {
-                    arpProxy.addGateway(entry.getKey(), entry.getValue());
-                    log.debug("Added public gateway IP {}, MAC {}",
-                              entry.getKey(), entry.getValue());
-                });
-        // TODO notice gateway MAC change to VMs holds this gateway IP
-    }
-
-    private class InternalHostListener implements HostListener {
-
-        @Override
-        public void event(HostEvent event) {
-            Host host = event.subject();
-            if (!mastershipService.isLocalMaster(host.location().deviceId())) {
-                // do not allow to proceed without mastership
-                return;
-            }
-
-            Instance instance = Instance.of(host);
-            switch (event.type()) {
-                case HOST_UPDATED:
-                case HOST_ADDED:
-                    eventExecutor.execute(() -> instanceDetected(instance));
-                    break;
-                case HOST_REMOVED:
-                    eventExecutor.execute(() -> instanceRemoved(instance));
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private class InternalPacketProcessor implements PacketProcessor {
-
-        @Override
-        public void process(PacketContext context) {
-            if (context.isHandled()) {
-                return;
-            }
-            Ethernet ethPacket = context.inPacket().parsed();
-            if (ethPacket == null || ethPacket.getEtherType() != Ethernet.TYPE_ARP) {
-                return;
-            }
-            arpProxy.processArpPacket(context, ethPacket);
-        }
-    }
-
-    private class InternalConfigListener implements NetworkConfigListener {
-
-        @Override
-        public void event(NetworkConfigEvent event) {
-            if (!event.configClass().equals(CordVtnConfig.class)) {
-                return;
-            }
-
-            switch (event.type()) {
-                case CONFIG_ADDED:
-                case CONFIG_UPDATED:
-                    readConfiguration();
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-}
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
index c037c05..fbac581 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnNodeManager.java
@@ -33,7 +33,7 @@
 import org.opencord.cordvtn.api.CordVtnConfig;
 import org.opencord.cordvtn.api.CordVtnNode;
 import org.opencord.cordvtn.api.CordVtnNodeState;
-import org.opencord.cordvtn.api.CordVtnService;
+import org.opencord.cordvtn.api.InstanceService;
 import org.opencord.cordvtn.api.NetworkAddress;
 import org.opencord.cordvtn.api.SshAccessInfo;
 import org.onosproject.core.ApplicationId;
@@ -85,9 +85,9 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
 import static org.onlab.util.Tools.groupedThreads;
-import static org.opencord.cordvtn.impl.CordVtnPipeline.DEFAULT_TUNNEL;
 import static org.onosproject.net.Device.Type.SWITCH;
 import static org.onosproject.net.behaviour.TunnelDescription.Type.VXLAN;
+import static org.opencord.cordvtn.api.Constants.*;
 import static org.opencord.cordvtn.impl.RemoteIpCommandUtil.*;
 import static org.slf4j.LoggerFactory.getLogger;
 
@@ -109,11 +109,6 @@
             .register(SshAccessInfo.class)
             .register(NetworkAddress.class);
 
-    private static final String DEFAULT_BRIDGE = "br-int";
-    private static final String VPORT_PREFIX = "tap";
-    private static final String OK = "OK";
-    private static final String NO = "NO";
-
     private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
         {
             put("key", "flow");
@@ -154,7 +149,7 @@
     protected LeadershipService leadershipService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CordVtnInstanceManager instanceManager;
+    protected InstanceService instanceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordVtnPipeline pipeline;
@@ -219,7 +214,7 @@
 
     @Activate
     protected void activate() {
-        appId = coreService.getAppId(CordVtnService.CORDVTN_APP_ID);
+        appId = coreService.getAppId(CORDVTN_APP_ID);
 
         localNodeId = clusterService.getLocalNode().id();
         leadershipService.runForLeadership(appId.name());
@@ -315,13 +310,13 @@
                         "Data plane IP added to br-int : %s (%s)%n" +
                         "Local management IP added to br-int : %s (%s)",
                 node.state(),
-                isBrIntCreated(node) ? OK : NO, node.intBrId(),
-                isTunnelIntfCreated(node) ? OK : NO,
-                isDataPlaneIntfAdded(node) ? OK : NO, node.dpIntf(),
+                isBrIntCreated(node) ? MSG_OK : MSG_NO, node.intBrId(),
+                isTunnelIntfCreated(node) ? MSG_OK : MSG_NO,
+                isDataPlaneIntfAdded(node) ? MSG_OK : MSG_NO, node.dpIntf(),
                 isInterfaceUp(session, node.dpIntf()) &&
-                        getCurrentIps(session, node.dpIntf()).isEmpty() ? OK : NO, node.dpIntf(),
-                intBrIps.contains(node.dpIp().ip()) ? OK : NO, node.dpIp().cidr(),
-                intBrIps.contains(node.localMgmtIp().ip()) ? OK : NO, node.localMgmtIp().cidr());
+                        getCurrentIps(session, node.dpIntf()).isEmpty() ? MSG_OK : MSG_NO, node.dpIntf(),
+                intBrIps.contains(node.dpIp().ip()) ? MSG_OK : MSG_NO, node.dpIp().cidr(),
+                intBrIps.contains(node.localMgmtIp().ip()) ? MSG_OK : MSG_NO, node.localMgmtIp().cidr());
 
         disconnect(session);
 
@@ -449,12 +444,12 @@
         deviceService.getPorts(node.intBrId()).stream()
                 .filter(port -> portName(port).startsWith(VPORT_PREFIX) &&
                         port.isEnabled())
-                .forEach(port -> instanceManager.addInstance(connectPoint(port)));
+                .forEach(port -> instanceService.addInstance(connectPoint(port)));
 
         hostService.getHosts().forEach(host -> {
             if (deviceService.getPort(host.location().deviceId(),
                                       host.location().port()) == null) {
-                instanceManager.removeInstance(connectPoint(host));
+                instanceService.removeInstance(connectPoint(host));
             }
         });
 
@@ -872,7 +867,7 @@
 
             if (portName.startsWith(VPORT_PREFIX)) {
                 if (isNodeStateComplete(node)) {
-                    instanceManager.addInstance(connectPoint(port));
+                    instanceService.addInstance(connectPoint(port));
                 } else {
                     log.debug("VM is detected on incomplete node, ignore it.", portName);
                 }
@@ -900,7 +895,7 @@
 
             if (portName.startsWith(VPORT_PREFIX)) {
                 if (isNodeStateComplete(node)) {
-                    instanceManager.removeInstance(connectPoint(port));
+                    instanceService.removeInstance(connectPoint(port));
                 } else {
                     log.debug("VM is vanished from incomplete node, ignore it.", portName);
                 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java b/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java
index f5799c3..8aa0946 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java
+++ b/src/main/java/org/opencord/cordvtn/impl/CordVtnPipeline.java
@@ -28,8 +28,8 @@
 import org.onlab.packet.TpPort;
 import org.onlab.packet.VlanId;
 import org.onlab.util.ItemNotFoundException;
+import org.opencord.cordvtn.api.Constants;
 import org.opencord.cordvtn.api.CordVtnNode;
-import org.opencord.cordvtn.api.CordVtnService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.Device;
@@ -90,14 +90,12 @@
 
     public static final int VXLAN_UDP_PORT = 4789;
     public static final VlanId VLAN_WAN = VlanId.vlanId((short) 500);
-    public static final String DEFAULT_TUNNEL = "vxlan";
-    private static final String PORT_NAME = "portName";
 
     private ApplicationId appId;
 
     @Activate
     protected void activate() {
-        appId = coreService.registerApplication(CordVtnService.CORDVTN_APP_ID);
+        appId = coreService.registerApplication(Constants.CORDVTN_APP_ID);
         log.info("Started");
     }
 
@@ -388,18 +386,17 @@
     public ExtensionTreatment tunnelDstTreatment(DeviceId deviceId, Ip4Address remoteIp) {
         try {
             Device device = deviceService.getDevice(deviceId);
-
-            if (device.is(ExtensionTreatmentResolver.class)) {
-                ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
-                ExtensionTreatment treatment =
-                        resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
-                treatment.setPropertyValue("tunnelDst", remoteIp);
-                return treatment;
-            } else {
-                log.warn("The extension treatment resolving behaviour is not supported in device {}",
-                         device.id().toString());
+            if (!device.is(ExtensionTreatmentResolver.class)) {
+                log.error("The extension treatment is not supported");
                 return null;
+
             }
+
+            ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+            ExtensionTreatment treatment =
+                    resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+            treatment.setPropertyValue("tunnelDst", remoteIp);
+            return treatment;
         } catch (ItemNotFoundException | UnsupportedOperationException |
                 ExtensionPropertyException e) {
             log.error("Failed to get extension instruction {}", deviceId);
diff --git a/src/main/java/org/opencord/cordvtn/impl/CordVtn.java b/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
similarity index 93%
rename from src/main/java/org/opencord/cordvtn/impl/CordVtn.java
rename to src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
index 854bd40..2eb73db 100644
--- a/src/main/java/org/opencord/cordvtn/impl/CordVtn.java
+++ b/src/main/java/org/opencord/cordvtn/impl/DependencyManager.java
@@ -28,7 +28,7 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.opencord.cordvtn.api.CordVtnNode;
-import org.opencord.cordvtn.api.CordVtnService;
+import org.opencord.cordvtn.api.DependencyService;
 import org.opencord.cordvtn.api.Instance;
 import org.onosproject.core.DefaultGroupId;
 import org.onosproject.core.GroupId;
@@ -59,8 +59,6 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
 import static org.opencord.cordvtn.impl.CordVtnPipeline.*;
 import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -70,16 +68,21 @@
  */
 @Component(immediate = true)
 @Service
-public class CordVtn extends AbstractInstanceHandler implements CordVtnService {
+public class DependencyManager extends AbstractInstanceHandler implements DependencyService {
 
     protected final Logger log = getLogger(getClass());
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected GroupService groupService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnPipeline pipeline;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnNodeManager nodeManager;
+
     @Activate
     protected void activate() {
-        eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn", "event-handler"));
         super.activate();
     }
 
@@ -89,7 +92,7 @@
     }
 
     @Override
-    public void createServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId,
+    public void createDependency(VtnServiceId tServiceId, VtnServiceId pServiceId,
                                         boolean isBidirectional) {
         VtnService tService = getVtnService(tServiceId);
         VtnService pService = getVtnService(pServiceId);
@@ -105,7 +108,7 @@
     }
 
     @Override
-    public void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId) {
+    public void removeDependency(VtnServiceId tServiceId, VtnServiceId pServiceId) {
         VtnService tService = getVtnService(tServiceId);
         VtnService pService = getVtnService(pServiceId);
 
@@ -128,9 +131,9 @@
 
         // TODO get bidirectional information from XOS once XOS supports
         service.tenantServices().stream().forEach(
-                tServiceId -> createServiceDependency(tServiceId, service.id(), true));
+                tServiceId -> createDependency(tServiceId, service.id(), true));
         service.providerServices().stream().forEach(
-                pServiceId -> createServiceDependency(service.id(), pServiceId, true));
+                pServiceId -> createDependency(service.id(), pServiceId, true));
 
         updateProviderServiceInstances(service);
     }
@@ -180,6 +183,7 @@
                         groupKey,
                         new GroupBuckets(bucketsToRemove),
                         groupKey, appId);
+                log.debug("Removes instances from {} : {}", service.name(), bucketsToRemove);
             }
 
             List<GroupBucket> bucketsToAdd = Lists.newArrayList(newBuckets);
@@ -190,6 +194,7 @@
                         groupKey,
                         new GroupBuckets(bucketsToAdd),
                         groupKey, appId);
+                log.debug("Adds instances to {} : {}", service.name(), bucketsToRemove);
             }
         }
     }
diff --git a/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
new file mode 100644
index 0000000..3041bbf
--- /dev/null
+++ b/src/main/java/org/opencord/cordvtn/impl/InstanceManager.java
@@ -0,0 +1,313 @@
+/*
+ * 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.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.opencord.cordvtn.api.CordVtnConfig;
+import org.opencord.cordvtn.api.Instance;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.dhcp.DhcpService;
+import org.onosproject.dhcp.IpAssignment;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Port;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.xosclient.api.OpenStackAccess;
+import org.onosproject.xosclient.api.VtnPort;
+import org.onosproject.xosclient.api.VtnPortApi;
+import org.onosproject.xosclient.api.VtnService;
+import org.onosproject.xosclient.api.VtnServiceApi;
+import org.onosproject.xosclient.api.VtnServiceId;
+import org.onosproject.xosclient.api.XosAccess;
+import org.onosproject.xosclient.api.XosClientService;
+import org.opencord.cordvtn.api.InstanceService;
+import org.slf4j.Logger;
+
+import java.util.Date;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
+import static org.onosproject.xosclient.api.VtnServiceApi.NetworkType.MANAGEMENT;
+import static org.opencord.cordvtn.api.Constants.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Adds or removes instances to network services.
+ */
+@Component(immediate = true)
+@Service
+public class InstanceManager extends AbstractProvider implements HostProvider,
+        InstanceService {
+
+    protected final Logger log = getLogger(getClass());
+
+    private static final Ip4Address DEFAULT_DNS = Ip4Address.valueOf("8.8.8.8");
+    private static final int DHCP_INFINITE_LEASE = -1;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry configRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostProviderRegistry hostProviderRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DhcpService dhcpService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected XosClientService xosClient;
+
+    private final ConfigFactory configFactory =
+            new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, CordVtnConfig.class, "cordvtn") {
+                @Override
+                public CordVtnConfig createConfig() {
+                    return new CordVtnConfig();
+                }
+            };
+
+    private final ExecutorService eventExecutor =
+            newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-instance", "event-handler"));
+    private final NetworkConfigListener configListener = new InternalConfigListener();
+
+    private ApplicationId appId;
+    private HostProviderService hostProvider;
+    private XosAccess xosAccess = null;
+    private OpenStackAccess osAccess = null;
+
+    /**
+     * Creates an cordvtn host location provider.
+     */
+    public InstanceManager() {
+        super(new ProviderId("host", CORDVTN_APP_ID));
+    }
+
+    @Activate
+    protected void activate() {
+        appId = coreService.registerApplication(CORDVTN_APP_ID);
+
+        hostProvider = hostProviderRegistry.register(this);
+        configRegistry.registerConfigFactory(configFactory);
+        configRegistry.addListener(configListener);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        hostProviderRegistry.unregister(this);
+
+        configRegistry.unregisterConfigFactory(configFactory);
+        configRegistry.removeListener(configListener);
+
+        eventExecutor.shutdown();
+        log.info("Stopped");
+    }
+
+    @Override
+    public void triggerProbe(Host host) {
+        /*
+         * Note: In CORD deployment, we assume that all hosts are configured.
+         * Therefore no probe is required.
+         */
+    }
+
+    @Override
+    public void addInstance(ConnectPoint connectPoint) {
+        Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
+        if (port == null) {
+            log.debug("No port found from {}", connectPoint);
+            return;
+        }
+
+        VtnPort vtnPort = getVtnPort(port.annotations().value(PORT_NAME));
+        if (vtnPort == null) {
+            return;
+        }
+
+        VtnService vtnService = getVtnService(vtnPort.serviceId());
+        if (vtnService == null) {
+            return;
+        }
+
+        // register DHCP lease for the new instance
+        registerDhcpLease(vtnPort.mac(), vtnPort.ip().getIp4Address(), vtnService);
+
+        // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
+        // existing instances.
+        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+                .set(Instance.SERVICE_TYPE, vtnService.serviceType().toString())
+                .set(Instance.SERVICE_ID, vtnPort.serviceId().id())
+                .set(Instance.PORT_ID, vtnPort.id().id())
+                .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
+
+        HostDescription hostDesc = new DefaultHostDescription(
+                vtnPort.mac(),
+                VlanId.NONE,
+                new HostLocation(connectPoint, System.currentTimeMillis()),
+                Sets.newHashSet(vtnPort.ip()),
+                annotations.build());
+
+        HostId hostId = HostId.hostId(vtnPort.mac());
+        hostProvider.hostDetected(hostId, hostDesc, false);
+    }
+
+    @Override
+    public void addNestedInstance(HostId hostId, HostDescription description) {
+        DefaultAnnotations annotations  = DefaultAnnotations.builder()
+                .set(Instance.NESTED_INSTANCE, Instance.TRUE)
+                .build();
+        annotations = annotations.merge(annotations, description.annotations());
+
+        HostDescription nestedHost = new DefaultHostDescription(
+                description.hwAddress(),
+                description.vlan(),
+                description.location(),
+                description.ipAddress(),
+                annotations);
+
+        hostProvider.hostDetected(hostId, nestedHost, false);
+    }
+
+    @Override
+    public void removeInstance(ConnectPoint connectPoint) {
+        hostService.getConnectedHosts(connectPoint).stream()
+                .forEach(host -> {
+                    dhcpService.removeStaticMapping(host.mac());
+                    hostProvider.hostVanished(host.id());
+                });
+    }
+
+    @Override
+    public void removeNestedInstance(HostId hostId) {
+        hostProvider.hostVanished(hostId);
+    }
+
+    private void registerDhcpLease(MacAddress macAddr, Ip4Address ipAddr, VtnService service) {
+        Ip4Address broadcast = Ip4Address.makeMaskedAddress(
+                ipAddr,
+                service.subnet().prefixLength());
+
+        IpAssignment.Builder ipBuilder = IpAssignment.builder()
+                .ipAddress(ipAddr)
+                .leasePeriod(DHCP_INFINITE_LEASE)
+                .timestamp(new Date())
+                .subnetMask(Ip4Address.makeMaskPrefix(service.subnet().prefixLength()))
+                .broadcast(broadcast)
+                .domainServer(DEFAULT_DNS)
+                .assignmentStatus(Option_RangeNotEnforced);
+
+        if (service.networkType() != MANAGEMENT) {
+            ipBuilder = ipBuilder.routerAddress(service.serviceIp().getIp4Address());
+        }
+
+        log.debug("Set static DHCP mapping for {} {}", macAddr, ipAddr);
+        dhcpService.setStaticMapping(macAddr, ipBuilder.build());
+    }
+
+    private VtnService getVtnService(VtnServiceId serviceId) {
+        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
+        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
+
+        // TODO remove openstack access when XOS provides all information
+        VtnServiceApi serviceApi = xosClient.getClient(xosAccess).vtnService();
+        VtnService service = serviceApi.service(serviceId, osAccess);
+        if (service == null) {
+            log.warn("Failed to get VtnService for {}", serviceId);
+        }
+        return service;
+    }
+
+    private VtnPort getVtnPort(String portName) {
+        checkNotNull(osAccess, ERROR_OPENSTACK_ACCESS);
+        checkNotNull(xosAccess, ERROR_XOS_ACCESS);
+
+        // TODO remove openstack access when XOS provides all information
+        VtnPortApi portApi = xosClient.getClient(xosAccess).vtnPort();
+        VtnPort vtnPort = portApi.vtnPort(portName, osAccess);
+        if (vtnPort == null) {
+            log.warn("Failed to get port information of {}", portName);
+        }
+        return vtnPort;
+    }
+
+    private void readConfiguration() {
+        CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
+        if (config == null) {
+            log.debug("No configuration found");
+            return;
+        }
+
+        log.info("Load CORD-VTN configurations");
+        xosAccess = config.xosAccess();
+        osAccess = config.openstackAccess();
+    }
+
+    private class InternalConfigListener implements NetworkConfigListener {
+
+        @Override
+        public void event(NetworkConfigEvent event) {
+            if (!event.configClass().equals(CordVtnConfig.class)) {
+                return;
+            }
+
+            switch (event.type()) {
+                case CONFIG_ADDED:
+                case CONFIG_UPDATED:
+                    readConfiguration();
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/src/main/java/org/opencord/cordvtn/impl/service/DefaultInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
similarity index 95%
rename from src/main/java/org/opencord/cordvtn/impl/service/DefaultInstanceHandler.java
rename to src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
index 0c19509..98673cf 100644
--- a/src/main/java/org/opencord/cordvtn/impl/service/DefaultInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/DefaultInstanceHandler.java
@@ -13,12 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.impl.service;
+package org.opencord.cordvtn.impl.handler;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
@@ -34,12 +36,11 @@
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
 import org.onosproject.xosclient.api.VtnService;
+import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
 import java.util.Optional;
 
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.DEFAULT;
 
 /**
@@ -48,10 +49,15 @@
 @Component(immediate = true)
 public class DefaultInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnPipeline pipeline;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnNodeManager nodeManager;
+
     @Activate
     protected void activate() {
         serviceType = Optional.of(DEFAULT);
-        eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-default", "event-handler"));
         super.activate();
     }
 
@@ -112,7 +118,6 @@
                 .transition(CordVtnPipeline.TABLE_ACCESS_TYPE)
                 .build();
 
-
         FlowRule flowRule = DefaultFlowRule.builder()
                 .fromApp(appId)
                 .withSelector(selector)
diff --git a/src/main/java/org/opencord/cordvtn/impl/service/ManagementInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
similarity index 85%
rename from src/main/java/org/opencord/cordvtn/impl/service/ManagementInstanceHandler.java
rename to src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
index 2e49a0b..fe30781 100644
--- a/src/main/java/org/opencord/cordvtn/impl/service/ManagementInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/ManagementInstanceHandler.java
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.impl.service;
+package org.opencord.cordvtn.impl.handler;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.Ethernet;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.DefaultFlowRule;
@@ -34,8 +36,6 @@
 
 import java.util.Optional;
 
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.MANAGEMENT;
 
 /**
@@ -44,10 +44,12 @@
 @Component(immediate = true)
 public class ManagementInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnPipeline pipeline;
+
     @Activate
     protected void activate() {
         serviceType = Optional.of(MANAGEMENT);
-        eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-mgmt", "event-handler"));
         super.activate();
     }
 
@@ -153,7 +155,7 @@
                 .withTreatment(treatment)
                 .withPriority(CordVtnPipeline.PRIORITY_MANAGEMENT)
                 .forDevice(instance.deviceId())
-                .forTable(CordVtnPipeline.TABLE_ACCESS_TYPE)
+                .forTable(CordVtnPipeline.TABLE_ZERO)
                 .makePermanent()
                 .build();
 
@@ -182,5 +184,27 @@
                 .build();
 
         pipeline.processFlowRule(install, flowRule);
+
+        selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(instance.ipAddress().toIpPrefix())
+                .build();
+
+        treatment = DefaultTrafficTreatment.builder()
+                .setEthDst(instance.mac())
+                .setOutput(instance.portNumber())
+                .build();
+
+        flowRule = DefaultFlowRule.builder()
+                .fromApp(appId)
+                .withSelector(selector)
+                .withTreatment(treatment)
+                .withPriority(CordVtnPipeline.PRIORITY_DEFAULT)
+                .forDevice(instance.deviceId())
+                .forTable(CordVtnPipeline.TABLE_DST_IP)
+                .makePermanent()
+                .build();
+
+        pipeline.processFlowRule(install, flowRule);
     }
 }
diff --git a/src/main/java/org/opencord/cordvtn/impl/service/OltAgentInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/OltAgentInstanceHandler.java
similarity index 93%
rename from src/main/java/org/opencord/cordvtn/impl/service/OltAgentInstanceHandler.java
rename to src/main/java/org/opencord/cordvtn/impl/handler/OltAgentInstanceHandler.java
index b409e79..853963e 100644
--- a/src/main/java/org/opencord/cordvtn/impl/service/OltAgentInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/OltAgentInstanceHandler.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.impl.service;
+package org.opencord.cordvtn.impl.handler;
 
 import com.google.common.collect.Maps;
 import org.apache.felix.scr.annotations.Activate;
@@ -37,8 +37,6 @@
 import java.util.Optional;
 import java.util.Set;
 
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.OLT_AGENT;
 
 /**
@@ -62,12 +60,10 @@
     @Activate
     protected void activate() {
         serviceType = Optional.of(OLT_AGENT);
-        eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-olt", "event-handler"));
+        configListener = new InternalConfigListener();
+        super.activate();
 
         configRegistry.registerConfigFactory(configFactory);
-        configListener = new InternalConfigListener();
-
-        super.activate();
     }
 
     @Deactivate
diff --git a/src/main/java/org/opencord/cordvtn/impl/service/VsgInstanceHandler.java b/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
similarity index 95%
rename from src/main/java/org/opencord/cordvtn/impl/service/VsgInstanceHandler.java
rename to src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
index e6fd643..1f96023 100644
--- a/src/main/java/org/opencord/cordvtn/impl/service/VsgInstanceHandler.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/VsgInstanceHandler.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.opencord.cordvtn.impl.service;
+package org.opencord.cordvtn.impl.handler;
 
 import com.google.common.base.Strings;
 import com.google.common.collect.Sets;
@@ -28,10 +28,10 @@
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.opencord.cordvtn.api.InstanceService;
 import org.opencord.cordvtn.impl.AbstractInstanceHandler;
 import org.opencord.cordvtn.api.Instance;
 import org.opencord.cordvtn.api.InstanceHandler;
-import org.opencord.cordvtn.impl.CordVtnInstanceManager;
 import org.onosproject.net.DefaultAnnotations;
 import org.onosproject.net.HostId;
 import org.onosproject.net.PortNumber;
@@ -50,6 +50,7 @@
 import org.onosproject.net.host.DefaultHostDescription;
 import org.onosproject.net.host.HostDescription;
 import org.onosproject.xosclient.api.VtnPort;
+import org.opencord.cordvtn.impl.CordVtnNodeManager;
 import org.opencord.cordvtn.impl.CordVtnPipeline;
 
 import java.util.Map;
@@ -57,8 +58,6 @@
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.net.flow.criteria.Criterion.Type.IPV4_DST;
 import static org.onosproject.net.flow.instructions.L2ModificationInstruction.L2SubType.VLAN_PUSH;
 import static org.onosproject.xosclient.api.VtnServiceApi.ServiceType.VSG;
@@ -70,6 +69,12 @@
 @Service(value = VsgInstanceHandler.class)
 public final class VsgInstanceHandler extends AbstractInstanceHandler implements InstanceHandler {
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnPipeline pipeline;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CordVtnNodeManager nodeManager;
+
     private static final String STAG = "stag";
     private static final String VSG_VM = "vsgVm";
 
@@ -77,12 +82,11 @@
     protected FlowRuleService flowRuleService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CordVtnInstanceManager instanceManager;
+    protected InstanceService instanceService;
 
     @Activate
     protected void activate() {
         serviceType = Optional.of(VSG);
-        eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-vsg", "event-handler"));
         super.activate();
     }
 
@@ -106,9 +110,9 @@
             Instance vsgVm = Instance.of(hostService.getHost(HostId.hostId(vsgVmId)));
             VtnPort vtnPort = getVtnPort(vsgVm);
             if (vtnPort == null || getStag(vtnPort) == null) {
+                log.warn("Failed to get vSG information {}", vsgVm);
                 return;
             }
-
             populateVsgRules(vsgVm, getStag(vtnPort),
                              nodeManager.dpPort(vsgVm.deviceId()),
                              vtnPort.addressPairs().keySet(),
@@ -119,7 +123,6 @@
             if (vtnPort == null || getStag(vtnPort) == null) {
                 return;
             }
-
             log.info("vSG VM detected {}", instance);
 
             // insert vSG containers inside the vSG VM as a host
@@ -197,7 +200,7 @@
                 .filter(host -> !vsgInstances.values().contains(host.mac()))
                 .forEach(host -> {
                     log.info("Removed vSG {}", host.toString());
-                    instanceManager.removeInstance(host.id());
+                    instanceService.removeNestedInstance(host.id());
                 });
     }
 
@@ -212,7 +215,6 @@
                 .set(Instance.SERVICE_TYPE, vsgVm.serviceType().toString())
                 .set(Instance.SERVICE_ID, vsgVm.serviceId().id())
                 .set(Instance.PORT_ID, vsgVm.portId().id())
-                .set(Instance.NESTED_INSTANCE, Instance.TRUE)
                 .set(STAG, stag)
                 .set(VSG_VM, vsgVm.host().id().toString())
                 .set(Instance.CREATE_TIME, String.valueOf(System.currentTimeMillis()));
@@ -224,7 +226,7 @@
                 Sets.newHashSet(vsgWanIp),
                 annotations.build());
 
-        instanceManager.addInstance(hostId, hostDesc);
+        instanceService.addNestedInstance(hostId, hostDesc);
     }
 
     private void populateVsgRules(Instance vsgVm, VlanId stag, PortNumber dpPort,
diff --git a/src/main/java/org/opencord/cordvtn/impl/service/package-info.java b/src/main/java/org/opencord/cordvtn/impl/handler/package-info.java
similarity index 94%
rename from src/main/java/org/opencord/cordvtn/impl/service/package-info.java
rename to src/main/java/org/opencord/cordvtn/impl/handler/package-info.java
index 7d00d9f..c4c0aa0 100644
--- a/src/main/java/org/opencord/cordvtn/impl/service/package-info.java
+++ b/src/main/java/org/opencord/cordvtn/impl/handler/package-info.java
@@ -17,4 +17,4 @@
 /**
  * Implementation of instance handlers for various network services.
  */
-package org.opencord.cordvtn.impl.service;
\ No newline at end of file
+package org.opencord.cordvtn.impl.handler;
\ No newline at end of file