BUG 6841: Few Remote flows not deleted on DPNs
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnInterfaceManager.java
index d7dea34601140411fe58204032496f209e0d8e42..6963239076acda6a7c5863a4ad311d497872d819 100644 (file)
  */
 package org.opendaylight.netvirt.vpnmanager;
 
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
-import org.opendaylight.netvirt.fibmanager.api.IFibManager;
-import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
-
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Iterators;
+import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.JdkFutureAdapters;
-
-import org.opendaylight.controller.md.sal.binding.api.*;
-import org.opendaylight.genius.mdsalutil.*;
-import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
+import org.opendaylight.genius.mdsalutil.ActionInfo;
+import org.opendaylight.genius.mdsalutil.ActionType;
+import org.opendaylight.genius.mdsalutil.FlowEntity;
+import org.opendaylight.genius.mdsalutil.InstructionInfo;
+import org.opendaylight.genius.mdsalutil.InstructionType;
+import org.opendaylight.genius.mdsalutil.MDSALUtil;
+import org.opendaylight.genius.mdsalutil.MatchFieldType;
+import org.opendaylight.genius.mdsalutil.MatchInfo;
+import org.opendaylight.genius.mdsalutil.MetaDataUtil;
+import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.genius.utils.ServiceIndex;
+import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
+import org.opendaylight.netvirt.fibmanager.api.IFibManager;
+import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
+import org.opendaylight.netvirt.vpnmanager.intervpnlink.InterVpnLinkUtil;
+import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.VpnTargets;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.af.config.vpntargets.VpnTarget;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.LabelRouteMap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRoute;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.SubnetRouteBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.label.route.map.LabelRouteInfoKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AddDpnEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AddDpnEventBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.NeutronRouterDpns;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.PrefixToInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RemoveDpnEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RemoveDpnEventBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.add.dpn.event.AddEventData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.add.dpn.event.AddEventDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.prefix.to._interface.vpn.ids.Prefixes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnListBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.RouterDpnListKey;
@@ -37,216 +117,134 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neu
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfaces;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfacesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.neutron.router.dpns.router.dpn.list.dpn.vpninterfaces.list.RouterInterfacesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.remove.dpn.event.RemoveEventData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.remove.dpn.event.RemoveEventDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.id.to.vpn.instance.VpnIds;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.IpAddresses;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfacesKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.FibEntries;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTables;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.fibentries.VrfTablesKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntryKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseInput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.SendArpResponseInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
-
-import java.math.BigInteger;
-import java.util.Collection;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-
-import com.google.common.base.Optional;
-
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.NeutronvpnService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.servicebinding.rev160406.service.bindings.services.info.BoundServices;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
-import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.inter.vpn.link.rev160311.inter.vpn.links.InterVpnLink;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstanceKey;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
 import org.opendaylight.yangtools.yang.common.RpcError;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface> implements AutoCloseable {
+public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInterface, VpnInterfaceManager>
+        implements AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(VpnInterfaceManager.class);
-    private ListenerRegistration<DataChangeListener> listenerRegistration, opListenerRegistration;
-    private static final ThreadFactory threadFactory = new ThreadFactoryBuilder()
-        .setNameFormat("NV-VpnIntfMgr-%d").build();
-    private final DataBroker broker;
+    private final DataBroker dataBroker;
     private final IBgpManager bgpManager;
-    private IFibManager fibManager;
-    private IMdsalApiManager mdsalManager;
-    private OdlInterfaceRpcService interfaceManager;
-    private ItmRpcService itmProvider;
-    private IdManagerService idManager;
-    private OdlArputilService arpManager;
-    private NeutronvpnService neuService;
-    private VpnSubnetRouteHandler vpnSubnetRouteHandler;
+    private final IFibManager fibManager;
+    private final IMdsalApiManager mdsalManager;
+    private final IdManagerService idManager;
+    private final OdlArputilService arpManager;
+    private final OdlInterfaceRpcService ifaceMgrRpcService;
+    private final NotificationPublishService notificationPublishService;
     private ConcurrentHashMap<String, Runnable> vpnIntfMap = new ConcurrentHashMap<String, Runnable>();
     private ExecutorService executorService = Executors.newSingleThreadExecutor();
-    private InterfaceStateChangeListener interfaceListener;
-    private VpnInterfaceOpListener vpnInterfaceOpListener;
-    private ArpNotificationHandler arpNotificationHandler;
-    protected enum UpdateRouteAction {
-        ADVERTISE_ROUTE, WITHDRAW_ROUTE
-    }
-    /**
-     * Responsible for listening to data change related to VPN Interface
-     * Bind VPN Service on the interface and informs the BGP service
-     *
-     * @param db - dataBroker service reference
-     */
-    public VpnInterfaceManager(final DataBroker db, final IBgpManager bgpManager, NotificationService notificationService) {
-        super(VpnInterface.class);
-        broker = db;
-        this.bgpManager = bgpManager;
-        interfaceListener = new InterfaceStateChangeListener(db, this);
-        vpnInterfaceOpListener = new VpnInterfaceOpListener();
-        arpNotificationHandler = new ArpNotificationHandler(this, broker);
-        notificationService.registerNotificationListener(arpNotificationHandler);
-        vpnSubnetRouteHandler = new VpnSubnetRouteHandler(broker, bgpManager, this);
-        notificationService.registerNotificationListener(vpnSubnetRouteHandler);
-        registerListener(db);
-    }
 
-    public void setMdsalManager(IMdsalApiManager mdsalManager) {
+    public VpnInterfaceManager(final DataBroker dataBroker,
+                               final IBgpManager bgpManager,
+                               final OdlArputilService arpManager,
+                               final IdManagerService idManager,
+                               final IMdsalApiManager mdsalManager,
+                               final IFibManager fibManager,
+                               final OdlInterfaceRpcService ifaceMgrRpcService,
+                               final NotificationPublishService notificationPublishService) {
+        super(VpnInterface.class, VpnInterfaceManager.class);
+        this.dataBroker = dataBroker;
+        this.bgpManager = bgpManager;
+        this.arpManager = arpManager;
+        this.idManager = idManager;
         this.mdsalManager = mdsalManager;
-    }
-
-    public void setInterfaceManager(OdlInterfaceRpcService interfaceManager) {
-        this.interfaceManager = interfaceManager;
-        interfaceListener.setInterfaceManager(interfaceManager);
-    }
-
-    public void setITMProvider(ItmRpcService itmProvider) {
-        this.itmProvider = itmProvider;
-    }
-
-    public void setFibManager(IFibManager fibManager) {
         this.fibManager = fibManager;
+        this.ifaceMgrRpcService = ifaceMgrRpcService;
+        this.notificationPublishService = notificationPublishService;
     }
 
-    public IFibManager getFibManager() {
-        return this.fibManager;
-    }
-
-    public void setIdManager(IdManagerService idManager) {
-        this.idManager = idManager;
-        vpnSubnetRouteHandler.setIdManager(idManager);
-    }
-
-    public void setArpManager(OdlArputilService arpManager) {
-        this.arpManager = arpManager;
+    public void start() {
+        LOG.info("{} start", getClass().getSimpleName());
+        registerListener(LogicalDatastoreType.CONFIGURATION, dataBroker);
     }
 
-    public void setNeutronvpnManager(NeutronvpnService neuService) { this.neuService = neuService; }
-
-    public VpnSubnetRouteHandler getVpnSubnetRouteHandler() {
-        return this.vpnSubnetRouteHandler;
+    @Override
+    protected InstanceIdentifier<VpnInterface> getWildCardPath() {
+        return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
     }
 
     @Override
-    public void close() throws Exception {
-        if (listenerRegistration != null) {
-            try {
-                listenerRegistration.close();
-                opListenerRegistration.close();
-            } catch (final Exception e) {
-                LOG.error("Error when cleaning up DataChangeListener.", e);
-            }
-            listenerRegistration = null;
-            opListenerRegistration = null;
-        }
-        LOG.info("VPN Interface Manager Closed");
+    protected VpnInterfaceManager getDataTreeChangeListener() {
+        return VpnInterfaceManager.this;
     }
 
-    private void registerListener(final DataBroker db) {
-        try {
-            listenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
-                    getWildCardPath(), VpnInterfaceManager.this, DataChangeScope.SUBTREE);
-            opListenerRegistration = db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
-                                                                   getWildCardPath(), vpnInterfaceOpListener, DataChangeScope.SUBTREE);
-        } catch (final Exception e) {
-            LOG.error("VPN Service DataChange listener registration fail!", e);
-            throw new IllegalStateException("VPN Service registration Listener failed.", e);
-        }
-    }
 
     private InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface> getInterfaceListenerPath() {
         return InstanceIdentifier.create(InterfacesState.class)
-            .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class);
+                .child(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.class);
     }
 
     @Override
-    protected void add(final InstanceIdentifier<VpnInterface> identifier,
-            final VpnInterface vpnInterface) {
-        LOG.trace("VPN Interface key: {} , value: {}", identifier, vpnInterface );
-        addInterface(identifier, vpnInterface);
-    }
-
-    private void addInterface(final InstanceIdentifier<VpnInterface> identifier,
-                              final VpnInterface vpnInterface) {
+    public void add(final InstanceIdentifier<VpnInterface> identifier, final VpnInterface vpnInterface) {
         LOG.trace("VPN Interface add event - key: {}, value: {}" ,identifier, vpnInterface );
+        LOG.info("VPN Interface add event - intfName {}" ,vpnInterface.getName());
         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
-        String interfaceName = key.getName();
+        final String interfaceName = key.getName();
 
         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface interfaceState =
-            InterfaceUtils.getInterfaceStateFromOperDS(broker, interfaceName);
-        if (interfaceState != null) {
-            // Interface state is up
-            processVpnInterfaceUp(InterfaceUtils.getDpIdFromInterface(interfaceState), interfaceName, interfaceState.getIfIndex());
+                InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, interfaceName);
+        if(interfaceState != null){
+            try{
+                final BigInteger dpnId = InterfaceUtils.getDpIdFromInterface(interfaceState);
+                final int ifIndex = interfaceState.getIfIndex();
+                DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+                dataStoreCoordinator.enqueueJob("VPNINTERFACE-"+ interfaceName,
+                        new Callable<List<ListenableFuture<Void>>>() {
+                            @Override
+                            public List<ListenableFuture<Void>> call() throws Exception {
+                                WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
+                                WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
+                                WriteTransaction writeInvTxn = dataBroker.newWriteOnlyTransaction();
+                                processVpnInterfaceUp(dpnId, vpnInterface, ifIndex, false, writeConfigTxn, writeOperTxn, writeInvTxn);
+                                List<ListenableFuture<Void>> futures = new ArrayList<ListenableFuture<Void>>();
+                                futures.add(writeOperTxn.submit());
+                                futures.add(writeConfigTxn.submit());
+                                futures.add(writeInvTxn.submit());
+                                return futures;
+                            }
+                        });
+            }catch (Exception e){
+                LOG.error("Unable to retrieve dpnId from interface operational data store for interface {}. ", interfaceName, e);
+                return;
+            }
         } else {
-            LOG.trace("VPN interfaces are not yet operational.");
+            LOG.info("Handling addition of VPN interface {} skipped as interfaceState is not available", interfaceName);
         }
     }
 
-    protected void processVpnInterfaceUp(BigInteger dpId, String interfaceName, int lPortTag) {
-
-        VpnInterface vpnInterface = VpnUtil.getConfiguredVpnInterface(broker, interfaceName);
-        if(vpnInterface == null) {
-            LOG.info("Unable to process add/up for interface {} as it is not configured", interfaceName);
-            return;
-        }
-        String vpnName = vpnInterface.getVpnInstanceName();
-        LOG.info("Binding vpn service to interface {} ", interfaceName);
-        long vpnId = VpnUtil.getVpnId(broker, vpnName);
-        if (vpnId == VpnConstants.INVALID_ID) {
-            LOG.trace("VpnInstance to VPNId mapping is not yet available, bailing out now.");
-            return;
-        }
-        boolean waitForVpnInterfaceOpRemoval = false;
-        int numAdjs = 0;
-        VpnInterface opVpnInterface = null;
-        synchronized (interfaceName.intern()) {
-            opVpnInterface = VpnUtil.getOperationalVpnInterface(broker, vpnInterface.getName());
+    protected void processVpnInterfaceUp(final BigInteger dpId, VpnInterface vpnInterface,
+                                         final int lPortTag, boolean isInterfaceUp,
+                                         WriteTransaction writeConfigTxn,
+                                         WriteTransaction writeOperTxn,
+                                         WriteTransaction writeInvTxn) {
+
+        final String interfaceName = vpnInterface.getName();
+        if (!isInterfaceUp) {
+            final String vpnName = vpnInterface.getVpnInstanceName();
+            LOG.info("Binding vpn service to interface {} ", interfaceName);
+            long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
+            if (vpnId == VpnConstants.INVALID_ID) {
+                LOG.trace("VpnInstance to VPNId mapping is not yet available, bailing out now.");
+                return;
+            }
+            boolean waitForVpnInterfaceOpRemoval = false;
+            VpnInterface opVpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, vpnInterface.getName());
             if (opVpnInterface != null ) {
                 String opVpnName = opVpnInterface.getVpnInstanceName();
                 String primaryInterfaceIp = null;
@@ -255,340 +253,858 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
                     // If so, we have to process ADD, as this might be a DPN Restart with Remove and Add triggered
                     // back to back
                     // However, if the primary VRF Entry for this VPNInterface exists, please continue bailing out !
-                    List<Adjacency> adjs = VpnUtil.getAdjacenciesForVpnInterfaceFromConfig(broker, interfaceName);
+                    List<Adjacency> adjs = VpnUtil.getAdjacenciesForVpnInterfaceFromConfig(dataBroker, interfaceName);
                     if (adjs == null) {
-                        LOG.info("VPN Interface {} addition failed as adjacencies for this vpn interface could not be obtained",
-                                interfaceName);
+                        LOG.info("VPN Interface {} addition failed as adjacencies for this vpn interface could not be obtained", interfaceName);
                         return;
                     }
-                    numAdjs = adjs.size();
                     for (Adjacency adj : adjs) {
                         if (adj.getMacAddress() != null && !adj.getMacAddress().isEmpty()) {
-                            primaryInterfaceIp = adj.getNextHopIp();
+                            primaryInterfaceIp = adj.getIpAddress();
                             break;
                         }
                     }
                     if (primaryInterfaceIp == null) {
-                        LOG.info("VPN Interface {} addition failed as primary adjacency for this vpn interface could not be obtained",
-                                interfaceName);
+                        LOG.info("VPN Interface {} addition failed as primary adjacency "
+                                + "for this vpn interface could not be obtained", interfaceName);
                         return;
                     }
                     // Get the rd of the vpn instance
                     String rd = getRouteDistinguisher(opVpnName);
-                    VrfEntry vrf = VpnUtil.getVrfEntry(broker, rd, primaryInterfaceIp);
+                    rd =  (rd == null) ? opVpnName : rd;
+                    VrfEntry vrf = VpnUtil.getVrfEntry(dataBroker, rd, primaryInterfaceIp);
                     if (vrf != null) {
                         LOG.info("VPN Interface {} already provisioned , bailing out from here.", interfaceName);
                         return;
                     }
                     waitForVpnInterfaceOpRemoval = true;
                 } else {
-                    LOG.info("vpn interface {} to go to configured vpn {}, but in operational vpn {}", interfaceName, vpnName, opVpnName);
+                    LOG.info("vpn interface {} to go to configured vpn {}, but in operational vpn {}",
+                            interfaceName, vpnName, opVpnName);
                 }
             }
             if (!waitForVpnInterfaceOpRemoval) {
                 // Add the VPNInterface and quit
-                bindService(dpId, vpnName, interfaceName, lPortTag);
-                updateDpnDbs(dpId, vpnName, interfaceName, true);
-                processVpnInterfaceAdjacencies(dpId, VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
+                updateVpnToDpnMapping(dpId, vpnName, interfaceName, true /* add */);
+                bindService(dpId, vpnName, interfaceName, lPortTag, writeConfigTxn, writeInvTxn);
+                processVpnInterfaceAdjacencies(dpId, vpnName, interfaceName, writeConfigTxn, writeOperTxn);
+                return;
+            }
+
+            // FIB didn't get a chance yet to clean up this VPNInterface
+            // Let us give it a chance here !
+            LOG.info("Trying to add VPN Interface {}, but waiting for FIB to clean up! ", interfaceName);
+            try {
+                Runnable notifyTask = new VpnNotifyTask();
+                vpnIntfMap.put(interfaceName, notifyTask);
+                synchronized (notifyTask) {
+                    try {
+                        notifyTask.wait(VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS);
+                    } catch (InterruptedException e) {
+                    }
+                }
+            } finally {
+                vpnIntfMap.remove(interfaceName);
+            }
+
+            opVpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, interfaceName);
+            if (opVpnInterface != null) {
+                LOG.error("VPN Interface {} removal by FIB did not complete on time, bailing addition ...", interfaceName);
                 return;
             }
+            // VPNInterface got removed, proceed with Add
+            updateVpnToDpnMapping(dpId, vpnName, interfaceName, true /* add */);
+            bindService(dpId, vpnName, interfaceName, lPortTag, writeConfigTxn, writeInvTxn);
+            processVpnInterfaceAdjacencies(dpId, vpnName, interfaceName, writeConfigTxn, writeOperTxn);
+        } else {
+            // Interface is retained in the DPN, but its Link Up.
+            // Advertise prefixes again for this interface to BGP
+            advertiseAdjacenciesForVpnToBgp(dpId, VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()),
+                    vpnInterface);
         }
+    }
 
-        // FIB didn't get a chance yet to clean up this VPNInterface
-        // Let us give it a chance here !
-        LOG.info("VPN Interface {} waiting for FIB to clean up! ", interfaceName);
-        try {
-            Runnable notifyTask = new VpnNotifyTask();
-            vpnIntfMap.put(interfaceName, notifyTask);
-            synchronized (notifyTask) {
-                try {
-                    notifyTask.wait(VpnConstants.MAX_WAIT_TIME_IN_MILLISECONDS);
-                } catch (InterruptedException e) {
+
+//    private class UpdateDpnToVpnWorker implements Callable<List<ListenableFuture<Void>>> {
+//        BigInteger dpnId;
+//        String vpnName;
+//        String interfaceName;
+//        boolean addToDpn;
+//        int lPortTag;
+//
+//        public UpdateDpnToVpnWorker(BigInteger dpnId, String vpnName, String interfaceName,
+//                                    int lPortTag, boolean addToDpn) {
+//            this.dpnId= dpnId;
+//            this.vpnName = vpnName;
+//            this.interfaceName = interfaceName;
+//            this.lPortTag = lPortTag;
+//            this.addToDpn = addToDpn;
+//        }
+//
+//        @Override
+//        public List<ListenableFuture<Void>> call() throws Exception {
+//            // If another renderer(for eg : CSS) needs to be supported, check can be performed here
+//            // to call the respective helpers.
+//            WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
+//            updateDpnDbs(dpnId, vpnName, interfaceName, addToDpn, writeTxn);
+//            List<ListenableFuture<Void>> futures = new ArrayList<>();
+//            futures.add(writeTxn.submit());
+//            ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
+//            Futures.addCallback(listenableFuture,
+//                    new UpdateDpnToVpnCallback(dpnId, vpnName, interfaceName, lPortTag, addToDpn));
+//            return futures;
+//        }
+//    }
+//
+//
+//    /**
+//     * JobCallback class is used as a future callback for
+//     * main and rollback workers to handle success and failure.
+//     */
+//    private class UpdateDpnToVpnCallback implements FutureCallback<List<Void>> {
+//        BigInteger dpnId;
+//        String vpnName;
+//        String interfaceName;
+//        boolean addToDpn;
+//        int lPortTag;
+//
+//        public UpdateDpnToVpnCallback(BigInteger dpnId, String vpnName, String interfaceName,
+//                                      int lPortTag, boolean addToDpn) {
+//            this.dpnId= dpnId;
+//            this.vpnName = vpnName;
+//            this.interfaceName = interfaceName;
+//            this.lPortTag = lPortTag;
+//            this.addToDpn = addToDpn;
+//        }
+//
+//        /**
+//         * @param voids
+//         * This implies that all the future instances have returned success. -- TODO: Confirm this
+//         */
+//        @Override
+//        public void onSuccess(List<Void> voids) {
+//            WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
+//            bindService(dpnId, vpnName, interfaceName, lPortTag, writeTxn);
+//            processVpnInterfaceAdjacencies(dpnId, vpnName, interfaceName, writeTxn);
+//            writeTxn.submit();
+//        }
+//
+//        /**
+//         *
+//         * @param throwable
+//         * This method is used to handle failure callbacks.
+//         * If more retry needed, the retrycount is decremented and mainworker is executed again.
+//         * After retries completed, rollbackworker is executed.
+//         * If rollbackworker fails, this is a double-fault. Double fault is logged and ignored.
+//         */
+//
+//        @Override
+//        public void onFailure(Throwable throwable) {
+//            LOG.warn("Job: failed with exception: {}", throwable.getStackTrace());
+//        }
+//    }
+
+
+
+
+    private void advertiseAdjacenciesForVpnToBgp(BigInteger dpnId, final InstanceIdentifier<VpnInterface> identifier,
+                                                 VpnInterface intf) {
+        //Read NextHops
+        InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
+        Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
+
+        String rd = VpnUtil.getVpnRd(dataBroker, intf.getVpnInstanceName());
+        if (rd == null) {
+            LOG.error("advertiseAdjacenciesForVpnFromBgp: Unable to recover rd for interface {} in vpn {}",
+                    intf.getName(), intf.getVpnInstanceName());
+            return;
+        } else {
+            if (rd.equals(intf.getVpnInstanceName())) {
+                LOG.info("advertiseAdjacenciesForVpnFromBgp: Ignoring BGP advertisement for interface {} as it is in " +
+                        "internal vpn{} with rd {}", intf.getName(), intf.getVpnInstanceName(), rd);
+
+                return;
+            }
+        }
+        LOG.info("advertiseAdjacenciesForVpnToBgp: Advertising interface {} in vpn {} with rd {} ", intf.getName(),
+                intf.getVpnInstanceName(), rd);
+
+        String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
+        if (nextHopIp == null){
+            LOG.trace("advertiseAdjacenciesForVpnToBgp: NextHop for interface {} is null, returning", intf.getName());
+            return;
+        }
+
+        if (adjacencies.isPresent()) {
+            List<Adjacency> nextHops = adjacencies.get().getAdjacency();
+
+            if (!nextHops.isEmpty()) {
+                LOG.trace("NextHops are " + nextHops);
+                for (Adjacency nextHop : nextHops) {
+                    long label = nextHop.getLabel();
+                    try {
+                        LOG.info("VPN ADVERTISE: Adding Fib Entry rd {} prefix {} nexthop {} label {}", rd, nextHop.getIpAddress(), nextHopIp, label);
+                        bgpManager.advertisePrefix(rd, nextHop.getIpAddress(), nextHopIp, (int)label);
+                        LOG.info("VPN ADVERTISE: Added Fib Entry rd {} prefix {} nexthop {} label {}", rd, nextHop.getIpAddress(), nextHopIp, label);
+                    } catch(Exception e) {
+                        LOG.error("Failed to advertise prefix {} in vpn {} with rd {} for interface {} ",
+                                nextHop.getIpAddress(), intf.getVpnInstanceName(), rd, intf.getName(), e);
+                    }
                 }
             }
-        } finally {
-            vpnIntfMap.remove(interfaceName);
         }
+    }
 
-        opVpnInterface = VpnUtil.getOperationalVpnInterface(broker, interfaceName);
-        if (opVpnInterface != null) {
-            LOG.error("VPN Interface {} removal by FIB did not complete on time, bailing addition ...", interfaceName);
+    private void withdrawAdjacenciesForVpnFromBgp(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
+        //Read NextHops
+        InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
+        Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
+
+        String rd = VpnUtil.getVpnRd(dataBroker, intf.getVpnInstanceName());
+        if (rd == null) {
+            LOG.error("withdrawAdjacenciesForVpnFromBgp: Unable to recover rd for interface {} in vpn {}",
+                    intf.getName(), intf.getVpnInstanceName());
             return;
+        } else {
+            if (rd.equals(intf.getVpnInstanceName())) {
+                LOG.info("withdrawAdjacenciesForVpnFromBgp: Ignoring BGP withdrawal for interface {} as it is in " +
+                        "internal vpn{} with rd {}", intf.getName(), intf.getVpnInstanceName(), rd);
+                return;
+            }
         }
-        // VPNInterface got removed, proceed with Add
-        synchronized (interfaceName.intern()) {
-            bindService(dpId, vpnName, interfaceName, lPortTag);
-            updateDpnDbs(dpId, vpnName, interfaceName, true);
-            processVpnInterfaceAdjacencies(dpId, VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getName()), vpnInterface);
+        LOG.info("withdrawAdjacenciesForVpnFromBgp: For interface {} in vpn {} with rd {}", intf.getName(),
+                intf.getVpnInstanceName(), rd);
+        if (adjacencies.isPresent()) {
+            List<Adjacency> nextHops = adjacencies.get().getAdjacency();
+
+            if (!nextHops.isEmpty()) {
+                LOG.trace("NextHops are " + nextHops);
+                for (Adjacency nextHop : nextHops) {
+                    try {
+                        LOG.info("VPN WITHDRAW: Removing Fib Entry rd {} prefix {}", rd, nextHop.getIpAddress());
+                        bgpManager.withdrawPrefix(rd, nextHop.getIpAddress());
+                        LOG.info("VPN WITHDRAW: Removed Fib Entry rd {} prefix {}", rd, nextHop.getIpAddress());
+                    } catch(Exception e) {
+                        LOG.error("Failed to withdraw prefix {} in vpn {} with rd {} for interface {} ",
+                                nextHop.getIpAddress(), intf.getVpnInstanceName(), rd, intf.getName(), e);
+                    }
+                }
+            }
         }
     }
 
-    private void updateDpnDbs(BigInteger dpId, String vpnName, String interfaceName, boolean add) {
-        long vpnId = VpnUtil.getVpnId(broker, vpnName);
+    public void updateVpnToDpnMapping(BigInteger dpId, String vpnName, String interfaceName, boolean add) {
+        long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
         if (dpId == null) {
-            dpId = InterfaceUtils.getDpnForInterface(interfaceManager, interfaceName);
+            dpId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, interfaceName);
         }
         if(!dpId.equals(BigInteger.ZERO)) {
             if(add)
-                updateMappingDbs(vpnId, dpId, interfaceName, vpnName);
+                createOrUpdateVpnToDpnList(vpnId, dpId, interfaceName, vpnName);
             else
-                removeFromMappingDbs(vpnId, dpId, interfaceName, vpnName);
+                removeOrUpdateVpnToDpnList(vpnId, dpId, interfaceName, vpnName);
         }
+    }
 
+    private void bindService(BigInteger dpId, final String vpnInstanceName, final String vpnInterfaceName,
+                             int lPortTag, WriteTransaction writeConfigTxn, WriteTransaction writeInvTxn) {
+        final int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
+        final long vpnId = VpnUtil.getVpnId(dataBroker, vpnInstanceName);
+
+        DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+        dataStoreCoordinator.enqueueJob(vpnInterfaceName,
+                new Callable<List<ListenableFuture<Void>>>() {
+                    @Override
+                    public List<ListenableFuture<Void>> call() throws Exception {
+                        WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
+                        int instructionKey = 0;
+                        List<Instruction> instructions = new ArrayList<Instruction>();
+
+                        instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(
+                                MetaDataUtil.getVpnIdMetadata(vpnId), MetaDataUtil.METADATA_MASK_VRFID, ++instructionKey));
+                        instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_GW_MAC_TABLE, ++instructionKey));
+
+                        BoundServices
+                                serviceInfo =
+                                InterfaceUtils.getBoundServices(String.format("%s.%s.%s", "vpn",vpnInstanceName, vpnInterfaceName),
+                                        ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants.L3VPN_SERVICE_INDEX), priority,
+                                        NwConstants.COOKIE_VM_INGRESS_TABLE, instructions);
+                        writeTxn.put(LogicalDatastoreType.CONFIGURATION,
+                                InterfaceUtils.buildServiceId(vpnInterfaceName, ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants.L3VPN_SERVICE_INDEX)), serviceInfo, true);
+                        List<ListenableFuture<Void>> futures = new ArrayList<ListenableFuture<Void>>();
+                        futures.add(writeTxn.submit());
+                        return futures;
+                    }
+                });
+        setupGwMacIfExternalVpn(dpId, vpnInterfaceName, vpnId, writeInvTxn, NwConstants.ADD_FLOW);
     }
 
-    private void bindService(BigInteger dpId, String vpnInstanceName, String vpnInterfaceName, int lPortTag) {
-        int priority = VpnConstants.DEFAULT_FLOW_PRIORITY;
-        long vpnId = VpnUtil.getVpnId(broker, vpnInstanceName);
+    private void setupGwMacIfExternalVpn(BigInteger dpnId, String interfaceName, long vpnId,
+            WriteTransaction writeInvTxn, int addOrRemove) {
+        InstanceIdentifier<VpnIds> vpnIdsInstanceIdentifier = VpnUtil.getVpnIdToVpnInstanceIdentifier(vpnId);
+        Optional<VpnIds> vpnIdsOptional = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIdsInstanceIdentifier);
+        if (vpnIdsOptional.isPresent() && vpnIdsOptional.get().isExternalVpn()) {
+            Optional<String> gwMacAddressOptional = InterfaceUtils.getMacAddressForInterface(dataBroker, interfaceName);
+            if (!gwMacAddressOptional.isPresent()) {
+                LOG.error("Failed to get gwMacAddress for interface {}", interfaceName);
+                return;
+            }
+            String gwMacAddress = gwMacAddressOptional.get();
+            FlowEntity flowEntity = VpnUtil.buildL3vpnGatewayFlow(dpnId, gwMacAddress, vpnId);
+            if (addOrRemove == NwConstants.ADD_FLOW) {
+                mdsalManager.addFlowToTx(flowEntity, writeInvTxn);
+            } else if (addOrRemove == NwConstants.DEL_FLOW) {
+                mdsalManager.removeFlowToTx(flowEntity, writeInvTxn);
+            }
+        }
+    }
 
-        int instructionKey = 0;
-        List<Instruction> instructions = new ArrayList<>();
+    protected void processVpnInterfaceAdjacencies(BigInteger dpnId, String vpnName, String interfaceName,
+                                                WriteTransaction writeConfigTxn,
+                                                WriteTransaction writeOperTxn) {
+        InstanceIdentifier<VpnInterface> identifier = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
+        // Read NextHops
+        InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
+        Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, path);
 
-        instructions.add(MDSALUtil.buildAndGetWriteMetadaInstruction(BigInteger.valueOf(vpnId), MetaDataUtil.METADATA_MASK_VRFID, ++instructionKey));
-        instructions.add(MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_FIB_TABLE, ++instructionKey));
+        if (adjacencies.isPresent()) {
+            List<Adjacency> nextHops = adjacencies.get().getAdjacency();
+            List<Adjacency> value = new ArrayList<>();
 
-        BoundServices
-            serviceInfo =
-            InterfaceUtils.getBoundServices(String.format("%s.%s.%s", "vpn",vpnInstanceName, vpnInterfaceName),
-                                            VpnConstants.L3VPN_SERVICE_IDENTIFIER, priority,
-                                            VpnConstants.COOKIE_VM_INGRESS_TABLE, instructions);
-        VpnUtil.syncWrite(broker, LogicalDatastoreType.CONFIGURATION,
-                          InterfaceUtils.buildServiceId(vpnInterfaceName, VpnConstants.L3VPN_SERVICE_IDENTIFIER), serviceInfo);
-        makeArpFlow(dpId, VpnConstants.L3VPN_SERVICE_IDENTIFIER, lPortTag, vpnInterfaceName,
-                    vpnId, ArpReplyOrRequest.REQUEST, NwConstants.ADD_FLOW);
-        makeArpFlow(dpId, VpnConstants.L3VPN_SERVICE_IDENTIFIER, lPortTag, vpnInterfaceName,
-                vpnId, ArpReplyOrRequest.REPLY, NwConstants.ADD_FLOW);
+            // Get the rd of the vpn instance
+            String rd = getRouteDistinguisher(vpnName);
 
-    }
+            String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
+            if (nextHopIp == null){
+                LOG.error("NextHop for interface {} is null", interfaceName);
+                return;
+            }
 
-    private void processVpnInterfaceAdjacencies(BigInteger dpnId, final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
-        String intfName = intf.getName();
+            List<VpnInstanceOpDataEntry> vpnsToImportRoute = getVpnsImportingMyRoute(vpnName);
+
+            LOG.trace("NextHops for interface {} are {}", interfaceName, nextHops);
+            for (Adjacency nextHop : nextHops) {
+                String prefix = VpnUtil.getIpPrefix(nextHop.getIpAddress());
+                long label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
+                        VpnUtil.getNextHopLabelKey((rd == null) ? vpnName
+                                : rd, prefix));
+                if (label == VpnConstants.INVALID_LABEL) {
+                    LOG.error("Unable to fetch label from Id Manager. Bailing out of processing add/update of vpn interface {} for vpn {}", interfaceName, vpnName);
+                    return;
+                }
+                List<String> adjNextHop = nextHop.getNextHopIpList();
+                value.add(new AdjacencyBuilder(nextHop).setLabel(label).setNextHopIpList(
+                        (adjNextHop != null && !adjNextHop.isEmpty()) ? adjNextHop : Arrays.asList(nextHopIp))
+                        .setIpAddress(prefix).setKey(new AdjacencyKey(prefix)).build());
+
+                if (nextHop.getMacAddress() != null && !nextHop.getMacAddress().isEmpty()) {
+                    LOG.trace("Adding prefix {} to interface {} for vpn {}", prefix, interfaceName, vpnName);
+                    writeOperTxn.merge(
+                            LogicalDatastoreType.OPERATIONAL,
+                            VpnUtil.getPrefixToInterfaceIdentifier(
+                                    VpnUtil.getVpnId(dataBroker, vpnName), prefix),
+                            VpnUtil.getPrefixToInterface(dpnId, interfaceName, prefix), true);
+                } else {
+                    //Extra route adjacency
+                    LOG.trace("Adding prefix {} and nexthopList {} as extra-route for vpn", nextHop.getIpAddress(), nextHop.getNextHopIpList(), vpnName);
+                    writeOperTxn.merge(
+                            LogicalDatastoreType.OPERATIONAL,
+                            VpnUtil.getVpnToExtrarouteIdentifier(
+                                    (rd != null) ? rd : vpnName, nextHop.getIpAddress()),
+                            VpnUtil.getVpnToExtraroute(nextHop.getIpAddress(), nextHop.getNextHopIpList()), true);
+                }
+            }
 
-        synchronized (intfName) {
-            // Read NextHops
-            InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
-            Optional<Adjacencies> adjacencies = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, path);
+            Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
+
+            VpnInterface opInterface = VpnUtil.getVpnInterface(interfaceName, vpnName, aug, dpnId, Boolean.FALSE);
+            InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
+            writeOperTxn.put(LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface, true);
+            long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
+
+            for (Adjacency nextHop : aug.getAdjacency()) {
+                long label = nextHop.getLabel();
+                if (rd != null) {
+                    addToLabelMapper(label, dpnId, nextHop.getIpAddress(), Arrays.asList(nextHopIp), vpnId,
+                            interfaceName, null,false, rd, writeOperTxn);
+                    addPrefixToBGP(rd, nextHop.getIpAddress(), nextHopIp, label, writeConfigTxn);
+                    //TODO: ERT - check for VPNs importing my route
+                    for (VpnInstanceOpDataEntry vpn : vpnsToImportRoute) {
+                        String vpnRd = vpn.getVrfId();
+                        if (vpnRd != null) {
+                            LOG.debug("Exporting route with rd {} prefix {} nexthop {} label {} to VPN {}", vpnRd, nextHop.getIpAddress(), nextHopIp, label, vpn);
+                            fibManager.addOrUpdateFibEntry(dataBroker, vpnRd, nextHop.getIpAddress(), Arrays.asList(nextHopIp), (int) label,
+                                    RouteOrigin.SELF_IMPORTED, writeConfigTxn);
+                        }
+                    }
+                } else {
+                    // ### add FIB route directly
+                    fibManager.addOrUpdateFibEntry(dataBroker, vpnName, nextHop.getIpAddress(), Arrays.asList(nextHopIp),
+                            (int) label, RouteOrigin.STATIC, writeConfigTxn);
+                }
+            }
+        }
+    }
 
-            if (adjacencies.isPresent()) {
-                List<Adjacency> nextHops = adjacencies.get().getAdjacency();
-                List<Adjacency> value = new ArrayList<>();
+    private List<VpnInstanceOpDataEntry> getVpnsImportingMyRoute(final String vpnName) {
+        List<VpnInstanceOpDataEntry> vpnsToImportRoute = new ArrayList<>();
 
-                // Get the rd of the vpn instance
-                String rd = getRouteDistinguisher(intf.getVpnInstanceName());
+        final String vpnRd = VpnUtil.getVpnRd(dataBroker, vpnName);
+        final VpnInstanceOpDataEntry vpnInstanceOpDataEntry = VpnUtil.getVpnInstanceOpData(dataBroker, vpnRd);
+        if (vpnInstanceOpDataEntry == null) {
+            LOG.debug("Could not retrieve vpn instance op data for {} to check for vpns importing the routes", vpnName);
+            return vpnsToImportRoute;
+        }
 
-                String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, dpnId);
-                if (nextHopIp == null){
-                    LOG.error("NextHop for interface {} is null", intfName);
+        Predicate<VpnInstanceOpDataEntry> excludeVpn = new Predicate<VpnInstanceOpDataEntry>() {
+            @Override
+            public boolean apply(VpnInstanceOpDataEntry input) {
+                if (input.getVpnInstanceName() == null) {
+                    LOG.error("Received vpn instance without identity");
+                    return false;
                 }
+                return !input.getVpnInstanceName().equals(vpnName);
+            }
+        };
 
-                LOG.trace("NextHops are {}", nextHops);
-                for (Adjacency nextHop : nextHops) {
-                    String prefix = VpnUtil.getIpPrefix(nextHop.getIpAddress());
-                    long label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME, VpnUtil
-                            .getNextHopLabelKey((rd == null) ? intf.getVpnInstanceName() : rd, prefix));
-                    String adjNextHop = nextHop.getNextHopIp();
-                    value.add(new AdjacencyBuilder(nextHop).setLabel(label).setNextHopIp((adjNextHop != null && !adjNextHop.isEmpty()) ? adjNextHop : nextHopIp)
-                            .setIpAddress(prefix).setKey(new AdjacencyKey(prefix)).build());
-                    if(nextHop.getMacAddress() != null && !nextHop.getMacAddress().isEmpty()) {
-                        VpnUtil.syncUpdate(
-                                broker,
-                                LogicalDatastoreType.OPERATIONAL,
-                                VpnUtil.getPrefixToInterfaceIdentifier(
-                                        VpnUtil.getVpnId(broker, intf.getVpnInstanceName()), prefix),
-                                VpnUtil.getPrefixToInterface(dpnId, intf.getName(), prefix));
-                    } else {
-                        //Extra route adjacency
-                        VpnUtil.syncUpdate(
-                                broker,
-                                LogicalDatastoreType.OPERATIONAL,
-                                VpnUtil.getVpnToExtrarouteIdentifier(
-                                        (rd != null) ? rd : intf.getVpnInstanceName(), nextHop.getIpAddress()),
-                                VpnUtil.getVpnToExtraroute(nextHop.getIpAddress(), nextHop.getNextHopIp()));
+        Predicate<VpnInstanceOpDataEntry> matchRTs = new Predicate<VpnInstanceOpDataEntry>() {
+            @Override
+            public boolean apply(VpnInstanceOpDataEntry input) {
+                Iterable<String> commonRTs = intersection(getRts(vpnInstanceOpDataEntry, VpnTarget.VrfRTType.ExportExtcommunity),
+                        getRts(input, VpnTarget.VrfRTType.ImportExtcommunity));
+                return Iterators.size(commonRTs.iterator()) > 0;
+            }
+        };
 
-                    }
-                }
+        Function<VpnInstanceOpDataEntry, String> toInstanceName = new Function<VpnInstanceOpDataEntry, String>() {
+            @Override
+            public String apply(VpnInstanceOpDataEntry vpnInstance) {
+                //return vpnInstance.getVpnInstanceName();
+                return vpnInstance.getVrfId();
+            }
+        };
 
-                Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(value);
-                VpnInterface opInterface = VpnUtil.getVpnInterface(intfName, intf.getVpnInstanceName(), aug);
-                InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(intfName);
-                VpnUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, interfaceId, opInterface);
-                for (Adjacency nextHop : aug.getAdjacency()) {
-                    long label = nextHop.getLabel();
-                    //String adjNextHop = nextHop.getNextHopIp();
-                    if (rd != null) {
-                        addPrefixToBGP(rd, nextHop.getIpAddress(),
-                                            nextHopIp, label);
-                    } else {
-                        // ### add FIB route directly
-                        addFibEntryToDS(intf.getVpnInstanceName(), nextHop.getIpAddress(),
-                                            nextHopIp, (int) label);
-                    }
+        vpnsToImportRoute = FluentIterable.from(VpnUtil.getAllVpnInstanceOpData(dataBroker)).
+                filter(excludeVpn).
+                filter(matchRTs).toList();
+        return vpnsToImportRoute;
+    }
+
+    private List<VpnInstanceOpDataEntry> getVpnsExportingMyRoute(final String vpnName) {
+        List<VpnInstanceOpDataEntry> vpnsToExportRoute = new ArrayList<>();
+
+        String vpnRd = VpnUtil.getVpnRd(dataBroker, vpnName);
+        final VpnInstanceOpDataEntry vpnInstanceOpDataEntry = VpnUtil.getVpnInstanceOpData(dataBroker, vpnRd);
+        if (vpnInstanceOpDataEntry == null) {
+            LOG.debug("Could not retrieve vpn instance op data for {} to check for vpns exporting the routes", vpnName);
+            return vpnsToExportRoute;
+        }
+
+        Predicate<VpnInstanceOpDataEntry> excludeVpn = new Predicate<VpnInstanceOpDataEntry>() {
+            @Override
+            public boolean apply(VpnInstanceOpDataEntry input) {
+                if (input.getVpnInstanceName() == null) {
+                    LOG.error("Received vpn instance without identity");
+                    return false;
                 }
+                return !input.getVpnInstanceName().equals(vpnName);
             }
-        }
+        };
+
+        Predicate<VpnInstanceOpDataEntry> matchRTs = new Predicate<VpnInstanceOpDataEntry>() {
+            @Override
+            public boolean apply(VpnInstanceOpDataEntry input) {
+                Iterable<String> commonRTs = intersection(getRts(vpnInstanceOpDataEntry, VpnTarget.VrfRTType.ImportExtcommunity),
+                        getRts(input, VpnTarget.VrfRTType.ExportExtcommunity));
+                return Iterators.size(commonRTs.iterator()) > 0;
+            }
+        };
+
+        Function<VpnInstanceOpDataEntry, String> toInstanceName = new Function<VpnInstanceOpDataEntry, String>() {
+            @Override
+            public String apply(VpnInstanceOpDataEntry vpnInstance) {
+                return vpnInstance.getVpnInstanceName();
+            }
+        };
+
+        vpnsToExportRoute = FluentIterable.from(VpnUtil.getAllVpnInstanceOpData(dataBroker)).
+                filter(excludeVpn).
+                filter(matchRTs).toList();
+        return vpnsToExportRoute;
     }
 
-    private void makeArpFlow(BigInteger dpId,short sIndex, int lPortTag, String vpnInterfaceName,
-                             long vpnId, ArpReplyOrRequest replyOrRequest, int addOrRemoveFlow){
-        List<MatchInfo> matches = new ArrayList<>();
-        BigInteger metadata = MetaDataUtil.getMetaDataForLPortDispatcher(lPortTag, ++sIndex, BigInteger.valueOf(vpnId));
-        BigInteger metadataMask = MetaDataUtil.getMetaDataMaskForLPortDispatcher(MetaDataUtil.METADATA_MASK_SERVICE_INDEX,
-                MetaDataUtil.METADATA_MASK_LPORT_TAG, MetaDataUtil.METADATA_MASK_VRFID);
-
-        // Matching Arp reply flows
-        matches.add(new MatchInfo(MatchFieldType.eth_type, new long[] { NwConstants.ETHTYPE_ARP }));
-        matches.add(new MatchInfo(MatchFieldType.metadata, new BigInteger[] {
-                metadata, metadataMask }));
-
-        matches.add(new MatchInfo(MatchFieldType.arp_op, new long[] { replyOrRequest.getArpOperation() }));
-
-        // Instruction to punt to controller
-        List<InstructionInfo> instructions = new ArrayList<>();
-        List<ActionInfo> actionsInfos = new ArrayList<>();
-        actionsInfos.add(new ActionInfo(ActionType.punt_to_controller, new String[] {}));
-        instructions.add(new InstructionInfo(InstructionType.apply_actions, actionsInfos));
-
-        // Install the flow entry in L3_INTERFACE_TABLE
-        String flowRef = VpnUtil.getFlowRef(dpId, NwConstants.L3_INTERFACE_TABLE,
-                    NwConstants.ETHTYPE_ARP, lPortTag, replyOrRequest.getArpOperation());
-        FlowEntity flowEntity;
-        flowEntity = MDSALUtil.buildFlowEntity(dpId, NwConstants.L3_INTERFACE_TABLE, flowRef,
-                NwConstants.DEFAULT_ARP_FLOW_PRIORITY, replyOrRequest.getName(), 0, 0,
-                VpnUtil.getCookieArpFlow(lPortTag), matches, instructions);
-
-        if (addOrRemoveFlow == NwConstants.ADD_FLOW) {
-            LOG.debug("Creating ARP Flow for interface {}",vpnInterfaceName);
-            mdsalManager.installFlow(flowEntity);
-        } else {
-            LOG.debug("Deleting ARP Flow for interface {}",vpnInterfaceName);
-            mdsalManager.removeFlow(flowEntity);
+    private <T> Iterable<T> intersection(final Collection<T> collection1, final Collection<T> collection2) {
+        final Predicate<T> inPredicate = Predicates.<T>in(collection2);
+        return new Iterable<T>() {
+            @Override
+            public Iterator<T> iterator() {
+                return Iterators.filter(collection1.iterator(), inPredicate);
+            }
+        };
+    }
+
+    private List<String> getRts(VpnInstanceOpDataEntry vpnInstance, VpnTarget.VrfRTType rtType) {
+        String name = vpnInstance.getVpnInstanceName();
+        List<String> rts = new ArrayList<>();
+        org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnTargets targets = vpnInstance.getVpnTargets();
+        if (targets == null) {
+            LOG.trace("vpn targets not available for {}", name);
+            return rts;
+        }
+        List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget> vpnTargets = targets.getVpnTarget();
+        if (vpnTargets == null) {
+            LOG.trace("vpnTarget values not available for {}", name);
+            return rts;
+        }
+        for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpntargets.VpnTarget target : vpnTargets) {
+            //TODO: Check for RT type is Both
+            if(target.getVrfRTType().equals(rtType) ||
+                    target.getVrfRTType().equals(VpnTarget.VrfRTType.Both)) {
+                String rtValue = target.getVrfRTValue();
+                rts.add(rtValue);
+            }
         }
+        return rts;
+    }
+    
+    private List<String> getExportRts(VpnInstance vpnInstance) {
+        List<String> exportRts = new ArrayList<>();
+        VpnAfConfig vpnConfig = vpnInstance.getIpv4Family();
+        VpnTargets targets = vpnConfig.getVpnTargets();
+        List<VpnTarget> vpnTargets = targets.getVpnTarget();
+        for (VpnTarget target : vpnTargets) {
+            if (target.getVrfRTType().equals(VpnTarget.VrfRTType.ExportExtcommunity)) {
+                String rtValue = target.getVrfRTValue();
+                exportRts.add(rtValue);
+            }
+        }
+        return exportRts;
     }
 
     private String getRouteDistinguisher(String vpnName) {
         InstanceIdentifier<VpnInstance> id = InstanceIdentifier.builder(VpnInstances.class)
-                                      .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
-        Optional<VpnInstance> vpnInstance = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
-        String rd = "";
+                .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
+        Optional<VpnInstance> vpnInstance = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+        String rd = null;
         if(vpnInstance.isPresent()) {
             VpnInstance instance = vpnInstance.get();
-            VpnAfConfig config = instance.getIpv4Family();
+            org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnAfConfig config = instance.getIpv4Family();
             rd = config.getRouteDistinguisher();
         }
         return rd;
     }
 
-    private synchronized void updateMappingDbs(long vpnId, BigInteger dpnId, String intfName, String vpnName) {
+    /**
+     * JobCallback class is used as a future callback for
+     * main and rollback workers to handle success and failure.
+     */
+    private class DpnEnterExitVpnWorker implements FutureCallback<List<Void>> {
+        BigInteger dpnId;
+        String vpnName;
+        String rd;
+        boolean entered;
+
+        public DpnEnterExitVpnWorker(BigInteger dpnId, String vpnName, String rd, boolean entered) {
+            this.entered = entered;
+            this.dpnId = dpnId;
+            this.vpnName = vpnName;
+            this.rd = rd;
+        }
+
+        /**
+         * @param voids
+         * This implies that all the future instances have returned success. -- TODO: Confirm this
+         */
+        @Override
+        public void onSuccess(List<Void> voids) {
+            if (entered) {
+                publishAddNotification(dpnId, vpnName, rd);
+            } else {
+                publishRemoveNotification(dpnId, vpnName, rd);
+            }
+        }
+
+        /**
+         *
+         * @param throwable
+         * This method is used to handle failure callbacks.
+         * If more retry needed, the retrycount is decremented and mainworker is executed again.
+         * After retries completed, rollbackworker is executed.
+         * If rollbackworker fails, this is a double-fault. Double fault is logged and ignored.
+         */
+        @Override
+        public void onFailure(Throwable throwable) {
+            LOG.warn("Job: failed with exception: {}", throwable.getStackTrace());
+        }
+    }
+
+    private void createOrUpdateVpnToDpnList(long vpnId, BigInteger dpnId, String intfName, String vpnName) {
         String routeDistinguisher = getRouteDistinguisher(vpnName);
         String rd = (routeDistinguisher == null) ? vpnName : routeDistinguisher;
-        InstanceIdentifier<VpnToDpnList> id = VpnUtil.getVpnToDpnListIdentifier(rd, dpnId);
-        Optional<VpnToDpnList> dpnInVpn = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
-        org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces
-            vpnInterface = new VpnInterfacesBuilder().setInterfaceName(intfName).build();
-
-        if (dpnInVpn.isPresent()) {
-            VpnUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id.child(
-                org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance
-                    .op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces.class,
-                    new VpnInterfacesKey(intfName)), vpnInterface);
-        } else {
-            VpnUtil.syncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
-                                    VpnUtil.getVpnInstanceOpDataIdentifier(rd),
-                                    VpnUtil.getVpnInstanceOpDataBuilder(rd, vpnId));
-            VpnToDpnListBuilder vpnToDpnList = new VpnToDpnListBuilder().setDpnId(dpnId);
-            List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
-                .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> vpnInterfaces =  new ArrayList<>();
-            vpnInterfaces.add(vpnInterface);
-            VpnUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, id,
-                              vpnToDpnList.setVpnInterfaces(vpnInterfaces).build());
-
-            /**
-             * FIXME: DC Gateway tunnel should be built dynamically
-            //this is the first VM in this VPN on the DPN, may be a new DPN has come up,
-            //if tunnel to DC GW does not exist, create it
-            //if(!tunnelExists(dpnID, bgpManager.getDCGWIP()))
-            String dcGW = bgpManager.getDCGwIP();
-            if(dcGW != null && !dcGW.isEmpty())
-            {
-                LOG.debug("Building tunnel from DPN {} to DC GW {}", dpnId, dcGW);
-                itmProvider.buildTunnelFromDPNToDCGW(dpnId, new IpAddress(dcGW.toCharArray()));
-            }*/
-            fibManager.populateFibOnNewDpn(dpnId, vpnId, (rd == null) ? vpnName : rd);
+        Boolean newDpnOnVpn = Boolean.FALSE;
+
+        synchronized (vpnName.intern()) {
+            WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
+            InstanceIdentifier<VpnToDpnList> id = VpnUtil.getVpnToDpnListIdentifier(rd, dpnId);
+            Optional<VpnToDpnList> dpnInVpn = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+            org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data
+                    .entry.vpn.to.dpn.list.VpnInterfaces
+                    vpnInterface = new VpnInterfacesBuilder().setInterfaceName(intfName).build();
+
+            if (dpnInVpn.isPresent()) {
+                VpnToDpnList vpnToDpnList = dpnInVpn.get();
+                List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
+                        .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> vpnInterfaces = vpnToDpnList.getVpnInterfaces();
+                if (vpnInterfaces == null) {
+                    vpnInterfaces = new ArrayList<>();
+                }
+                vpnInterfaces.add(vpnInterface);
+                VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder(vpnToDpnList);
+                vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setVpnInterfaces(vpnInterfaces);
+
+                if (writeTxn != null) {
+                    writeTxn.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(), true);
+                } else {
+                    VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build());
+                }
+                /* If earlier state was inactive, it is considered new DPN coming back to the
+                 * same VPN
+                 */
+                if (vpnToDpnList.getDpnState() == VpnToDpnList.DpnState.Inactive) {
+                    newDpnOnVpn = Boolean.TRUE;
+                }
+            } else {
+                List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
+                        .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> vpnInterfaces = new ArrayList<>();
+                vpnInterfaces.add(vpnInterface);
+                VpnToDpnListBuilder vpnToDpnListBuilder = new VpnToDpnListBuilder().setDpnId(dpnId);
+                vpnToDpnListBuilder.setDpnState(VpnToDpnList.DpnState.Active).setVpnInterfaces(vpnInterfaces);
+
+                if (writeTxn != null) {
+                    writeTxn.put(LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build(), true);
+                } else {
+                    VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id, vpnToDpnListBuilder.build());
+                }
+                newDpnOnVpn = Boolean.TRUE;
+            }
+            CheckedFuture<Void, TransactionCommitFailedException> futures = writeTxn.submit();
+            try {
+                futures.get();
+            } catch (InterruptedException | ExecutionException e) {
+                LOG.error("Error adding to dpnToVpnList for vpn {} interface {} dpn {}", vpnName, intfName, dpnId);
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+        /*
+         * Informing the Fib only after writeTxn is submitted successfuly.
+         */
+        if (newDpnOnVpn) {
+            LOG.debug("Sending populateFib event for new dpn {} in VPN {}", dpnId, vpnName);
+            fibManager.populateFibOnNewDpn(dpnId, vpnId, rd, new DpnEnterExitVpnWorker(dpnId, vpnName, rd, true /* entered */));
         }
     }
 
-    private synchronized void removeFromMappingDbs(long vpnId, BigInteger dpnId, String intfName, String vpnName) {
-        //TODO: Delay 'DPN' removal so that other services can cleanup the entries for this dpn
-        String rd = VpnUtil.getVpnRd(broker, vpnName);
-        InstanceIdentifier<VpnToDpnList> id = VpnUtil.getVpnToDpnListIdentifier(rd, dpnId);
-        Optional<VpnToDpnList> dpnInVpn = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
-        if (dpnInVpn.isPresent()) {
-            List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
-                .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> vpnInterfaces = dpnInVpn.get().getVpnInterfaces();
-            org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces
-                    currVpnInterface = new VpnInterfacesBuilder().setInterfaceName(intfName).build();
-
-            if (vpnInterfaces.remove(currVpnInterface)) {
-                if (vpnInterfaces.isEmpty()) {
-                    VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL, id, VpnUtil.DEFAULT_CALLBACK);
-                    fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd);
-                } else {
-                    VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL, id.child(
-                        org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
-                            .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces.class,
-                            new VpnInterfacesKey(intfName)), VpnUtil.DEFAULT_CALLBACK);
+    private void removeOrUpdateVpnToDpnList(long vpnId, BigInteger dpnId, String intfName, String vpnName) {
+        Boolean lastDpnOnVpn = Boolean.FALSE;
+        String rd = VpnUtil.getVpnRd(dataBroker, vpnName);
+        synchronized (vpnName.intern()) {
+            InstanceIdentifier<VpnToDpnList> id = VpnUtil.getVpnToDpnListIdentifier(rd, dpnId);
+            Optional<VpnToDpnList> dpnInVpn = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+            WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
+            if (dpnInVpn.isPresent()) {
+                List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
+                        .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces> vpnInterfaces = dpnInVpn.get().getVpnInterfaces();
+                org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces
+                        currVpnInterface = new VpnInterfacesBuilder().setInterfaceName(intfName).build();
+
+                if (vpnInterfaces.remove(currVpnInterface)) {
+                    if (vpnInterfaces.isEmpty()) {
+                        List<IpAddresses> ipAddresses = dpnInVpn.get().getIpAddresses();
+                        if (ipAddresses == null || ipAddresses.isEmpty()) {
+                            VpnToDpnListBuilder dpnInVpnBuilder =
+                                    new VpnToDpnListBuilder(dpnInVpn.get())
+                                            .setDpnState(VpnToDpnList.DpnState.Inactive)
+                                            .setVpnInterfaces(null);
+                            if (writeTxn != null) {
+                                writeTxn.put(LogicalDatastoreType.OPERATIONAL, id, dpnInVpnBuilder.build(), true);
+                            } else {
+                                VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id, dpnInVpnBuilder.build());
+                            }
+                            lastDpnOnVpn = Boolean.TRUE;
+                        } else {
+                            LOG.warn("vpn interfaces are empty but ip addresses are present for the vpn {} in dpn {}", vpnName, dpnId);
+                        }
+                    } else {
+                        if (writeTxn != null) {
+                            writeTxn.delete(LogicalDatastoreType.OPERATIONAL, id.child(
+                                    org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
+                                            .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces.class,
+                                    new VpnInterfacesKey(intfName)));
+                        } else {
+                            VpnUtil.delete(dataBroker, LogicalDatastoreType.OPERATIONAL, id.child(
+                                    org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
+                                            .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces.class,
+                                    new VpnInterfacesKey(intfName)), VpnUtil.DEFAULT_CALLBACK);
+                        }
+                    }
                 }
             }
+            CheckedFuture<Void, TransactionCommitFailedException> futures = writeTxn.submit();
+            try {
+                futures.get();
+            } catch (InterruptedException | ExecutionException e) {
+                LOG.error("Error removing from dpnToVpnList for vpn {} interface {} dpn {}", vpnName, intfName, dpnId);
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+        if (lastDpnOnVpn) {
+            LOG.debug("Sending cleanup event for dpn {} in VPN {}", dpnId, vpnName);
+            fibManager.cleanUpDpnForVpn(dpnId, vpnId, rd, new DpnEnterExitVpnWorker(dpnId, vpnName, rd, false /* exited */));
         }
     }
 
-    private void addPrefixToBGP(String rd, String prefix, String nextHopIp, long label) {
+    void handleVpnsExportingRoutes(String vpnName, String vpnRd) {
+        List<VpnInstanceOpDataEntry> vpnsToExportRoute = getVpnsExportingMyRoute(vpnName);
+        for (VpnInstanceOpDataEntry vpn : vpnsToExportRoute) {
+            String rd = vpn.getVrfId();
+            List<VrfEntry> vrfEntries = VpnUtil.getAllVrfEntries(dataBroker, vpn.getVrfId());
+            WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
+            if (vrfEntries != null) {
+                for (VrfEntry vrfEntry : vrfEntries) {
+                    try {
+                        if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.STATIC) {
+                            continue;
+                        }
+                        String prefix = vrfEntry.getDestPrefix();
+                        long label = vrfEntry.getLabel();
+                        List<String> nextHops = vrfEntry.getNextHopAddressList();
+                        SubnetRoute route = vrfEntry.getAugmentation(SubnetRoute.class);
+                        for (String nh : nextHops) {
+                            if (route != null) {
+                                LOG.info("Importing subnet route fib entry rd {} prefix {} nexthop {} label {} to vpn {}", vpnRd, prefix, nh, label, vpn.getVpnInstanceName());
+                                importSubnetRouteForNewVpn(vpnRd, prefix, nh, (int)label, route, writeConfigTxn);
+                            } else {
+                                LOG.info("Importing fib entry rd {} prefix {} nexthop {} label {} to vpn {}", vpnRd, prefix, nh, label, vpn.getVpnInstanceName());
+                                fibManager.addOrUpdateFibEntry(dataBroker, vpnRd, prefix, Arrays.asList(nh), (int)label,
+                                        RouteOrigin.SELF_IMPORTED, writeConfigTxn);
+                            }
+                        }
+                    } catch (Exception e) {
+                        LOG.error("Exception occurred while importing route with prefix {} label {} nexthop {} from vpn {} to vpn {}", vrfEntry.getDestPrefix(), vrfEntry.getLabel(), vrfEntry.getNextHopAddressList(), vpn.getVpnInstanceName(), vpnName);
+                    }
+                }
+                writeConfigTxn.submit();
+            } else {
+                LOG.info("No vrf entries to import from vpn {} with rd {}", vpn.getVpnInstanceName(), vpn.getVrfId());
+            }
+        }
+    }
+
+    private void addPrefixToBGP(String rd, String prefix, String nextHopIp, long label, WriteTransaction writeConfigTxn) {
         try {
-            //FIXME: TBD once odl-fib yang has nexthoplist and related changes follow
-            //bgpManager.addPrefix(rd, prefix, nextHopIp, (int)label);
+            LOG.info("ADD: Adding Fib entry rd {} prefix {} nextHop {} label {}", rd, prefix, nextHopIp, label);
+            fibManager.addOrUpdateFibEntry(dataBroker, rd, prefix, Arrays.asList(nextHopIp), (int)label, RouteOrigin.STATIC, writeConfigTxn);
+            bgpManager.advertisePrefix(rd, prefix, Arrays.asList(nextHopIp), (int)label);
+            LOG.info("ADD: Added Fib entry rd {} prefix {} nextHop {} label {}", rd, prefix, nextHopIp, label);
         } catch(Exception e) {
             LOG.error("Add prefix failed", e);
         }
     }
 
-
-    private InstanceIdentifier<VpnInterface> getWildCardPath() {
-        return InstanceIdentifier.create(VpnInterfaces.class).child(VpnInterface.class);
-    }
-
     @Override
-    protected void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
+    public void remove( InstanceIdentifier<VpnInterface> identifier, VpnInterface vpnInterface) {
         LOG.trace("Remove event - key: {}, value: {}" ,identifier, vpnInterface );
+        LOG.info("VPN Interface remove event - intfName {}" ,vpnInterface.getName());
         final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
-        String interfaceName = key.getName();
+        final String interfaceName = key.getName();
 
         InstanceIdentifier<VpnInterface> interfaceId = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
-        Optional<VpnInterface> existingVpnInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, interfaceId);
+        final Optional<VpnInterface> optVpnInterface = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, interfaceId);
         org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface interfaceState =
-            InterfaceUtils.getInterfaceStateFromOperDS(broker, interfaceName);
+                InterfaceUtils.getInterfaceStateFromOperDS(dataBroker, interfaceName);
+        if (optVpnInterface.isPresent()){
+            BigInteger dpnId = BigInteger.ZERO;
+            Boolean dpnIdRetrieved = Boolean.FALSE;
+            if(interfaceState != null){
+                try{
+                    dpnId = InterfaceUtils.getDpIdFromInterface(interfaceState);
+                    dpnIdRetrieved = Boolean.TRUE;
+                }catch (Exception e){
+                    LOG.error("Unable to retrieve dpnId from interface operational data store for interface {}. Fetching from vpn interface op data store. ", interfaceName, e);
+                }
+            } else {
+                LOG.error("Unable to retrieve interfaceState for interface {} , quitting ", interfaceName);
+                return;
+            }
+            final VpnInterface vpnOpInterface = optVpnInterface.get();
+            if(dpnIdRetrieved == Boolean.FALSE){
+                LOG.info("dpnId for {} has not been retrieved yet. Fetching from vpn interface operational DS", interfaceName);
+                dpnId = vpnOpInterface.getDpnId();
+            }
+            final int ifIndex = interfaceState.getIfIndex();
+            final BigInteger dpId = dpnId;
+            DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+            dataStoreCoordinator.enqueueJob("VPNINTERFACE-" + interfaceName,
+                    new Callable<List<ListenableFuture<Void>>>() {
+                        @Override
+                        public List<ListenableFuture<Void>> call() throws Exception {
+                            WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
+                            WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
+                            WriteTransaction writeInvTxn = dataBroker.newWriteOnlyTransaction();
+                            processVpnInterfaceDown(dpId, interfaceName, ifIndex, false, true, writeConfigTxn, writeOperTxn, writeInvTxn);
+                            List<ListenableFuture<Void>> futures = new ArrayList<ListenableFuture<Void>>();
+                            futures.add(writeOperTxn.submit());
+                            futures.add(writeConfigTxn.submit());
+                            futures.add(writeInvTxn.submit());
+                            return futures;
+                        }
+                    });
 
-        if (existingVpnInterface.isPresent() && interfaceState != null) {
-            processVpnInterfaceDown(InterfaceUtils.getDpIdFromInterface(interfaceState), interfaceName, interfaceState.getIfIndex(), false);
-        } else {
+        }else{
             LOG.warn("VPN interface {} was unavailable in operational data store to handle remove event", interfaceName);
         }
     }
 
-    protected void processVpnInterfaceDown(BigInteger dpId, String interfaceName, int lPortTag, boolean isInterfaceStateDown) {
-        VpnInterface vpnInterface = VpnUtil.getOperationalVpnInterface(broker, interfaceName);
-        if(vpnInterface == null) {
-            LOG.info("Unable to process delete/down for interface {} as it is not available in operational data store", interfaceName);
-            return;
-        }
-        String vpnName = vpnInterface.getVpnInstanceName();
+    protected void processVpnInterfaceDown(BigInteger dpId,
+                                           String interfaceName,
+                                           int lPortTag,
+                                           boolean isInterfaceStateDown,
+                                           boolean isConfigRemoval,
+                                           WriteTransaction writeConfigTxn,
+                                           WriteTransaction writeOperTxn,
+                                           WriteTransaction writeInvTxn) {
         InstanceIdentifier<VpnInterface> identifier = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
-
-        synchronized (interfaceName.intern()) {
-            removeAdjacenciesFromVpn(identifier, vpnInterface);
-            LOG.info("Unbinding vpn service from interface {} ", interfaceName);
-            unbindService(dpId, vpnName, interfaceName, lPortTag, isInterfaceStateDown);
+        if (!isInterfaceStateDown) {
+            VpnInterface vpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, interfaceName);
+            if(vpnInterface == null){
+                LOG.info("Unable to process delete/down for interface {} as it is not available in operational data store", interfaceName);
+                return;
+            }else{
+                final String vpnName = vpnInterface.getVpnInstanceName();
+                if(!vpnInterface.isScheduledForRemove()){
+                    VpnUtil.scheduleVpnInterfaceForRemoval(dataBroker, interfaceName, dpId, vpnName, Boolean.TRUE, writeOperTxn);
+                    removeAdjacenciesFromVpn(dpId, interfaceName, vpnInterface.getVpnInstanceName(), writeConfigTxn);
+                    LOG.info("Unbinding vpn service from interface {} ", interfaceName);
+                    unbindService(dpId, vpnName, interfaceName, lPortTag, isInterfaceStateDown, isConfigRemoval, writeConfigTxn, writeInvTxn);
+                }else{
+                    LOG.info("Unbinding vpn service for interface {} has already been scheduled by a different event ", interfaceName);
+                    return;
+                }
+            }
+        } else {
+            // Interface is retained in the DPN, but its Link Down.
+            // Only withdraw the prefixes for this interface from BGP
+            VpnInterface vpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, interfaceName);
+            if(vpnInterface == null){
+                LOG.info("Unable to withdraw adjacencies for vpn interface {} from BGP as it is not available in operational data store", interfaceName);
+                return;
+            }else {
+                withdrawAdjacenciesForVpnFromBgp(identifier, vpnInterface);
+            }
         }
+    }
 
+    private void waitForFibToRemoveVpnPrefix(String interfaceName) {
         // FIB didn't get a chance yet to clean up this VPNInterface
         // Let us give it a chance here !
         LOG.info("VPN Interface {} removal waiting for FIB to clean up ! ", interfaceName);
@@ -604,37 +1120,65 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
         } finally {
             vpnIntfMap.remove(interfaceName);
         }
-
     }
 
-    private void removeAdjacenciesFromVpn(final InstanceIdentifier<VpnInterface> identifier, VpnInterface intf) {
+    private void removeAdjacenciesFromVpn(final BigInteger dpnId, final String interfaceName, final String vpnName,
+                                          WriteTransaction writeConfigTxn) {
         //Read NextHops
+        InstanceIdentifier<VpnInterface> identifier = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
         InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
-        Optional<Adjacencies> adjacencies = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, path);
+        Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
 
-        String rd = VpnUtil.getVpnRd(broker, intf.getVpnInstanceName());
-        LOG.trace("removeAdjacenciesFromVpn: For interface {} RD recovered for vpn {} as rd {}", intf.getName(),
-                intf.getVpnInstanceName(), rd);
+        String rd = VpnUtil.getVpnRd(dataBroker, vpnName);
+        LOG.trace("removeAdjacenciesFromVpn: For interface {} RD recovered for vpn {} as rd {}", interfaceName,
+                vpnName, rd);
         if (adjacencies.isPresent()) {
             List<Adjacency> nextHops = adjacencies.get().getAdjacency();
 
             if (!nextHops.isEmpty()) {
                 LOG.trace("NextHops are " + nextHops);
                 for (Adjacency nextHop : nextHops) {
-                    // Commenting the release of ID here as it will be released by FIB
-                   /* VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME,
-                                      VpnUtil.getNextHopLabelKey(rd, nextHop.getIpAddress()));
-                    VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
-                                   VpnUtil.getPrefixToInterfaceIdentifier(
-                                       VpnUtil.getVpnId(broker, intf.getVpnInstanceName()),
-                                       nextHop.getIpAddress()),
-                                   VpnUtil.DEFAULT_CALLBACK);*/
-                    if (rd.equals(intf.getVpnInstanceName())) {
+                    List<String> nhList = new ArrayList<String>();
+                    if (nextHop.getMacAddress() == null || nextHop.getMacAddress().isEmpty()) {
+                        // This is either an extra-route (or) a learned IP via subnet-route
+                        String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
+                        if (nextHopIp == null || nextHopIp.isEmpty()) {
+                            LOG.error("Unable to obtain nextHopIp for extra-route/learned-route in rd {} prefix {}",
+                                    rd, nextHop.getIpAddress());
+                            continue;
+                        }
+                        nhList = Arrays.asList(nextHopIp);
+                    } else {
+                        // This is a primary adjacency
+                        nhList = nextHop.getNextHopIpList();
+                    }
+                    if (rd.equals(vpnName)) {
                         //this is an internal vpn - the rd is assigned to the vpn instance name;
                         //remove from FIB directly
-                        removeFibEntryFromDS(intf.getVpnInstanceName(), nextHop.getIpAddress());
+                        for(String nh : nhList) {
+                            fibManager.removeOrUpdateFibEntry(dataBroker, vpnName, nextHop.getIpAddress(), nh, writeConfigTxn);
+                        }
                     } else {
-                        removePrefixFromBGP(rd, nextHop.getIpAddress());
+                        List<VpnInstanceOpDataEntry> vpnsToImportRoute = getVpnsImportingMyRoute(vpnName);
+                        for (String nh : nhList) {
+                            //IRT: remove routes from other vpns importing it
+                            removePrefixFromBGP(rd, nextHop.getIpAddress(), nh, writeConfigTxn);
+                            for (VpnInstanceOpDataEntry vpn : vpnsToImportRoute) {
+                                String vpnRd = vpn.getVrfId();
+                                if (vpnRd != null) {
+                                    LOG.info("Removing Exported route with rd {} prefix {} from VPN {}", vpnRd, nextHop.getIpAddress(), vpn.getVpnInstanceName());
+                                    fibManager.removeOrUpdateFibEntry(dataBroker, vpnRd, nextHop.getIpAddress(), nh, writeConfigTxn);
+                                }
+                            }
+                        }
+                    }
+                    String ip = nextHop.getIpAddress().split("/")[0];
+                    VpnPortipToPort vpnPortipToPort = VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker,
+                            vpnName, ip);
+                    if (vpnPortipToPort != null && !vpnPortipToPort.isConfig()) {
+                        LOG.trace("VpnInterfaceManager removing adjacency for Interface {} ip {} from VpnPortData Entry",
+                                vpnPortipToPort.getPortName(),ip);
+                        VpnUtil.removeVpnPortFixedIpToPort(dataBroker, vpnName, ip);
                     }
                 }
             }
@@ -642,25 +1186,38 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
     }
 
 
-    private void unbindService(BigInteger dpId, String vpnInstanceName, String vpnInterfaceName,
-                               int lPortTag, boolean isInterfaceStateDown) {
-        if (!isInterfaceStateDown) {
-            MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION,
-                           InterfaceUtils.buildServiceId(vpnInterfaceName,
-                                                         VpnConstants.L3VPN_SERVICE_IDENTIFIER));
+    private void unbindService(BigInteger dpId, String vpnInstanceName, final String vpnInterfaceName,
+                               int lPortTag, boolean isInterfaceStateDown, boolean isConfigRemoval,
+                               WriteTransaction writeConfigTxn, WriteTransaction writeInvTxn) {
+        short l3vpn_service_index = ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants.L3VPN_SERVICE_INDEX);
+        if (!isInterfaceStateDown && isConfigRemoval) {
+            DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
+            dataStoreCoordinator.enqueueJob(vpnInterfaceName,
+                    new Callable<List<ListenableFuture<Void>>>() {
+                        @Override
+                        public List<ListenableFuture<Void>> call() throws Exception {
+                            WriteTransaction writeTxn = dataBroker.newWriteOnlyTransaction();
+                            writeTxn.delete(LogicalDatastoreType.CONFIGURATION,
+                                    InterfaceUtils.buildServiceId(vpnInterfaceName,
+                                            ServiceIndex.getIndex(NwConstants.L3VPN_SERVICE_NAME, NwConstants.L3VPN_SERVICE_INDEX)));
+
+                            List<ListenableFuture<Void>> futures = new ArrayList<ListenableFuture<Void>>();
+                            futures.add(writeTxn.submit());
+                            return futures;
+                        }
+                    });
         }
-        long vpnId = VpnUtil.getVpnId(broker, vpnInstanceName);
-        makeArpFlow(dpId, VpnConstants.L3VPN_SERVICE_IDENTIFIER, lPortTag, vpnInterfaceName,
-                    vpnId, ArpReplyOrRequest.REQUEST, NwConstants.DEL_FLOW);
-        makeArpFlow(dpId, VpnConstants.L3VPN_SERVICE_IDENTIFIER, lPortTag, vpnInterfaceName,
-                vpnId, ArpReplyOrRequest.REPLY, NwConstants.DEL_FLOW);
+        long vpnId = VpnUtil.getVpnId(dataBroker, vpnInstanceName);
+        setupGwMacIfExternalVpn(dpId, vpnInterfaceName, vpnId, writeConfigTxn, NwConstants.DEL_FLOW);
     }
 
 
-    private void removePrefixFromBGP(String rd, String prefix) {
-        removeFibEntryFromDS(rd, prefix);
+    private void removePrefixFromBGP(String rd, String prefix, String nextHop, WriteTransaction writeConfigTxn) {
         try {
-            bgpManager.withdrawPrefix(rd, prefix);
+            LOG.info("VPN WITHDRAW: Removing Fib Entry rd {} prefix {}", rd, prefix);
+            fibManager.removeOrUpdateFibEntry(dataBroker, rd, prefix, nextHop, writeConfigTxn);
+            bgpManager.withdrawPrefix(rd, prefix); // TODO: Might be needed to include nextHop here
+            LOG.info("VPN WITHDRAW: Removed Fib Entry rd {} prefix {}", rd, prefix);
         } catch(Exception e) {
             LOG.error("Delete prefix failed", e);
         }
@@ -668,43 +1225,47 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
 
     @Override
     protected void update(InstanceIdentifier<VpnInterface> identifier, VpnInterface original, VpnInterface update) {
-        if (LOG.isTraceEnabled()) {
-            LOG.trace("Updating VPN Interface : key " + identifier + ",  original value=" + original + ", update " +
-                    "value=" + update);
-        }
+        LOG.trace("Updating VPN Interface : key {},  original value={}, update value={}", identifier, original, update);
+        LOG.info("VPN Interface update event - intfName {}" ,update.getName());
         String oldVpnName = original.getVpnInstanceName();
         String newVpnName = update.getVpnInstanceName();
-        List<Adjacency> oldAdjs = original.getAugmentation(Adjacencies.class).getAdjacency();
-        List<Adjacency> newAdjs = update.getAugmentation(Adjacencies.class).getAdjacency();
-        if (oldAdjs == null) {
-            oldAdjs = new ArrayList<>();
+        BigInteger dpnId = update.getDpnId();
+        List<Adjacency> oldAdjsList = new ArrayList<>();
+        List<Adjacency> newAdjsList = new ArrayList<>();
+        Adjacencies oldAdjs = original.getAugmentation(Adjacencies.class);
+        Adjacencies newAdjs = update.getAugmentation(Adjacencies.class);
+        if (oldAdjs != null) {
+            oldAdjsList = oldAdjs.getAdjacency();
         }
-        if (newAdjs == null) {
-            newAdjs = new ArrayList<>();
+        if (newAdjs != null) {
+            newAdjsList = newAdjs.getAdjacency();
         }
         //handles switching between <internal VPN - external VPN>
         if (!oldVpnName.equals(newVpnName)) {
             remove(identifier, original);
+            waitForFibToRemoveVpnPrefix(update.getName());
             add(identifier, update);
         }
         //handle both addition and removal of adjacencies
         //currently, new adjacency may be an extra route
-        if (!oldAdjs.equals(newAdjs)) {
-            for (Adjacency adj : newAdjs) {
-                if (oldAdjs.contains(adj)) {
-                    oldAdjs.remove(adj);
+        if (!oldAdjsList.equals(newAdjsList)) {
+            for (Adjacency adj : newAdjsList) {
+                if (oldAdjsList.contains(adj)) {
+                    oldAdjsList.remove(adj);
                 } else {
                     // add new adjacency - right now only extra route will hit this path
                     addNewAdjToVpnInterface(identifier, adj);
                 }
             }
-            for (Adjacency adj : oldAdjs) {
-                delAdjFromVpnInterface(identifier, adj);
+            for (Adjacency adj : oldAdjsList) {
+                delAdjFromVpnInterface(identifier, adj, dpnId);
             }
         }
     }
 
-    public void processArpRequest(IpAddress srcIP, PhysAddress srcMac, IpAddress targetIP, PhysAddress targetMac,String srcInterface){
+    public void processArpRequest(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715
+                                          .IpAddress srcIP, PhysAddress srcMac, org.opendaylight.yang.gen.v1.urn.ietf
+            .params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress targetIP, PhysAddress targetMac, String srcInterface){
         //Build ARP response with ARP requests TargetIp TargetMac as the Arp Response SrcIp and SrcMac
         SendArpResponseInput input = new SendArpResponseInputBuilder().setInterface(srcInterface)
                 .setDstIpaddress(srcIP).setDstMacaddress(srcMac).setSrcIpaddress(targetIP).setSrcMacaddress(targetMac).build();
@@ -731,49 +1292,67 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
         StringBuilder errorText = new StringBuilder();
         for(RpcError error : errors) {
             errorText.append(",").append(error.getErrorType()).append("-")
-                     .append(error.getMessage());
+                    .append(error.getMessage());
         }
         return errorText.toString();
     }
 
-    private String getTunnelInterfaceFlowRef(BigInteger dpnId, short tableId, String ifName) {
-        return new StringBuilder().append(dpnId).append(tableId).append(ifName).toString();
-    }
-
-
-
-    public synchronized void addFibEntryToDS(String rd, String prefix,
-            String nexthop, int label) {
-
-        VrfEntry vrfEntry = new VrfEntryBuilder().setDestPrefix(prefix).
-                    setNextHopAddressList(Arrays.asList(nexthop)).setLabel((long)label).build();
-        LOG.debug("Created vrfEntry for {} nexthop {} label {}", prefix, nexthop, label);
-
-        List<VrfEntry> vrfEntryList = new ArrayList<>();
-        vrfEntryList.add(vrfEntry);
-
-        InstanceIdentifierBuilder<VrfTables> idBuilder =
-                    InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
-        InstanceIdentifier<VrfTables> vrfTableId = idBuilder.build();
-
-        VrfTables vrfTableNew = new VrfTablesBuilder().setRouteDistinguisher(rd).
-                    setVrfEntry(vrfEntryList).build();
-
-        VpnUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTableNew);
+    private void addToLabelMapper(Long label, BigInteger dpnId, String prefix, List<String> nextHopIpList, Long vpnId,
+                                  String vpnInterfaceName, Long elanTag, boolean isSubnetRoute, String rd,
+                                  WriteTransaction writeOperTxn) {
+        Preconditions.checkNotNull(label, "label cannot be null or empty!");
+        Preconditions.checkNotNull(prefix, "prefix cannot be null or empty!");
+        Preconditions.checkNotNull(vpnId, "vpnId cannot be null or empty!");
+        Preconditions.checkNotNull(rd, "rd cannot be null or empty!");
+        if (!isSubnetRoute) {
+            // NextHop must be present for non-subnetroute entries
+            Preconditions.checkNotNull(nextHopIpList, "nextHopIp cannot be null or empty!");
+        }
+        LOG.info("Adding to label mapper : label {} dpn {} prefix {} nexthoplist {} vpnid {} vpnIntfcName {} rd {}", label, dpnId, prefix, nextHopIpList, vpnId, vpnInterfaceName, rd);
+        if (dpnId != null) {
+            InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
+                    .child(LabelRouteInfo.class, new LabelRouteInfoKey((long)label)).build();
+            LabelRouteInfoBuilder lriBuilder = new LabelRouteInfoBuilder();
+            lriBuilder.setLabel(label).setDpnId(dpnId).setPrefix(prefix).setNextHopIpList(nextHopIpList).setParentVpnid(vpnId)
+                    .setIsSubnetRoute(isSubnetRoute);
+            if (elanTag != null) {
+                lriBuilder.setElanTag(elanTag);
+            }
+            if (vpnInterfaceName != null) {
+                lriBuilder.setVpnInterfaceName(vpnInterfaceName);
+            }
+            lriBuilder.setParentVpnRd(rd);
+            VpnInstanceOpDataEntry vpnInstanceOpDataEntry = VpnUtil.getVpnInstanceOpData(dataBroker, rd);
+            if (vpnInstanceOpDataEntry != null) {
+                List<String> vpnInstanceNames = Arrays.asList(vpnInstanceOpDataEntry.getVpnInstanceName());
+                lriBuilder.setVpnInstanceList(vpnInstanceNames);
+            }
+            LabelRouteInfo lri = lriBuilder.build();
+            LOG.trace("Adding route info to label map: {}", lri);
+            if (writeOperTxn != null) {
+                writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, lriIid, lri, true);
+            } else {
+                VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid, lri);
+            }
+        } else {
+            LOG.trace("Can't add entry to label map for lable {},dpnId is null", label);
+        }
     }
 
-    public synchronized void addSubnetRouteFibEntryToDS(String rd, String prefix,
-                                                        String nexthop, int label,long elantag) {
-
+    public void addSubnetRouteFibEntryToDS(String rd, String vpnName, String prefix, String nextHop, int label,
+                                                        long elantag, BigInteger dpnId, WriteTransaction writeTxn) {
         SubnetRoute route = new SubnetRouteBuilder().setElantag(elantag).build();
+        RouteOrigin origin = RouteOrigin.STATIC; // Only case when a route is considered as directly connected
+        VrfEntry vrfEntry = new VrfEntryBuilder().setDestPrefix(prefix).setNextHopAddressList(Arrays.asList(nextHop))
+                .setLabel((long)label).setOrigin(origin.getValue())
+                .addAugmentation(SubnetRoute.class, route).build();
 
-        VrfEntry vrfEntry = new VrfEntryBuilder().setDestPrefix(prefix).
-                setNextHopAddressList(Arrays.asList(nexthop)).setLabel((long)label).addAugmentation(SubnetRoute
-                .class,route).build();
-        LOG.debug("Created vrfEntry for {} nexthop {} label {} and elantag {}", prefix, nexthop, label, elantag);
+        LOG.debug("Created vrfEntry for {} nexthop {} label {} and elantag {}", prefix, nextHop, label, elantag);
 
-        List<VrfEntry> vrfEntryList = new ArrayList<VrfEntry>();
-        vrfEntryList.add(vrfEntry);
+        //TODO: What should be parentVpnId? Get it from RD?
+        long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
+        addToLabelMapper((long)label, dpnId, prefix, Arrays.asList(nextHop), vpnId, null, elantag, true, rd, null);
+        List<VrfEntry> vrfEntryList = Arrays.asList(vrfEntry);
 
         InstanceIdentifierBuilder<VrfTables> idBuilder =
                 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
@@ -782,45 +1361,81 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
         VrfTables vrfTableNew = new VrfTablesBuilder().setRouteDistinguisher(rd).
                 setVrfEntry(vrfEntryList).build();
 
-        VpnUtil.syncUpdate(broker, LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTableNew);
-    }
-
-    public synchronized void removeFibEntryFromDS(String rd, String prefix) {
-
-        LOG.debug("Removing fib entry with destination prefix {} from vrf table for rd {}", prefix, rd);
-
-        InstanceIdentifierBuilder<VrfEntry> idBuilder =
-            InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd)).child(VrfEntry.class, new VrfEntryKey(prefix));
-        InstanceIdentifier<VrfEntry> vrfEntryId = idBuilder.build();
-        MDSALUtil.syncDelete(broker, LogicalDatastoreType.CONFIGURATION, vrfEntryId);
+        if (writeTxn != null) {
+            writeTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTableNew, true);
+        } else {
+            VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTableNew);
+        }
 
+        List<VpnInstanceOpDataEntry> vpnsToImportRoute = getVpnsImportingMyRoute(vpnName);
+        if (vpnsToImportRoute.size() > 0) {
+            VrfEntry importingVrfEntry = new VrfEntryBuilder().setDestPrefix(prefix).setNextHopAddressList(Arrays.asList(nextHop))
+                    .setLabel((long) label).setOrigin(RouteOrigin.SELF_IMPORTED.getValue())
+                    .addAugmentation(SubnetRoute.class, route).build();
+            List<VrfEntry> importingVrfEntryList = Arrays.asList(importingVrfEntry);
+            for (VpnInstanceOpDataEntry vpnInstance : vpnsToImportRoute) {
+                LOG.info("Exporting subnet route rd {} prefix {} nexthop {} label {} to vpn {}", rd, prefix, nextHop, label, vpnInstance.getVpnInstanceName());
+                String importingRd = vpnInstance.getVrfId();
+                InstanceIdentifier<VrfTables> importingVrfTableId = InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(importingRd)).build();
+                VrfTables importingVrfTable = new VrfTablesBuilder().setRouteDistinguisher(importingRd).setVrfEntry(importingVrfEntryList).build();
+                if (writeTxn != null) {
+                    writeTxn.merge(LogicalDatastoreType.CONFIGURATION, importingVrfTableId, importingVrfTable, true);
+                } else {
+                    VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, importingVrfTableId, importingVrfTable);
+                }
+            }
+        }
     }
 
-    public synchronized void removeVrfFromDS(String rd) {
-        LOG.debug("Removing vrf table for  rd {}", rd);
+    public synchronized void importSubnetRouteForNewVpn(String rd, String prefix, String nextHop, int label,
+                                                        SubnetRoute route, WriteTransaction writeConfigTxn) {
 
+        RouteOrigin origin = RouteOrigin.SELF_IMPORTED;
+        VrfEntry vrfEntry = new VrfEntryBuilder().setDestPrefix(prefix).setNextHopAddressList(Arrays.asList(nextHop))
+                .setLabel((long)label).setOrigin(origin.getValue())
+                .addAugmentation(SubnetRoute.class, route).build();
+        LOG.debug("Created vrfEntry for {} nexthop {} label {} and elantag {}", prefix, nextHop, label, route.getElantag());
+        List<VrfEntry> vrfEntryList = Arrays.asList(vrfEntry);
         InstanceIdentifierBuilder<VrfTables> idBuilder =
                 InstanceIdentifier.builder(FibEntries.class).child(VrfTables.class, new VrfTablesKey(rd));
         InstanceIdentifier<VrfTables> vrfTableId = idBuilder.build();
+        VrfTables vrfTableNew = new VrfTablesBuilder().setRouteDistinguisher(rd).
+                setVrfEntry(vrfEntryList).build();
+        if (writeConfigTxn != null) {
+            writeConfigTxn.merge(LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTableNew, true);
+        } else {
+            VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.CONFIGURATION, vrfTableId, vrfTableNew);
+        }
+    }
 
-        VpnUtil.delete(broker, LogicalDatastoreType.CONFIGURATION, vrfTableId, VpnUtil.DEFAULT_CALLBACK);
-
+    public void deleteSubnetRouteFibEntryFromDS(String rd, String prefix, String vpnName){
+        fibManager.removeFibEntry(dataBroker, rd, prefix, null);
+        List<VpnInstanceOpDataEntry> vpnsToImportRoute = getVpnsImportingMyRoute(vpnName);
+        for (VpnInstanceOpDataEntry vpnInstance : vpnsToImportRoute) {
+            String importingRd = vpnInstance.getVrfId();
+            LOG.info("Deleting imported subnet route rd {} prefix {} from vpn {}", rd, prefix, vpnInstance.getVpnInstanceName());
+            fibManager.removeFibEntry(dataBroker, importingRd, prefix, null);
+        }
     }
 
     protected void addNewAdjToVpnInterface(InstanceIdentifier<VpnInterface> identifier, Adjacency adj) {
 
-        Optional<VpnInterface> optVpnInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+        Optional<VpnInterface> optVpnInterface = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, identifier);
 
         if (optVpnInterface.isPresent()) {
             VpnInterface currVpnIntf = optVpnInterface.get();
             String prefix = VpnUtil.getIpPrefix(adj.getIpAddress());
             String rd = getRouteDistinguisher(currVpnIntf.getVpnInstanceName());
+            rd = (rd != null) ? rd : currVpnIntf.getVpnInstanceName();
             InstanceIdentifier<Adjacencies> adjPath = identifier.augmentation(Adjacencies.class);
-            Optional<Adjacencies> optAdjacencies = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, adjPath);
+            Optional<Adjacencies> optAdjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, adjPath);
             long label =
                     VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
-                            VpnUtil.getNextHopLabelKey((rd != null) ? rd : currVpnIntf.getVpnInstanceName(), prefix));
-
+                            VpnUtil.getNextHopLabelKey(rd, prefix));
+            if (label == 0) {
+                LOG.error("Unable to fetch label from Id Manager. Bailing out of adding new adjacency {} to vpn interface {} for vpn {}", adj.getIpAddress(), currVpnIntf.getName(), currVpnIntf.getVpnInstanceName());
+                return;
+            }
             List<Adjacency> adjacencies;
             if (optAdjacencies.isPresent()) {
                 adjacencies = optAdjacencies.get().getAdjacency();
@@ -829,50 +1444,66 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
                 adjacencies = new ArrayList<>();
             }
 
-            adjacencies.add(new AdjacencyBuilder(adj).setLabel(label).setNextHopIp(adj.getNextHopIp())
-                    .setIpAddress(prefix).setKey(new AdjacencyKey(prefix)).build());
-
+            adjacencies.add(new AdjacencyBuilder(adj).setLabel(label).setNextHopIpList(adj.getNextHopIpList())
+                    .setIpAddress(prefix).setMacAddress(adj.getMacAddress()).setKey(new AdjacencyKey(prefix)).build());
+            
             Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencies);
-            VpnInterface newVpnIntf = VpnUtil.getVpnInterface(currVpnIntf.getName(), currVpnIntf.getVpnInstanceName(), aug);
-
-            VpnUtil.syncUpdate(broker, LogicalDatastoreType.OPERATIONAL, identifier, newVpnIntf);
-            addExtraRoute(adj.getIpAddress(), adj.getNextHopIp(), rd, currVpnIntf.getVpnInstanceName(), (int) label, currVpnIntf.getName());
-
+            VpnInterface newVpnIntf = VpnUtil.getVpnInterface(currVpnIntf.getName(), currVpnIntf.getVpnInstanceName(), aug, currVpnIntf.getDpnId(), currVpnIntf.isScheduledForRemove());
+
+            VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.OPERATIONAL, identifier, newVpnIntf);
+            
+            if (adj.getMacAddress() != null && !adj.getMacAddress().isEmpty()) {
+                LOG.trace("Adding prefix {} to interface {} for vpn {}", prefix, currVpnIntf.getName(), currVpnIntf.getVpnInstanceName());
+                VpnUtil.syncUpdate( dataBroker,
+                        LogicalDatastoreType.OPERATIONAL,
+                        VpnUtil.getPrefixToInterfaceIdentifier(
+                                VpnUtil.getVpnId(dataBroker, currVpnIntf.getVpnInstanceName()), prefix),
+                        VpnUtil.getPrefixToInterface(currVpnIntf.getDpnId(), currVpnIntf.getName(), prefix));
+            }
+                      
+            if (adj.getNextHopIpList() != null) {
+                for (String nh : adj.getNextHopIpList()) {
+                    addExtraRoute(adj.getIpAddress(), nh, rd, currVpnIntf.getVpnInstanceName(), (int) label,
+                            currVpnIntf.getName());
+                }
+            }
         }
-
     }
 
-    protected void delAdjFromVpnInterface(InstanceIdentifier<VpnInterface> identifier, Adjacency adj) {
-        Optional<VpnInterface> optVpnInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, identifier);
+    protected void delAdjFromVpnInterface(InstanceIdentifier<VpnInterface> identifier, Adjacency adj, BigInteger dpnId) {
+        Optional<VpnInterface> optVpnInterface = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, identifier);
 
         if (optVpnInterface.isPresent()) {
             VpnInterface currVpnIntf = optVpnInterface.get();
 
             InstanceIdentifier<Adjacencies> path = identifier.augmentation(Adjacencies.class);
-            Optional<Adjacencies> optAdjacencies = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, path);
+            Optional<Adjacencies> optAdjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
             if (optAdjacencies.isPresent()) {
                 List<Adjacency> adjacencies = optAdjacencies.get().getAdjacency();
 
                 if (!adjacencies.isEmpty()) {
                     String rd = getRouteDistinguisher(currVpnIntf.getVpnInstanceName());
+                    rd = (rd != null) ? rd :currVpnIntf.getVpnInstanceName();
                     LOG.trace("Adjacencies are " + adjacencies);
                     Iterator<Adjacency> adjIt = adjacencies.iterator();
                     while (adjIt.hasNext()) {
                         Adjacency adjElem = adjIt.next();
                         if (adjElem.getIpAddress().equals(adj.getIpAddress())) {
-                            // Commenting the release of ID here as it will be released by FIB
-                           /* VpnUtil.releaseId(idManager, VpnConstants.VPN_IDPOOL_NAME,
-                                    VpnUtil.getNextHopLabelKey(rd, adj.getIpAddress()));*/
                             adjIt.remove();
 
                             Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencies);
                             VpnInterface newVpnIntf = VpnUtil.getVpnInterface(currVpnIntf.getName(),
-                                                                              currVpnIntf.getVpnInstanceName(),
-                                                                              aug);
+                                    currVpnIntf.getVpnInstanceName(),
+                                    aug, dpnId, currVpnIntf.isScheduledForRemove());
 
-                            VpnUtil.syncUpdate(broker, LogicalDatastoreType.OPERATIONAL, identifier, newVpnIntf);
+                            VpnUtil.syncUpdate(dataBroker, LogicalDatastoreType.OPERATIONAL, identifier, newVpnIntf);
 
-                            delExtraRoute(adj.getIpAddress(), rd, currVpnIntf.getVpnInstanceName());
+                            if (adj.getNextHopIpList() != null) {
+                                for (String nh : adj.getNextHopIpList()) {
+                                    delExtraRoute(adj.getIpAddress(), nh, rd, currVpnIntf.getVpnInstanceName(),
+                                            currVpnIntf.getName());
+                                }
+                            }
                             break;
                         }
 
@@ -883,349 +1514,168 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
 
     }
 
-    protected void addExtraRoute(String destination, String nextHop, String rd, String routerID, int label, String intfName) {
+    protected void addExtraRoute(String destination, String nextHop, String rd, String routerID, int label,
+                                 String intfName) {
 
         //add extra route to vpn mapping; advertise with nexthop as tunnel ip
         VpnUtil.syncUpdate(
-            broker,
-            LogicalDatastoreType.OPERATIONAL,
-            VpnUtil.getVpnToExtrarouteIdentifier(
-                (rd != null) ? rd : routerID, destination),
-            VpnUtil.getVpnToExtraroute(destination, nextHop));
+                dataBroker,
+                LogicalDatastoreType.OPERATIONAL,
+                VpnUtil.getVpnToExtrarouteIdentifier( (rd != null) ? rd : routerID, destination),
+                VpnUtil.getVpnToExtraroute(destination, Arrays.asList(nextHop)));
 
+        BigInteger dpnId = null;
         if (intfName != null && !intfName.isEmpty()) {
-            BigInteger dpnId = InterfaceUtils.getDpnForInterface(interfaceManager, intfName);
-            String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, dpnId);
+            dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
+            String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
             if (nextHopIp == null || nextHopIp.isEmpty()) {
-                LOG.warn("NextHop for interface {} is null / empty. Failed advertising extra route for prefix {}", intfName, destination);
+                LOG.error("NextHop for interface {} is null / empty. Failed advertising extra route for prefix {}",
+                        intfName, destination);
+                return;
             }
             nextHop = nextHopIp;
         }
+        List<String> nextHopIpList = Arrays.asList(nextHop);
         if (rd != null) {
-            addPrefixToBGP(rd, destination, nextHop, label);
-        } else {
-            // ### add FIB route directly
-            addFibEntryToDS(routerID, destination, nextHop, label);
+            /* Label mapper is required only for BGP VPN and not for Internal VPN */
+            addToLabelMapper((long) label, dpnId, destination, nextHopIpList, VpnUtil.getVpnId(dataBroker, routerID),
+                    intfName, null, false, rd, null);
         }
-    }
 
-    protected void delExtraRoute(String destination, String rd, String routerID) {
-        if (rd != null) {
-            removePrefixFromBGP(rd, destination);
+        // TODO (eperefr): This is a limitation to be stated in docs. When configuring static route to go to
+        // another VPN, there can only be one nexthop or, at least, the nexthop to the interVpnLink should be in
+        // first place.
+        Optional<InterVpnLink> optInterVpnLink = InterVpnLinkUtil.getInterVpnLinkByEndpointIp(dataBroker, nextHop);
+        if ( optInterVpnLink.isPresent() ) {
+            InterVpnLink interVpnLink = optInterVpnLink.get();
+            // If the nexthop is the endpoint of Vpn2, then prefix must be advertised to Vpn1 in DC-GW, with nexthops
+            // pointing to the DPNs where Vpn1 is instantiated. LFIB in these DPNS must have a flow entry, with lower
+            // priority, where if Label matches then sets the lportTag of the Vpn2 endpoint and goes to LportDispatcher
+            // This is like leaking one of the Vpn2 routes towards Vpn1
+            boolean nexthopIsVpn2 = ( interVpnLink.getSecondEndpoint().getIpAddress().getValue().equals(nextHop) );
+            String srcVpnUuid = (nexthopIsVpn2) ? interVpnLink.getSecondEndpoint().getVpnUuid().getValue()
+                    : interVpnLink.getFirstEndpoint().getVpnUuid().getValue();
+            String dstVpnUuid = (nexthopIsVpn2) ? interVpnLink.getFirstEndpoint().getVpnUuid().getValue()
+                    : interVpnLink.getSecondEndpoint().getVpnUuid().getValue();
+            String dstVpnRd = VpnUtil.getVpnRd(dataBroker, dstVpnUuid);
+            long newLabel = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
+                    VpnUtil.getNextHopLabelKey(dstVpnRd, destination));
+            if (newLabel == 0) {
+                LOG.error("Unable to fetch label from Id Manager. Bailing out of adding intervpnlink route for destination {}", destination);
+                return;
+            }
+            InterVpnLinkUtil.leakRoute(dataBroker, bgpManager, interVpnLink, srcVpnUuid, dstVpnUuid, destination, newLabel);
         } else {
-            // ### add FIB route directly
-            removeFibEntryFromDS(routerID, destination);
-        }
-    }
-
-    class VpnInterfaceOpListener extends AbstractDataChangeListener<VpnInterface> {
-
-        public VpnInterfaceOpListener() {
-            super(VpnInterface.class);
-        }
-
-        @Override
-        protected void remove(InstanceIdentifier<VpnInterface> identifier, VpnInterface del) {
-            final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
-            String interfaceName = key.getName();
-            String vpnName = del.getVpnInstanceName();
-
-            LOG.trace("VpnInterfaceOpListener removed: interface name {} vpnName {}", interfaceName, vpnName);
-            //decrement the vpn interface count in Vpn Instance Op Data
-            InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance>
-                    id = VpnUtil.getVpnInstanceToVpnIdIdentifier(vpnName);
-            Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance
-                    = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
-
-            if (vpnInstance.isPresent()) {
-                String rd = null;
-                rd = vpnInstance.get().getVrfId();
-                //String rd = getRouteDistinguisher(del.getVpnInstanceName());
-
-                VpnInstanceOpDataEntry vpnInstOp = VpnUtil.getVpnInstanceOpData(broker, rd);
-                LOG.trace("VpnInterfaceOpListener removed: interface name {} rd {} vpnName {} in Vpn Op Instance {}",
-                        interfaceName, rd, vpnName, vpnInstOp);
-
-                if (vpnInstOp != null) {
-                    // Vpn Interface removed => No more adjacencies from it.
-                    // Hence clean up interface from vpn-dpn-interface list.
-                    Adjacency adjacency = del.getAugmentation(Adjacencies.class).getAdjacency().get(0);
-                    Optional<Prefixes> prefixToInterface = Optional.absent();
-                    prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                            VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                    VpnUtil.getIpPrefix(adjacency.getIpAddress())));
-                    if (!prefixToInterface.isPresent()) {
-                        prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                                VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                        VpnUtil.getIpPrefix(adjacency.getNextHopIp())));
-                    }
-                    if (prefixToInterface.isPresent()) {
-                        VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
-                                VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                        prefixToInterface.get().getIpAddress()),
-                                VpnUtil.DEFAULT_CALLBACK);
-                        synchronized (interfaceName.intern()) {
-                            updateDpnDbs(prefixToInterface.get().getDpnId(), del.getVpnInstanceName(), interfaceName, false);
-                        }
-                    }
-//                    Long ifCnt = 0L;
-//                    //ifCnt = vpnInstOp.getVpnInterfaceCount();
-//                    LOG.trace("VpnInterfaceOpListener removed: interface name {} rd {} vpnName {} Intf count {}",
-//                            interfaceName, rd, vpnName, ifCnt);
-//                    if ((ifCnt != null) && (ifCnt > 0)) {
-//                        VpnUtil.asyncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
-//                                VpnUtil.getVpnInstanceOpDataIdentifier(rd),
-//                                VpnUtil.updateIntfCntInVpnInstOpData(ifCnt - 1, rd), VpnUtil.DEFAULT_CALLBACK);
-//                    }
-                }
+            if (rd != null) {
+                addPrefixToBGP(rd, destination, nextHop, label, null);
             } else {
-                LOG.error("rd not retrievable as vpninstancetovpnid for vpn {} is absent, trying rd as ", vpnName, vpnName);
+                // ### add FIB route directly
+                fibManager.addOrUpdateFibEntry(dataBroker, routerID, destination, Arrays.asList(nextHop), label, RouteOrigin.STATIC, null);
             }
-            notifyTaskIfRequired(interfaceName);
         }
-
-        private void notifyTaskIfRequired(String intfName) {
-            Runnable notifyTask = vpnIntfMap.remove(intfName);
-            if (notifyTask == null) {
-                LOG.trace("VpnInterfaceOpListener update: No Notify Task queued for vpnInterface {}", intfName);
-                return;
+    }
+    protected void delExtraRoute(String destination, String nextHop, String rd, String routerID, String intfName) {
+        if (intfName != null && !intfName.isEmpty()) {
+            BigInteger dpnId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, intfName);
+            String nextHopIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, dpnId);
+            if (nextHopIp == null || nextHopIp.isEmpty()) {
+                LOG.warn("NextHop for interface {} is null / empty. Failed advertising extra route for prefix {}",
+                        intfName, destination);
             }
-            executorService.execute(notifyTask);
+            nextHop = nextHopIp;
         }
 
-        @Override
-        protected void update(InstanceIdentifier<VpnInterface> identifier, VpnInterface original, VpnInterface update) {
-            final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
-            String interfaceName = key.getName();
+        if (rd != null) {
+            removePrefixFromBGP(rd, destination, nextHop, null);
+        } else {
+            // ### add FIB route directly
+            fibManager.removeOrUpdateFibEntry(dataBroker, routerID, destination, nextHop, null);
+        }
+    }
 
-            if (original.getVpnInstanceName().equals(update.getVpnInstanceName())) {
-                return;
+    void publishAddNotification(final BigInteger dpnId, final String vpnName, final String rd) {
+        LOG.debug("Sending notification for add dpn {} in vpn {} event ", dpnId, vpnName);
+        AddEventData data = new AddEventDataBuilder().setVpnName(vpnName).setRd(rd).setDpnId(dpnId).build();
+        AddDpnEvent event = new AddDpnEventBuilder().setAddEventData(data).build();
+        final ListenableFuture<? extends Object> eventFuture = notificationPublishService.offerNotification(event);
+        Futures.addCallback(eventFuture, new FutureCallback<Object>() {
+            @Override
+            public void onFailure(Throwable error) {
+                LOG.warn("Error in notifying listeners for add dpn {} in vpn {} event ", dpnId, vpnName, error);
             }
 
-            //increment the vpn interface count in Vpn Instance Op Data
-            //Long ifCnt = 0L;
-            VpnInstanceOpDataEntry vpnInstOp = null;
-//            InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to
-//                    .vpn.id.VpnInstance>
-//                    updId = VpnUtil.getVpnInstanceToVpnIdIdentifier(update.getVpnInstanceName());
-//            Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
-//                    .VpnInstance> updVpnInstance
-//                    = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, updId);
-//
-//            if (updVpnInstance.isPresent()) {
-//                String rd = null;
-//                rd = updVpnInstance.get().getVrfId();
-//
-//                vpnInstOp = VpnUtil.getVpnInstanceOpData(broker, rd);
-//
-//                if (vpnInstOp != null && vpnInstOp.getVpnInterfaceCount() != null) {
-//                    ifCnt = vpnInstOp.getVpnInterfaceCount();
-//                }
-//
-//                LOG.trace("VpnInterfaceOpListener update: interface name {} rd {} interface count in updated Vpn Op
-// Instance {}", interfaceName, rd, ifCnt);
-//
-//                VpnUtil.asyncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
-//                        VpnUtil.getVpnInstanceOpDataIdentifier(rd),
-//                        VpnUtil.updateIntfCntInVpnInstOpData(ifCnt + 1, rd), VpnUtil.DEFAULT_CALLBACK);
-//            }
-
-            InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to
-                    .vpn.id.VpnInstance>
-                    origId = VpnUtil.getVpnInstanceToVpnIdIdentifier(original.getVpnInstanceName());
-            Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.to.vpn.id
-                    .VpnInstance> origVpnInstance
-                    = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, origId);
-
-            if (origVpnInstance.isPresent()) {
-                String rd = null;
-                rd = origVpnInstance.get().getVrfId();
-
-                vpnInstOp = VpnUtil.getVpnInstanceOpData(broker, rd);
-                LOG.trace("VpnInterfaceOpListener updated: interface name {} original rd {} original vpnName {}",
-                        interfaceName, rd, original.getVpnInstanceName());
-
-                if (vpnInstOp != null) {
-                    Adjacency adjacency = original.getAugmentation(Adjacencies.class).getAdjacency().get(0);
-                    Optional<Prefixes> prefixToInterface = Optional.absent();
-                    prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                            VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                    VpnUtil.getIpPrefix(adjacency.getIpAddress())));
-                    if (!prefixToInterface.isPresent()) {
-                        prefixToInterface = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                                VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-                                        VpnUtil.getIpPrefix(adjacency.getNextHopIp())));
-                    }
-                    if (prefixToInterface.isPresent()) {
-//                        VpnUtil.delete(broker, LogicalDatastoreType.OPERATIONAL,
-//                                VpnUtil.getPrefixToInterfaceIdentifier(vpnInstOp.getVpnId(),
-//                                        prefixToInterface.get().getIpAddress()),
-//                                VpnUtil.DEFAULT_CALLBACK);
-                        synchronized (interfaceName.intern()) {
-                            updateDpnDbs(prefixToInterface.get().getDpnId(), original.getVpnInstanceName(), interfaceName, false);
-                        }
-                    }
-                }
+            @Override
+            public void onSuccess(Object arg) {
+                LOG.trace("Successful in notifying listeners for add dpn {} in vpn {} event ", dpnId, vpnName);
             }
-            notifyTaskIfRequired(interfaceName);
-//                if (vpnInstOp != null && vpnInstOp.getVpnInterfaceCount() != null) {
-//                    ifCnt = vpnInstOp.getVpnInterfaceCount();
-//                } else {
-//                    LOG.debug("VpnInterfaceOpListener update: Vpn interface count not recoverable from original, to handle update for rd {}", rd);
-//                    return;
-//                }
-//                LOG.trace("VpnInterfaceOpListener update: interface name {} rd {} interface count in original Vpn Op Instance {}", interfaceName, rd, ifCnt);
-//
-//                if (ifCnt > 0) {
-//                    VpnUtil.asyncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
-//                            VpnUtil.getVpnInstanceOpDataIdentifier(rd),
-//                            VpnUtil.updateIntfCntInVpnInstOpData(ifCnt - 1, rd), VpnUtil.DEFAULT_CALLBACK);
-//                }
-//            }
-        }
-
-        @Override
-        protected void add(InstanceIdentifier<VpnInterface> identifier, VpnInterface add) {
-            final VpnInterfaceKey key = identifier.firstKeyOf(VpnInterface.class, VpnInterfaceKey.class);
-            String interfaceName = key.getName();
-
-            //increment the vpn interface count in Vpn Instance Op Data
-//            Long ifCnt = 0L;
-//            String rd = getRouteDistinguisher(add.getVpnInstanceName());
-//            if(rd == null || rd.isEmpty()) rd = add.getVpnInstanceName();
-//            VpnInstanceOpDataEntry vpnInstOp = VpnUtil.getVpnInstanceOpData(broker, rd);
-//            if(vpnInstOp != null &&  vpnInstOp.getVpnInterfaceCount() != null) {
-//                ifCnt = vpnInstOp.getVpnInterfaceCount();
-//            }
-//
-//            LOG.trace("VpnInterfaceOpListener add: interface name {} rd {} interface count in Vpn Op Instance {}",
-//                    interfaceName, rd, ifCnt);
-//
-//            VpnUtil.asyncUpdate(broker, LogicalDatastoreType.OPERATIONAL,
-//                    VpnUtil.getVpnInstanceOpDataIdentifier(rd),
-//                    VpnUtil.updateIntfCntInVpnInstOpData(ifCnt + 1, rd), VpnUtil.DEFAULT_CALLBACK);
-
-        }
+        });
     }
 
-    protected void updatePrefixesForDPN(BigInteger dpnId, UpdateRouteAction action) {
-
-        LOG.info("Tunnel event triggered {} for Dpn:{} ", action.name(), dpnId);
-        InstanceIdentifierBuilder<VpnInstances> idBuilder = InstanceIdentifier.builder(VpnInstances.class);
-        InstanceIdentifier<VpnInstances> vpnInstancesId = idBuilder.build();
-        Optional<VpnInstances> vpnInstances = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, vpnInstancesId);
-
-        if (vpnInstances.isPresent()) {
-            List<VpnInstance> vpnInstanceList = vpnInstances.get().getVpnInstance();
-            Iterator<VpnInstance> vpnInstIter = vpnInstanceList.iterator();
-            while (vpnInstIter.hasNext()) {
-                VpnInstance vpnInstance = vpnInstIter.next();
-                try {
-                    VpnAfConfig vpnConfig = vpnInstance.getIpv4Family();
-                    String rd = vpnConfig.getRouteDistinguisher();
-                    if (rd == null || rd.isEmpty()) {
-                        rd = vpnInstance.getVpnInstanceName();
-                    }
-                    InstanceIdentifier<VpnToDpnList> id =
-                        VpnUtil.getVpnToDpnListIdentifier(rd, dpnId);
-                    Optional<VpnToDpnList> dpnInVpn =
-                        VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, id);
-                    if (dpnInVpn.isPresent()) {
-                        // if (action == UpdateRouteAction.ADVERTISE_ROUTE) {
-                        //    fibManager.populateFibOnNewDpn(dpnId, VpnUtil
-                        //        .getVpnId(broker, vpnInstance.getVpnInstanceName()), rd);
-                        // }
-                        List<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
-                            .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces>
-                            vpnInterfaces = dpnInVpn.get().getVpnInterfaces();
-                        for (org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data
-                            .vpn.instance.op.data.entry.vpn.to.dpn.list.VpnInterfaces vpnInterface : vpnInterfaces) {
-                            InstanceIdentifier<VpnInterface> vpnIntfId =
-                                VpnUtil.getVpnInterfaceIdentifier(vpnInterface.getInterfaceName());
-                            InstanceIdentifier<Adjacencies> path =
-                                vpnIntfId.augmentation(Adjacencies.class);
-                            Optional<Adjacencies> adjacencies =
-                                VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL, path);
-
-                            if (adjacencies.isPresent()) {
-                                List<Adjacency> adjacencyList = adjacencies.get().getAdjacency();
-                                Iterator<Adjacency> adjacencyIterator = adjacencyList.iterator();
-
-                                while (adjacencyIterator.hasNext()) {
-                                    Adjacency adjacency = adjacencyIterator.next();
-                                    try {
-                                        if (action == UpdateRouteAction.ADVERTISE_ROUTE) {
-                                            //FIXME: TBD once odl-fib yang has nexthoplist and related changes follow
-                                            //bgpManager.addPrefix(rd, adjacency.getIpAddress(), adjacency.getNextHopIp(), adjacency.getLabel().intValue());
-
-                                        }
-                                        else if (action == UpdateRouteAction.WITHDRAW_ROUTE)
-                                            bgpManager.deletePrefix(rd, adjacency.getIpAddress());
-                                    } catch (Exception e) {
-                                        LOG.error("Exception when updating prefix {} in vrf {} to BGP",
-                                            adjacency.getIpAddress(), rd);
-                                    }
-                                }
-                            }
+    void publishRemoveNotification(final BigInteger dpnId, final String vpnName, final String rd) {
+        LOG.debug("Sending notification for remove dpn {} in vpn {} event ", dpnId, vpnName);
+        RemoveEventData data = new RemoveEventDataBuilder().setVpnName(vpnName).setRd(rd).setDpnId(dpnId).build();
+        RemoveDpnEvent event = new RemoveDpnEventBuilder().setRemoveEventData(data).build();
+        final ListenableFuture<? extends Object> eventFuture = notificationPublishService.offerNotification(event);
+        Futures.addCallback(eventFuture, new FutureCallback<Object>() {
+            @Override
+            public void onFailure(Throwable error) {
+                LOG.warn("Error in notifying listeners for remove dpn {} in vpn {} event ", dpnId, vpnName, error);
+            }
 
-                        }
-                        // if (action == UpdateRouteAction.WITHDRAW_ROUTE) {
-                        //    fibManager.cleanUpDpnForVpn(dpnId, VpnUtil.getVpnId(broker, vpnInstance.getVpnInstanceName()), rd);
-                        // }
-                    }
-                } catch (Exception e) {
-                    LOG.error("updatePrefixesForDPN {} in vpn {} failed", dpnId, vpnInstance.getVpnInstanceName(), e);
-                }
+            @Override
+            public void onSuccess(Object arg) {
+                LOG.trace("Successful in notifying listeners for remove dpn {} in vpn {} event ", dpnId, vpnName);
             }
-        }
+        });
     }
 
-       InstanceIdentifier<DpnVpninterfacesList> getRouterDpnId(String routerName, BigInteger dpnId) {
+    InstanceIdentifier<DpnVpninterfacesList> getRouterDpnId(String routerName, BigInteger dpnId) {
         return InstanceIdentifier.builder(NeutronRouterDpns.class)
-            .child(RouterDpnList.class, new RouterDpnListKey(routerName))
-            .child(DpnVpninterfacesList.class, new DpnVpninterfacesListKey(dpnId)).build();
+                .child(RouterDpnList.class, new RouterDpnListKey(routerName))
+                .child(DpnVpninterfacesList.class, new DpnVpninterfacesListKey(dpnId)).build();
     }
 
     InstanceIdentifier<RouterDpnList> getRouterId(String routerName) {
         return InstanceIdentifier.builder(NeutronRouterDpns.class)
-            .child(RouterDpnList.class, new RouterDpnListKey(routerName)).build();
+                .child(RouterDpnList.class, new RouterDpnListKey(routerName)).build();
     }
 
-    protected void addToNeutronRouterDpnsMap(String routerName, String vpnInterfaceName) {
-        BigInteger dpId = InterfaceUtils.getDpnForInterface(interfaceManager, vpnInterfaceName);
+    protected void addToNeutronRouterDpnsMap(String routerName, String vpnInterfaceName, WriteTransaction writeOperTxn) {
+        BigInteger dpId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, vpnInterfaceName);
         if(dpId.equals(BigInteger.ZERO)) {
             LOG.warn("Could not retrieve dp id for interface {} to handle router {} association model", vpnInterfaceName, routerName);
             return;
         }
         InstanceIdentifier<DpnVpninterfacesList> routerDpnListIdentifier = getRouterDpnId(routerName, dpId);
 
-        Optional<DpnVpninterfacesList> optionalRouterDpnList = VpnUtil.read(broker, LogicalDatastoreType
+        Optional<DpnVpninterfacesList> optionalRouterDpnList = VpnUtil.read(dataBroker, LogicalDatastoreType
                 .OPERATIONAL, routerDpnListIdentifier);
         RouterInterfaces routerInterface = new RouterInterfacesBuilder().setKey(new RouterInterfacesKey(vpnInterfaceName)).setInterface(vpnInterfaceName).build();
         if (optionalRouterDpnList.isPresent()) {
-            MDSALUtil.syncWrite(broker,  LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
-                    RouterInterfaces.class,  new RouterInterfacesKey(vpnInterfaceName)), routerInterface);
+            writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
+                    RouterInterfaces.class, new RouterInterfacesKey(vpnInterfaceName)), routerInterface, true);
         } else {
-            MDSALUtil.syncUpdate(broker,  LogicalDatastoreType.OPERATIONAL,
-                    getRouterId(routerName),
-                    new RouterDpnListBuilder().setRouterId(routerName).build());
-            //VpnToDpnListBuilder vpnToDpnList = new VpnToDpnListBuilder().setDpnId(dpnId);
+            RouterDpnListBuilder builder = new RouterDpnListBuilder();
+            builder.setRouterId(routerName);
             DpnVpninterfacesListBuilder dpnVpnList = new DpnVpninterfacesListBuilder().setDpnId(dpId);
             List<RouterInterfaces> routerInterfaces =  new ArrayList<>();
             routerInterfaces.add(routerInterface);
-            MDSALUtil.syncWrite(broker,  LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier,
-                    dpnVpnList.setRouterInterfaces(routerInterfaces).build());
+            builder.setDpnVpninterfacesList(Arrays.asList(dpnVpnList.build()));
+            writeOperTxn.merge(LogicalDatastoreType.OPERATIONAL,
+                    getRouterId(routerName),
+                    builder.build(), true);
         }
     }
 
-    protected void removeFromNeutronRouterDpnsMap(String routerName, String vpnInterfaceName) {
-        BigInteger dpId = InterfaceUtils.getDpnForInterface(interfaceManager, vpnInterfaceName);
+    protected void removeFromNeutronRouterDpnsMap(String routerName, String vpnInterfaceName, WriteTransaction writeOperTxn) {
+        BigInteger dpId = InterfaceUtils.getDpnForInterface(ifaceMgrRpcService, vpnInterfaceName);
         if(dpId.equals(BigInteger.ZERO)) {
             LOG.warn("Could not retrieve dp id for interface {} to handle router {} dissociation model", vpnInterfaceName, routerName);
             return;
         }
         InstanceIdentifier<DpnVpninterfacesList> routerDpnListIdentifier = getRouterDpnId(routerName, dpId);
-        Optional<DpnVpninterfacesList> optionalRouterDpnList = VpnUtil.read(broker, LogicalDatastoreType
+        Optional<DpnVpninterfacesList> optionalRouterDpnList = VpnUtil.read(dataBroker, LogicalDatastoreType
                 .OPERATIONAL, routerDpnListIdentifier);
         if (optionalRouterDpnList.isPresent()) {
             List<RouterInterfaces> routerInterfaces = optionalRouterDpnList.get().getRouterInterfaces();
@@ -1233,33 +1683,43 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
 
             if (routerInterfaces != null && routerInterfaces.remove(routerInterface)) {
                 if (routerInterfaces.isEmpty()) {
-                    MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
+                    if (writeOperTxn != null) {
+                        writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
+                    } else {
+                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
+                    }
                 } else {
-                    MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
-                            RouterInterfaces.class,
-                            new RouterInterfacesKey(vpnInterfaceName)));
+                    if (writeOperTxn != null) {
+                        writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
+                                RouterInterfaces.class,
+                                new RouterInterfacesKey(vpnInterfaceName)));
+                    } else {
+                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
+                                RouterInterfaces.class,
+                                new RouterInterfacesKey(vpnInterfaceName)));
+                    }
                 }
             }
         }
     }
-       
-       protected void removeFromNeutronRouterDpnsMap(String routerName, String vpnInterfaceName,BigInteger dpId) {
+
+    protected void removeFromNeutronRouterDpnsMap(String routerName, String vpnInterfaceName, BigInteger dpId,
+                                                  WriteTransaction writeOperTxn) {
         if(dpId.equals(BigInteger.ZERO)) {
             LOG.warn("Could not retrieve dp id for interface {} to handle router {} dissociation model", vpnInterfaceName, routerName);
             return;
         }
         InstanceIdentifier<DpnVpninterfacesList> routerDpnListIdentifier = getRouterDpnId(routerName, dpId);
-        Optional<DpnVpninterfacesList> optionalRouterDpnList = VpnUtil.read(broker, LogicalDatastoreType
+        Optional<DpnVpninterfacesList> optionalRouterDpnList = VpnUtil.read(dataBroker, LogicalDatastoreType
                 .OPERATIONAL, routerDpnListIdentifier);
         if (optionalRouterDpnList.isPresent()) {
             List<RouterInterfaces> routerInterfaces = optionalRouterDpnList.get().getRouterInterfaces();
             RouterInterfaces routerInterface = new RouterInterfacesBuilder().setKey(new RouterInterfacesKey(vpnInterfaceName)).setInterface(vpnInterfaceName).build();
-
             if (routerInterfaces != null && routerInterfaces.remove(routerInterface)) {
                 if (routerInterfaces.isEmpty()) {
-                    MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
+                    writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier);
                 } else {
-                    MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
+                    writeOperTxn.delete(LogicalDatastoreType.OPERATIONAL, routerDpnListIdentifier.child(
                             RouterInterfaces.class,
                             new RouterInterfacesKey(vpnInterfaceName)));
                 }
@@ -1267,4 +1727,11 @@ public class VpnInterfaceManager extends AbstractDataChangeListener<VpnInterface
         }
     }
 
+    void notifyTaskIfRequired(String intfName) {
+        Runnable notifyTask = vpnIntfMap.remove(intfName);
+        if (notifyTask == null) {
+            return;
+        }
+        executorService.execute(notifyTask);
+    }
 }