Lower log levels for non error's
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / VpnSubnetRouteHandler.java
index 33fa34ce29405df3557523714f9ce06818571d2f..1df2f5d14e0697a351366e5df9e15f890caffcdc 100644 (file)
  */
 package org.opendaylight.netvirt.vpnmanager;
 
-import java.util.*;
-
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-
-import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.mdsalutil.MDSALUtil;
+import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
+import org.opendaylight.netvirt.fibmanager.api.RouteOrigin;
+import org.opendaylight.netvirt.vpnmanager.VpnOpDataSyncer.VpnOpDataType;
+import org.opendaylight.netvirt.vpnmanager.populator.input.L3vpnInput;
+import org.opendaylight.netvirt.vpnmanager.populator.intfc.VpnPopulator;
+import org.opendaylight.netvirt.vpnmanager.populator.registry.L3vpnRegistry;
 import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface.OperStatus;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.*;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.PortOpData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.SubnetOpData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.TaskState;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntryKey;
+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.subnet.op.data.SubnetOpDataEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.*;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.SubnetOpDataEntryKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.SubnetToDpn;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.subnet.op.data.subnet.op.data.entry.subnet.to.dpn.VpnInterfaces;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.Networks;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.external.networks.NetworksKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.natservice.rev160111.ExternalNetworks;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.Subnetmaps;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.SubnetmapKey;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-import java.math.BigInteger;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Optional;
-
-
-public class VpnSubnetRouteHandler implements NeutronvpnListener {
-    private static final Logger logger = LoggerFactory.getLogger(VpnSubnetRouteHandler.class);
-
-    private final DataBroker broker;
-    private SubnetOpDpnManager subOpDpnManager;
+public class VpnSubnetRouteHandler {
+    private static final Logger LOG = LoggerFactory.getLogger(VpnSubnetRouteHandler.class);
+    private static final String LOGGING_PREFIX = "SUBNETROUTE:";
+    private final DataBroker dataBroker;
+    private final SubnetOpDpnManager subOpDpnManager;
     private final IBgpManager bgpManager;
-    private IdManagerService idManager;
-    private VpnInterfaceManager vpnInterfaceManager;
-
-    public VpnSubnetRouteHandler(final DataBroker db, IBgpManager bgpManager, VpnInterfaceManager vpnIntfManager) {
-        broker = db;
-        subOpDpnManager = new SubnetOpDpnManager(broker);
+    private final VpnInterfaceManager vpnInterfaceManager;
+    private final IdManagerService idManager;
+    private LockManagerService lockManager;
+    private final VpnOpDataSyncer vpnOpDataSyncer;
+    private final VpnNodeListener vpnNodeListener;
+
+    public VpnSubnetRouteHandler(final DataBroker dataBroker, final SubnetOpDpnManager subnetOpDpnManager,
+        final IBgpManager bgpManager, final VpnInterfaceManager vpnIntfManager, final IdManagerService idManager,
+        LockManagerService lockManagerService, final VpnOpDataSyncer vpnOpDataSyncer,
+        final VpnNodeListener vpnNodeListener) {
+        this.dataBroker = dataBroker;
+        this.subOpDpnManager = subnetOpDpnManager;
         this.bgpManager = bgpManager;
         this.vpnInterfaceManager = vpnIntfManager;
-    }
-
-    public void setIdManager(IdManagerService idManager) {
         this.idManager = idManager;
+        this.lockManager = lockManagerService;
+        this.vpnOpDataSyncer = vpnOpDataSyncer;
+        this.vpnNodeListener = vpnNodeListener;
     }
 
-    @Override
-    public void onSubnetAddedToVpn(SubnetAddedToVpn notification) {
-        if (!notification.isExternalVpn()) {
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void onSubnetAddedToVpn(Subnetmap subnetmap, boolean isBgpVpn, Long elanTag) {
+        Uuid subnetId = subnetmap.getId();
+        String subnetIp = subnetmap.getSubnetIp();
+        Subnetmap subMap = null;
+        SubnetOpDataEntry subOpEntry = null;
+        SubnetOpDataEntryBuilder subOpBuilder = null;
+        InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = null;
+        Optional<SubnetOpDataEntry> optionalSubs = null;
+
+        Preconditions.checkNotNull(subnetId, LOGGING_PREFIX + " onSubnetAddedToVpn: SubnetId cannot be null or empty!");
+        Preconditions.checkNotNull(subnetIp,
+                LOGGING_PREFIX + " onSubnetAddedToVpn: SubnetPrefix cannot be null or empty!");
+        Preconditions.checkNotNull(elanTag, LOGGING_PREFIX + " onSubnetAddedToVpn: ElanTag cannot be null or empty!");
+
+        String vpnName;
+        if (subnetmap.getVpnId() != null) {
+            vpnName = subnetmap.getVpnId().getValue();
+            long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
+            if (vpnId == VpnConstants.INVALID_ID) {
+                vpnOpDataSyncer.waitForVpnDataReady(VpnOpDataType.vpnInstanceToId, vpnName,
+                        VpnConstants.PER_VPN_INSTANCE_MAX_WAIT_TIME_IN_MILLISECONDS);
+                vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
+                if (vpnId == VpnConstants.INVALID_ID) {
+                    LOG.error("{} onSubnetAddedToVpn: VpnInstance to VPNId mapping not yet available for VpnName {} "
+                              + "processing subnet {} with IP {}, bailing out now.", LOGGING_PREFIX, vpnName, subnetId,
+                            subnetIp);
+                    return;
+                }
+            }
+        } else {
+            LOG.error("onSubnetAddedToVpn: VpnId {} for subnet {} not found, bailing out", subnetmap.getVpnId(),
+                      subnetId);
             return;
         }
+        LOG.info("{} onSubnetAddedToVpn: Subnet {} with IP {}being added to vpn {}", LOGGING_PREFIX,
+                subnetId.getValue(), subnetIp, vpnName);
 
-        Uuid subnetId = notification.getSubnetId();
-        String vpnName = notification.getVpnName();
-        String subnetIp = notification.getSubnetIp();
-        Long elanTag = notification.getElanTag();
-
-        Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
-        Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
-        Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
-        Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
-
-        logger.info("onSubnetAddedToVpn: Subnet " + subnetId.getValue() + " being added to vpn");
         //TODO(vivek): Change this to use more granularized lock at subnetId level
-        synchronized (this) {
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
             try {
-                Subnetmap subMap = null;
 
                 // Please check if subnetId belongs to an External Network
-                InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
-                        child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
-                Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
+                InstanceIdentifier<Subnetmap> subMapid =
+                    InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
+                        new SubnetmapKey(subnetId)).build();
+                Optional<Subnetmap> sm = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, subMapid);
                 if (!sm.isPresent()) {
-                    logger.error("onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet : " + subnetId);
+                    LOG.error("{} onSubnetAddedToVpn: Unable to retrieve subnetmap entry for subnet {} IP {}"
+                            + " vpnName {}",  LOGGING_PREFIX, subnetId, subnetIp, vpnName);
                     return;
                 }
                 subMap = sm.get();
-                InstanceIdentifier<Networks> netsIdentifier = InstanceIdentifier.builder(ExternalNetworks.class).
-                        child(Networks.class, new NetworksKey(subMap.getNetworkId())).build();
-                Optional<Networks> optionalNets = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, netsIdentifier);
-                if (optionalNets.isPresent()) {
-                    logger.info("onSubnetAddedToVpn: subnet {} is an external subnet on external network {}, so ignoring this for SubnetRoute",
-                            subnetId.getValue(), subMap.getNetworkId().getValue());
-                    return;
+
+                if (isBgpVpn) {
+                    InstanceIdentifier<Networks> netsIdentifier = InstanceIdentifier.builder(ExternalNetworks.class)
+                            .child(Networks.class, new NetworksKey(subMap.getNetworkId())).build();
+                    Optional<Networks> optionalNets = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION,
+                            netsIdentifier);
+                    if (optionalNets.isPresent()) {
+                        LOG.info("{} onSubnetAddedToVpn: subnet {} with IP {} is an external subnet on external "
+                                + "network {}, so ignoring this for SubnetRoute on vpn {}", LOGGING_PREFIX,
+                                subnetId.getValue(), subnetIp, subMap.getNetworkId().getValue(), vpnName);
+                        return;
+                    }
                 }
                 //Create and add SubnetOpDataEntry object for this subnet to the SubnetOpData container
-                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
-                        child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
-                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
-                        LogicalDatastoreType.OPERATIONAL,
-                        subOpIdentifier);
+                subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
+                optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
                 if (optionalSubs.isPresent()) {
-                    logger.error("onSubnetAddedToVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
-                            " already detected to be present");
+                    LOG.error("{} onSubnetAddedToVpn: SubnetOpDataEntry for subnet {} with ip {} and vpn {} already"
+                            + " detected to be present", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName);
                     return;
                 }
-                logger.debug("onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
-                Map<BigInteger, SubnetToDpn> subDpnMap = new HashMap<BigInteger, SubnetToDpn>();
-                SubnetOpDataEntry subOpEntry = null;
-                BigInteger dpnId = null;
-                BigInteger nhDpnId = null;
-                SubnetToDpn subDpn = null;
-
-                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
+                LOG.debug("{} onSubnetAddedToVpn: Creating new SubnetOpDataEntry node for subnet {} subnetIp {}"
+                        + "vpn {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName);
+                subOpBuilder = new SubnetOpDataEntryBuilder().setKey(new SubnetOpDataEntryKey(subnetId));
                 subOpBuilder.setSubnetId(subnetId);
                 subOpBuilder.setSubnetCidr(subnetIp);
-                String rd = VpnUtil.getVpnRdFromVpnInstanceConfig(broker, vpnName);
-                if (rd == null) {
-                    logger.error("onSubnetAddedToVpn: The VPN Instance name " + notification.getVpnName() + " does not have RD ");
+                String primaryRd = VpnUtil.getPrimaryRd(dataBroker, vpnName);
+
+                if (isBgpVpn && !VpnUtil.isBgpVpn(vpnName, primaryRd)) {
+                    LOG.error("{} onSubnetAddedToVpn: The VPN Instance name {} does not have RD. Bailing out for"
+                            + " subnet {} subnetIp {} ", LOGGING_PREFIX, vpnName, subnetId.getValue(), subnetIp);
                     return;
                 }
-                subOpBuilder.setVrfId(rd);
+
+                subOpBuilder.setVrfId(primaryRd);
                 subOpBuilder.setVpnName(vpnName);
-                subOpBuilder.setSubnetToDpn(new ArrayList<SubnetToDpn>());
-                subOpBuilder.setRouteAdvState(TaskState.Na);
+                subOpBuilder.setSubnetToDpn(new ArrayList<>());
+                subOpBuilder.setRouteAdvState(TaskState.Idle);
                 subOpBuilder.setElanTag(elanTag);
+                Long l3Vni = VpnUtil.getVpnInstanceOpData(dataBroker, primaryRd).getL3vni();
+                subOpBuilder.setL3vni(l3Vni);
+
+                subOpEntry = subOpBuilder.build();
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
+                LOG.info("onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet {}",
+                        subnetId.getValue());
+            } catch (Exception ex) {
+                LOG.error("Creation of SubnetOpDataEntry for subnet {} failed ", subnetId.getValue(), ex);
+            } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
+            }
+
+            //In second critical section , Port-Op-Data will be updated.
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
+            try {
+                BigInteger dpnId = null;
+                SubnetToDpn subDpn = null;
+                Map<BigInteger, SubnetToDpn> subDpnMap = new HashMap<BigInteger, SubnetToDpn>();
 
-                // First recover set of ports available in this subnet
+                optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
+                subOpBuilder =
+                        new SubnetOpDataEntryBuilder(optionalSubs.get()).setKey(new SubnetOpDataEntryKey(subnetId));
                 List<Uuid> portList = subMap.getPortList();
                 if (portList != null) {
-                    for (Uuid port: portList) {
-                        Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,port.getValue());
+                    for (Uuid port : portList) {
+                        Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker,port.getValue());
                         if (intfState != null) {
-                            dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
-                            if (dpnId == null) {
-                                logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not assigned DPN yet, ignoring ");
+                            try {
+                                dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
+                            } catch (Exception e) {
+                                LOG.error("{} onSubnetAddedToVpn: Unable to obtain dpnId for interface {},"
+                                        + " subnetroute inclusion for this interface for subnet {} subnetIp {} vpn {}"
+                                        + " failed with exception {}", LOGGING_PREFIX, port.getValue(),
+                                        subnetId.getValue(), subnetIp, vpnName, e);
+                                continue;
+                            }
+                            if (dpnId.equals(BigInteger.ZERO)) {
+                                LOG.error("{} onSubnetAddedToVpn: Port {} is not assigned DPN yet,"
+                                        + " ignoring subnet {} subnetIP {} vpn {}", LOGGING_PREFIX, port.getValue(),
+                                        subnetId.getValue(), subnetIp, vpnName);
                                 continue;
                             }
                             subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, dpnId);
                             if (intfState.getOperStatus() != OperStatus.Up) {
-                                logger.info("onSubnetAddedToVpn: Port " + port.getValue() + " is not UP yet, ignoring ");
+                                LOG.error("{} onSubnetAddedToVpn: Port {} is not UP yet, ignoring subnet {}"
+                                        + " subnetIp {} vpn {}", LOGGING_PREFIX, port.getValue(), subnetId.getValue(),
+                                        subnetIp, vpnName);
                                 continue;
                             }
                             subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, port.getValue());
                             if (intfState.getOperStatus() == OperStatus.Up) {
                                 // port is UP
                                 subDpnMap.put(dpnId, subDpn);
-                                if (nhDpnId == null) {
-                                    nhDpnId = dpnId;
-                                }
                             }
                         } else {
                             subOpDpnManager.addPortOpDataEntry(port.getValue(), subnetId, null);
                         }
                     }
                     if (subDpnMap.size() > 0) {
-                        subOpBuilder.setSubnetToDpn(new ArrayList<SubnetToDpn>(subDpnMap.values()));
+                        subOpBuilder.setSubnetToDpn(new ArrayList<>(subDpnMap.values()));
                     }
                 }
-
-                if (nhDpnId != null) {
-                    logger.info("Next-Hop dpn {} is available for rd {} subnetIp {} vpn {}", nhDpnId, rd, subnetIp, vpnName);
-                    subOpBuilder.setNhDpnId(nhDpnId);
-                    try {
-                        /*
-                        Write the subnet route entry to the FIB.
-                        And also advertise the subnet route entry via BGP.
-                        */
-                        int label = getLabel(rd, subnetIp);
-                        addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                        advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                        subOpBuilder.setRouteAdvState(TaskState.Done);
-                    } catch (Exception ex) {
-                        logger.error("onSubnetAddedToVpn: FIB rules and Advertising nhDpnId " + nhDpnId +
-                                " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
-                        subOpBuilder.setRouteAdvState(TaskState.Pending);
-                    }
-                }else{
-                    logger.info("Next-Hop dpn is unavailable for rd {} subnetIp {} vpn {}", rd, subnetIp, vpnName);
-                }
-
+                electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
+                        subMap.getNetworkId(), isBgpVpn);
                 subOpEntry = subOpBuilder.build();
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
-                logger.info("onSubnetAddedToVpn: Added subnetopdataentry to OP Datastore for subnet " + subnetId.getValue());
+                MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
+                LOG.info("{} onSubnetAddedToVpn: Added PortOpDataEntry and VpnInterfaces to SubnetOpData"
+                                + " for subnet {} subnetIp {} vpn {} TaskState {} lastTaskState {}", LOGGING_PREFIX,
+                        subnetId.getValue(), subnetIp, vpnName, subOpEntry.getRouteAdvState(),
+                        subOpEntry.getLastAdvState());
             } catch (Exception ex) {
-                logger.error("Creation of SubnetOpDataEntry for subnet " +
-                        subnetId.getValue() + " failed {}", ex);
+                LOG.error("{} onSubnetAddedToVpn: Creation of SubnetOpDataEntry for subnet {} subnetIp {} vpn {}"
+                        + " failed {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName, ex);
             } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
+        } catch (Exception e) {
+            LOG.error("{} onSubnetAddedToVpn: Unable to handle subnet {} with ip {} added to vpn {} {}", LOGGING_PREFIX,
+                    subnetId.getValue(), subnetIp, vpnName, e);
         }
     }
 
-    @Override
-    public void onSubnetDeletedFromVpn(SubnetDeletedFromVpn notification) {
-        Uuid subnetId = notification.getSubnetId();
-
-        if (!notification.isExternalVpn()) {
-            return;
-        }
-        logger.info("onSubnetDeletedFromVpn: Subnet " + subnetId.getValue() + " being removed from vpn");
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void onSubnetDeletedFromVpn(Subnetmap subnetmap, boolean isBgpVpn) {
+        Uuid subnetId = subnetmap.getId();
+        LOG.info("{} onSubnetDeletedFromVpn: Subnet {} with ip {} being removed from vpnId {}", LOGGING_PREFIX,
+                subnetId, subnetmap.getSubnetIp(), subnetmap.getVpnId());
         //TODO(vivek): Change this to use more granularized lock at subnetId level
-        synchronized (this) {
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
             try {
-                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
-                    child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
-                logger.trace(" Removing the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
-                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
+                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+                    InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
+                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
                         LogicalDatastoreType.OPERATIONAL,
                         subOpIdentifier);
                 if (!optionalSubs.isPresent()) {
-                    logger.error("onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet " + subnetId.getValue() +
-                            " not available in datastore");
+                    LOG.error("{} onSubnetDeletedFromVpn: SubnetOpDataEntry for subnet {} subnetIp {} vpn {}"
+                            + " not available in datastore", LOGGING_PREFIX, subnetId.getValue(),
+                            subnetId.getValue(), subnetmap.getVpnId());
                     return;
                 }
-
+                LOG.trace("{} onSubnetDeletedFromVpn: Removing the SubnetOpDataEntry node for subnet {} subnetIp {}"
+                        + " vpnName {} rd {} TaskState {}", LOGGING_PREFIX, subnetId.getValue(),
+                        optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
+                        optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState());
                 /* If subnet is deleted (or if its removed from VPN), the ports that are DOWN on that subnet
                  * will continue to be stale in portOpData DS, as subDpnList used for portOpData removal will
                  * contain only ports that are UP. So here we explicitly cleanup the ports of the subnet by
                  * going through the list of ports on the subnet
                  */
-                InstanceIdentifier<Subnetmap> subMapid = InstanceIdentifier.builder(Subnetmaps.class).
-                        child(Subnetmap.class, new SubnetmapKey(subnetId)).build();
-                Optional<Subnetmap> sm = VpnUtil.read(broker, LogicalDatastoreType.CONFIGURATION, subMapid);
+                InstanceIdentifier<Subnetmap> subMapid =
+                    InstanceIdentifier.builder(Subnetmaps.class).child(Subnetmap.class,
+                        new SubnetmapKey(subnetId)).build();
+                Optional<Subnetmap> sm = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, subMapid);
                 if (!sm.isPresent()) {
-                    logger.error("Stale ports removal: Unable to retrieve subnetmap entry for subnet : " + subnetId);
+                    LOG.error("{} onSubnetDeletedFromVpn: Stale ports removal: Unable to retrieve subnetmap entry"
+                            + " for subnet {} subnetIp {} vpnName {}", LOGGING_PREFIX, subnetId.getValue(),
+                            optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName());
                 } else {
                     Subnetmap subMap = sm.get();
                     List<Uuid> portList = subMap.getPortList();
                     if (portList != null) {
                         for (Uuid port : portList) {
-                            InstanceIdentifier<PortOpDataEntry> portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).
-                                    child(PortOpDataEntry.class, new PortOpDataEntryKey(port.getValue())).build();
-                            logger.trace("Deleting portOpData entry for port " + port.getValue());
-                            MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
+                            InstanceIdentifier<PortOpDataEntry> portOpIdentifier =
+                                InstanceIdentifier.builder(PortOpData.class).child(PortOpDataEntry.class,
+                                    new PortOpDataEntryKey(port.getValue())).build();
+                            LOG.trace("{} onSubnetDeletedFromVpn: Deleting portOpData entry for port {}"
+                                    + " from subnet {} subnetIp {} vpnName {} TaskState()",
+                                    LOGGING_PREFIX, port.getValue(), subnetId.getValue(),
+                                    optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
+                                    optionalSubs.get().getRouteAdvState());
+                            MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
                         }
                     }
                 }
@@ -250,145 +319,160 @@ public class VpnSubnetRouteHandler implements NeutronvpnListener {
                 String rd = subOpBuilder.getVrfId();
                 String subnetIp = subOpBuilder.getSubnetCidr();
                 String vpnName = subOpBuilder.getVpnName();
-                MDSALUtil.syncDelete(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
-                logger.info("onSubnetDeletedFromVpn: Removed subnetopdataentry for subnet {} successfully from Datastore", subnetId.getValue());
-                try {
-                    //Withdraw the routes for all the interfaces on this subnet
-                    //Remove subnet route entry from FIB
-                    deleteSubnetRouteFromFib(rd, subnetIp, vpnName);
-                    withdrawSubnetRoutefromBgp(rd, subnetIp);
-                } catch (Exception ex) {
-                    logger.error("onSubnetAddedToVpn: Withdrawing routes from BGP for subnet " +
-                            subnetId.getValue() + " failed {}" + ex);
-                }
+                //Withdraw the routes for all the interfaces on this subnet
+                //Remove subnet route entry from FIB
+                deleteSubnetRouteFromFib(rd, subnetIp, vpnName, isBgpVpn);
+                MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
+                LOG.info("{} onSubnetDeletedFromVpn: Removed subnetopdataentry successfully from Datastore"
+                        + " for subnet {} subnetIp {} vpnName {} rd {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp,
+                        vpnName, rd);
             } catch (Exception ex) {
-                logger.error("Removal of SubnetOpDataEntry for subnet " +
-                        subnetId.getValue() + " failed {}" + ex);
+                LOG.error("{} onSubnetDeletedFromVpn: Removal of SubnetOpDataEntry for subnet {} subnetIp {}"
+                        + " vpnId {} failed {}", LOGGING_PREFIX, subnetId.getValue(), subnetmap.getSubnetIp(),
+                        subnetmap.getVpnId(), ex);
             } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
+        } catch (Exception e) {
+            LOG.error("{} onSubnetDeletedFromVpn: Unable to handle subnet {} with Ip {} removed from vpn {} {}",
+                    LOGGING_PREFIX, subnetId.getValue(), subnetmap.getSubnetIp(), subnetmap.getVpnId(), e);
         }
     }
 
-    @Override
-    public void onSubnetUpdatedInVpn(SubnetUpdatedInVpn notification) {
-        Uuid subnetId = notification.getSubnetId();
-        String vpnName = notification.getVpnName();
-        String subnetIp = notification.getSubnetIp();
-        Long elanTag = notification.getElanTag();
-
-        Preconditions.checkNotNull(subnetId, "SubnetId cannot be null or empty!");
-        Preconditions.checkNotNull(subnetIp, "SubnetPrefix cannot be null or empty!");
-        Preconditions.checkNotNull(vpnName, "VpnName cannot be null or empty!");
-        Preconditions.checkNotNull(elanTag, "ElanTag cannot be null or empty!");
-
-        InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
-                child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
-        Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
-                LogicalDatastoreType.OPERATIONAL,
-                subOpIdentifier);
+    public void onSubnetUpdatedInVpn(Subnetmap subnetmap, Long elanTag) {
+        Uuid subnetId = subnetmap.getId();
+        String vpnName = subnetmap.getVpnId().getValue();
+        String subnetIp = subnetmap.getSubnetIp();
+
+        Preconditions.checkNotNull(subnetId,
+                LOGGING_PREFIX + " onSubnetUpdatedInVpn: SubnetId cannot be null or empty!");
+        Preconditions.checkNotNull(subnetIp,
+                LOGGING_PREFIX + " onSubnetUpdatedInVpn: SubnetPrefix cannot be null or empty!");
+        Preconditions.checkNotNull(vpnName, LOGGING_PREFIX + " onSubnetUpdatedInVpn: VpnName cannot be null or empty!");
+        Preconditions.checkNotNull(elanTag, LOGGING_PREFIX + " onSubnetUpdatedInVpn: ElanTag cannot be null or empty!");
+
+        InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+            InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                new SubnetOpDataEntryKey(subnetId)).build();
+        Optional<SubnetOpDataEntry> optionalSubs =
+            VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier);
         if (optionalSubs.isPresent()) {
-            if (!notification.isExternalVpn()) {
-                SubnetDeletedFromVpnBuilder bldr = new SubnetDeletedFromVpnBuilder().setVpnName(vpnName);
-                bldr.setElanTag(elanTag).setExternalVpn(true).setSubnetIp(subnetIp).setSubnetId(subnetId);
-                onSubnetDeletedFromVpn(bldr.build());
-            }
-            // TODO(vivek): Something got updated, but we donot know what ?
+            onSubnetDeletedFromVpn(subnetmap, true);
         } else {
-            if (notification.isExternalVpn()) {
-                SubnetAddedToVpnBuilder bldr = new SubnetAddedToVpnBuilder().setVpnName(vpnName).setElanTag(elanTag);
-                bldr.setSubnetIp(subnetIp).setSubnetId(subnetId).setExternalVpn(true);;
-                onSubnetAddedToVpn(bldr.build());
-            }
-            // TODO(vivek): Something got updated, but we donot know what ?
+            onSubnetAddedToVpn(subnetmap, true, elanTag);
         }
+        LOG.info("{} onSubnetUpdatedInVpn: subnet {} with Ip {} updated successfully for vpn {}", LOGGING_PREFIX,
+                subnetId.getValue(), subnetIp, vpnName);
     }
 
-    @Override
-    public void onPortAddedToSubnet(PortAddedToSubnet notification) {
-        Uuid subnetId = notification.getSubnetId();
-        Uuid portId = notification.getPortId();
-
-        logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " being added to subnet " + subnetId.getValue());
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void onPortAddedToSubnet(Subnetmap subnetmap, Uuid portId) {
+        Uuid subnetId = subnetmap.getId();
+        LOG.info("{} onPortAddedToSubnet: Port {} being added to subnet {}", LOGGING_PREFIX, portId.getValue(),
+                subnetId.getValue());
         //TODO(vivek): Change this to use more granularized lock at subnetId level
-        synchronized (this) {
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
             try {
-                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
-                    child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
+                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+                    InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
 
-                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
+                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
                         subOpIdentifier);
                 if (!optionalSubs.isPresent()) {
-                    logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
-                            " that is not in VPN, ignoring");
+                    LOG.info("{} onPortAddedToSubnet: Port {} is part of a subnet {} that is not in VPN, ignoring",
+                            LOGGING_PREFIX, portId.getValue(), subnetId.getValue());
                     return;
                 }
-                Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(broker,portId.getValue());
+                String vpnName = optionalSubs.get().getVpnName();
+                String subnetIp = optionalSubs.get().getSubnetCidr();
+                String rd = optionalSubs.get().getVrfId();
+                String routeAdvState = optionalSubs.get().getRouteAdvState().toString();
+                LOG.info("{} onPortAddedToSubnet: Port {} being added to subnet {} subnetIp {} vpnName {} rd {} "
+                                + "TaskState {}", LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), subnetIp,
+                        vpnName, rd, routeAdvState);
+                subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null);
+                Interface intfState = InterfaceUtils.getInterfaceStateFromOperDS(dataBroker,portId.getValue());
                 if (intfState == null) {
                     // Interface State not yet available
-                    subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, null);
                     return;
                 }
-                BigInteger dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
-                if (dpnId == null) {
-                    logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not assigned DPN yet, ignoring ");
+                BigInteger dpnId = BigInteger.ZERO;
+                try {
+                    dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
+                } catch (Exception e) {
+                    LOG.error("{} onPortAddedToSubnet: Unable to obtain dpnId for interface {}. subnetroute inclusion"
+                                    + " for this interface failed for subnet {} subnetIp {} vpn {} rd {} with "
+                                    + "exception {}", LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), subnetIp,
+                            vpnName, rd, e);
+                    return;
+                }
+                if (dpnId.equals(BigInteger.ZERO)) {
+                    LOG.error("{} onPortAddedToSubnet: Port {} is not assigned DPN yet, ignoring subnetRoute "
+                                    + "inclusion for the interface into subnet {} subnetIp {} vpnName {} rd {}",
+                            LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), subnetIp, vpnName, rd);
                     return;
                 }
                 subOpDpnManager.addPortOpDataEntry(portId.getValue(), subnetId, dpnId);
                 if (intfState.getOperStatus() != OperStatus.Up) {
-                    logger.info("onPortAddedToSubnet: Port " + portId.getValue() + " is not UP yet, ignoring ");
+                    LOG.error("{} onPortAddedToSubnet: Port {} is not UP yet, ignoring subnetRoute inclusion for "
+                                    + "the interface into subnet {} subnetIp {} vpnName {} rd {}", LOGGING_PREFIX,
+                            portId.getValue(), subnetId.getValue(), subnetIp, vpnName, rd);
                     return;
                 }
-                logger.debug("onPortAddedToSubnet: Updating the SubnetOpDataEntry node for subnet: " + subnetId.getValue());
+                LOG.debug("{} onPortAddedToSubnet: Port {} added. Updating the SubnetOpDataEntry node for subnet {} "
+                                + "subnetIp {} vpnName {} rd {} TaskState {}", LOGGING_PREFIX, portId.getValue(),
+                        subnetId.getValue(), subnetIp, vpnName, rd, routeAdvState);
                 SubnetToDpn subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, portId.getValue());
                 if (subDpn == null) {
+                    LOG.error("{} onPortAddedToSubnet: subnet-to-dpn list is null for subnetId {}. portId {}, "
+                                    + "vpnName {}, rd {}, subnetIp {}", LOGGING_PREFIX, subnetId.getValue(),
+                            portId.getValue(), vpnName, rd, subnetIp);
                     return;
                 }
-                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
+                SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
+                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
                 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
                 subDpnList.add(subDpn);
                 subOpBuilder.setSubnetToDpn(subDpnList);
-                if (subOpBuilder.getNhDpnId()  == null) {
-                    subOpBuilder.setNhDpnId(dpnId);
-                }
-                BigInteger nhDpnId = subOpBuilder.getNhDpnId();
-                String rd = subOpBuilder.getVrfId();
-                String subnetIp = subOpBuilder.getSubnetCidr();
-                String vpnName = subOpBuilder.getVpnName();
-                Long elanTag = subOpBuilder.getElanTag();
-                if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) ||
-                        (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
-                    try {
-                        // Write the Subnet Route Entry to FIB
-                        // Advertise BGP Route here and set route_adv_state to DONE
-                        int label = getLabel(rd, subnetIp);
-                        addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                        advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                        subOpBuilder.setRouteAdvState(TaskState.Done);
-                    } catch (Exception ex) {
-                        logger.error("onPortAddedToSubnet: Advertising NextHopDPN "+ nhDpnId +
-                                " information for subnet " + subnetId.getValue() + " to BGP failed {}", ex);
+                if (subOpBuilder.getRouteAdvState() != TaskState.Advertised) {
+                    if (subOpBuilder.getNhDpnId() == null) {
+                        // No nexthop selected yet, elect one now
+                        electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
+                                subnetmap.getNetworkId(), true);
+                    } else if (!VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(), subnetId.getValue())) {
+                        // Already nexthop has been selected, only publishing to bgp required, so publish to bgp
+                        getNexthopTepAndPublishRoute(subOpBuilder, subnetId);
                     }
                 }
                 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
-                logger.info("onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port " + portId.getValue());
-
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
+                LOG.info("{} onPortAddedToSubnet: Updated subnetopdataentry to OP Datastore for port {} subnet {}"
+                        + " subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {}", LOGGING_PREFIX,
+                        portId.getValue(), subnetId.getValue(), subOpEntry.getSubnetCidr(), subOpEntry.getVpnName(),
+                        subOpBuilder.getVrfId(), subOpEntry.getRouteAdvState(), subOpEntry.getLastAdvState());
             } catch (Exception ex) {
-                logger.error("Creation of SubnetOpDataEntry for subnet " +
-                        subnetId.getValue() + " failed {}", ex);
+                LOG.error("{} onPortAddedToSubnet: Updation of subnetOpEntry for port {} subnet {} falied {}",
+                        LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), ex);
             } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
+        } catch (Exception e) {
+            LOG.error("{} onPortAddedToSubnet: Unable to handle port {} added to subnet {} {}", LOGGING_PREFIX,
+                    portId.getValue(), subnetId.getValue(), e);
         }
     }
 
-    @Override
-    public void onPortRemovedFromSubnet(PortRemovedFromSubnet notification) {
-        Uuid subnetId = notification.getSubnetId();
-        Uuid portId = notification.getPortId();
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void onPortRemovedFromSubnet(Subnetmap subnetmap, Uuid portId) {
+        Uuid subnetId = subnetmap.getId();
 
-        logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " being removed from subnet " + subnetId.getValue());
         //TODO(vivek): Change this to use more granularized lock at subnetId level
-        synchronized (this) {
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
             try {
                 PortOpDataEntry portOpEntry = subOpDpnManager.removePortOpDataEntry(portId.getValue());
                 if (portOpEntry == null) {
@@ -396,307 +480,504 @@ public class VpnSubnetRouteHandler implements NeutronvpnListener {
                 }
                 BigInteger dpnId = portOpEntry.getDpnId();
                 if (dpnId == null) {
-                    logger.debug("onPortRemovedFromSubnet:  Port {} does not have a DPNId associated, ignoring", portId.getValue());
+                    LOG.error("{} onPortRemovedFromSubnet:  Port {} does not have a DPNId associated,"
+                            + " ignoring removal from subnet {}", LOGGING_PREFIX, portId.getValue(),
+                            subnetId.getValue());
                     return;
                 }
-                logger.debug("onPortRemovedFromSubnet: Updating the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
                 boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, portId.getValue());
-                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
-                        child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
-                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                        subOpIdentifier);
+                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+                    InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
+                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                    subOpIdentifier);
                 if (!optionalSubs.isPresent()) {
-                    logger.info("onPortRemovedFromSubnet: Port " + portId.getValue() + " is part of a subnet " + subnetId.getValue() +
-                            " that is not in VPN, ignoring");
+                    LOG.info("{} onPortRemovedFromSubnet: Port {} is part of a subnet {} that is not in VPN,"
+                            + " ignoring", LOGGING_PREFIX, portId.getValue(), subnetId.getValue());
                     return;
                 }
-                SubnetOpDataEntry subOpEntry = null;
-                List<SubnetToDpn> subDpnList = null;
-                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
-                String rd = subOpBuilder.getVrfId();
-                String subnetIp = subOpBuilder.getSubnetCidr();
-                String vpnName = subOpBuilder.getVpnName();
-                Long elanTag = subOpBuilder.getElanTag();
+                LOG.info("{} onPortRemovedFromSubnet: Port {} being removed. Updating the SubnetOpDataEntry"
+                        + " for subnet {} subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {}", LOGGING_PREFIX,
+                        portId.getValue(), subnetId.getValue(), optionalSubs.get().getSubnetCidr(),
+                        optionalSubs.get().getVpnName(), optionalSubs.get().getVrfId(),
+                        optionalSubs.get().getRouteAdvState(), optionalSubs.get().getLastAdvState());
+                SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
+                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
                 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
                 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
                     // select another NhDpnId
                     if (last) {
-                        logger.debug("onPortRemovedFromSubnet: Last port " + portId + " on the subnet: " +  subnetId.getValue());
-                        // last port on this DPN, so we need to swap the NHDpnId
-                        subDpnList = subOpBuilder.getSubnetToDpn();
-                        if (subDpnList.isEmpty()) {
-                            subOpBuilder.setNhDpnId(null);
-                            try {
-                                // withdraw route from BGP
-                                deleteSubnetRouteFromFib(rd, subnetIp, vpnName);
-                                withdrawSubnetRoutefromBgp(rd, subnetIp);
-                                subOpBuilder.setRouteAdvState(TaskState.Na);
-                            } catch (Exception ex) {
-                                logger.error("onPortRemovedFromSubnet: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
-                                  subnetId.getValue() + " from BGP failed ", ex);
-                                subOpBuilder.setRouteAdvState(TaskState.Pending);
-                            }
-                        } else {
-                            nhDpnId = subDpnList.get(0).getDpnId();
-                            subOpBuilder.setNhDpnId(nhDpnId);
-                            logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
-                            try {
-                                // Best effort Withdrawal of route from BGP for this subnet
-                                // Advertise the new NexthopIP to BGP for this subnet
-                                //withdrawSubnetRoutefromBgp(rd, subnetIp);
-                                int label = getLabel(rd, subnetIp);
-                                addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                                advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                                subOpBuilder.setRouteAdvState(TaskState.Done);
-                            } catch (Exception ex) {
-                                logger.error("onPortRemovedFromSubnet: Swapping Withdrawing NextHopDPN " + dpnId +
-                                        " information for subnet " + subnetId.getValue() +
-                                        " to BGP failed {}" + ex);
-                                subOpBuilder.setRouteAdvState(TaskState.Pending);
-                            }
-                        }
+                        LOG.debug("{} onPortRemovedFromSubnet: Last port {} being removed from subnet {} subnetIp {}"
+                                + " vpnName {} rd {}", LOGGING_PREFIX, portId.getValue(), subnetId.getValue(),
+                                subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId());
+                        // last port on this DPN, so we need to elect the new NHDpnId
+                        electNewDpnForSubnetRoute(subOpBuilder, nhDpnId, subnetId, subnetmap.getNetworkId(),
+                                !VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(), subnetId.getValue()));
+                        MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
+                                subOpBuilder.build());
+                        LOG.info("{} onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore"
+                                + " removing port {} from subnet {} subnetIp {} vpnName {} rd {}", LOGGING_PREFIX,
+                                portId.getValue(), subnetId.getValue(), subOpBuilder.getSubnetCidr(),
+                                subOpBuilder.getVpnName(), subOpBuilder.getVrfId());
                     }
                 }
-                subOpEntry = subOpBuilder.build();
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
-                logger.info("onPortRemovedFromSubnet: Updated subnetopdataentry to OP Datastore removing port " + portId.getValue());
             } catch (Exception ex) {
-                logger.error("Creation of SubnetOpDataEntry for subnet " +
-                        subnetId.getValue() + " failed {}" + ex);
+                LOG.error("{} onPortRemovedFromSubnet: Removal of portOp for {} from subnet {} failed {}",
+                        LOGGING_PREFIX, portId.getValue(), subnetId.getValue(), ex);
             } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
+        } catch (Exception e) {
+            LOG.error("{} onPortRemovedFromSubnet: Unable to handle port {} removed from subnet {} {}",LOGGING_PREFIX,
+                    portId.getValue(), subnetId.getValue(), e);
         }
     }
 
-    public void onInterfaceUp(Interface intfState) {
-
-        logger.info("onInterfaceUp: Port " + intfState.getName());
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void onInterfaceUp(BigInteger dpnId, String intfName, Uuid subnetId) {
         //TODO(vivek): Change this to use more granularized lock at subnetId level
-        synchronized (this) {
-            SubnetToDpn subDpn = null;
-            String intfName = intfState.getName();
-            PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
-            if (portOpEntry == null) {
-                logger.info("onInterfaceUp: Port " + intfState.getName()  + "is part of a subnet not in VPN, ignoring");
-                return;
-            }
-            BigInteger dpnId = portOpEntry.getDpnId();
-            if (dpnId  == null) {
-                dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
-                if (dpnId == null) {
-                    logger.error("onInterfaceUp: Unable to determine the DPNID for port " + intfName);
-                    return;
-                }
-            }
-            Uuid subnetId = portOpEntry.getSubnetId();
+        SubnetToDpn subDpn = null;
+        if ((dpnId == null) || Objects.equals(dpnId, BigInteger.ZERO)) {
+            LOG.error("{} onInterfaceUp: Unable to determine the DPNID for port {} on subnet {}", LOGGING_PREFIX,
+                    intfName, subnetId.getValue());
+            return;
+        }
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
             try {
-                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
-                    child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
-                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker, LogicalDatastoreType.OPERATIONAL,
-                        subOpIdentifier);
+                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+                    InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
+                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                    subOpIdentifier);
                 if (!optionalSubs.isPresent()) {
-                    logger.error("onInterfaceUp: SubnetOpDataEntry for subnet " + subnetId.getValue() +
-                            " is not available");
+                    LOG.trace("{} onInterfaceUp: SubnetOpDataEntry for subnet {} is not available."
+                            + " Ignoring interfaceUp for port{}", LOGGING_PREFIX, subnetId.getValue(), intfName);
                     return;
                 }
-
-                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
-                logger.debug("onInterfaceUp: Updating the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
                 subOpDpnManager.addPortOpDataEntry(intfName, subnetId, dpnId);
                 subDpn = subOpDpnManager.addInterfaceToDpn(subnetId, dpnId, intfName);
                 if (subDpn == null) {
                     return;
                 }
+                SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
+                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
+                LOG.info("{} onInterfaceUp: Updating the SubnetOpDataEntry node for subnet {} subnetIp {} vpn {}"
+                        + " rd {} TaskState {} lastTaskState {}" , LOGGING_PREFIX, subnetId.getValue(),
+                        subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId(),
+                        subOpBuilder.getRouteAdvState(), subOpBuilder.getLastAdvState());
+                boolean isExternalSubnetVpn = VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(),
+                        subnetId.getValue());
                 List<SubnetToDpn> subDpnList = subOpBuilder.getSubnetToDpn();
                 subDpnList.add(subDpn);
                 subOpBuilder.setSubnetToDpn(subDpnList);
-                if (subOpBuilder.getNhDpnId()  == null) {
-                    subOpBuilder.setNhDpnId(dpnId);
-                }
-                BigInteger nhDpnId = subOpBuilder.getNhDpnId();
-                String rd = subOpBuilder.getVrfId();
-                String subnetIp = subOpBuilder.getSubnetCidr();
-                String vpnName = subOpBuilder.getVpnName();
-                Long elanTag = subOpBuilder.getElanTag();
-                if ((subOpBuilder.getRouteAdvState() == TaskState.Pending) || (subOpBuilder.getRouteAdvState() == TaskState.Na)) {
-                    try {
-                        // Write the Subnet Route Entry to FIB
-                        // Advertise BGP Route here and set route_adv_state to DONE
-                        int label = getLabel(rd, subnetIp);
-                        addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                        advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                        subOpBuilder.setRouteAdvState(TaskState.Done);
-                    } catch (Exception ex) {
-                        logger.error("onInterfaceUp: Advertising NextHopDPN " + nhDpnId + " information for subnet " +
-                          subnetId.getValue() + " to BGP failed {}" + ex);
+                if (subOpBuilder.getRouteAdvState() != TaskState.Advertised) {
+                    if (subOpBuilder.getNhDpnId() == null) {
+                        // No nexthop selected yet, elect one now
+                        electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
+                                null /*networkId*/, !isExternalSubnetVpn);
+                    } else if (!isExternalSubnetVpn) {
+                        // Already nexthop has been selected, only publishing to bgp required, so publish to bgp
+                        getNexthopTepAndPublishRoute(subOpBuilder, subnetId);
                     }
                 }
                 SubnetOpDataEntry subOpEntry = subOpBuilder.build();
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
-                logger.info("onInterfaceUp: Updated subnetopdataentry to OP Datastore port up " + intfName);
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
+                LOG.info("{} onInterfaceUp: Updated subnetopdataentry to OP Datastore port {} up for subnet {}"
+                        + " subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {} ", LOGGING_PREFIX, intfName,
+                        subnetId.getValue(), subOpEntry.getSubnetCidr(), subOpEntry.getVpnName(),
+                        subOpEntry.getVrfId(), subOpEntry.getRouteAdvState(), subOpEntry.getLastAdvState());
             } catch (Exception ex) {
-                logger.error("Creation of SubnetOpDataEntry for subnet " +
-                        subnetId.getValue() + " failed {}" + ex);
+                LOG.error("{} onInterfaceUp: Updation of SubnetOpDataEntry for subnet {} on port {} up failed {}",
+                        LOGGING_PREFIX, subnetId.getValue(), intfName, ex);
             } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
+        } catch (Exception e) {
+            LOG.error("{} onInterfaceUp: Unable to handle interface up event for port {} in subnet {} {}",
+                    LOGGING_PREFIX, intfName, subnetId.getValue(), e);
         }
     }
 
-    public void onInterfaceDown(Interface intfState) {
-        logger.info("onInterfaceDown: Port " + intfState.getName());
-        //TODO(vivek): Change this to use more granularized lock at subnetId level
-        synchronized (this) {
-            String intfName = intfState.getName();
-            PortOpDataEntry portOpEntry = subOpDpnManager.getPortOpDataEntry(intfName);
-            if (portOpEntry == null) {
-                logger.info("onInterfaceDown: Port " + intfState.getName()  + "is part of a subnet not in VPN, ignoring");
-                return;
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void onInterfaceDown(final BigInteger dpnId, final String interfaceName, Uuid subnetId) {
+        if ((dpnId == null) || (Objects.equals(dpnId, BigInteger.ZERO))) {
+            LOG.error("{} onInterfaceDown: Unable to determine the DPNID for port {} on subnet {}", LOGGING_PREFIX,
+                    interfaceName, subnetId.getValue());
+            return;
+        }
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
+            try {
+                boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, interfaceName);
+                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+                    InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
+                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
+                    LogicalDatastoreType.OPERATIONAL,
+                    subOpIdentifier);
+                if (!optionalSubs.isPresent()) {
+                    LOG.error("{} onInterfaceDown: SubnetOpDataEntry for subnet {} is not available."
+                            + " Ignoring port {} down event.", LOGGING_PREFIX, subnetId.getValue(), interfaceName);
+                    return;
+                }
+                SubnetOpDataEntry subnetOpDataEntry = optionalSubs.get();
+                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subnetOpDataEntry);
+                LOG.info("{} onInterfaceDown: Updating the SubnetOpDataEntry node for subnet {} subnetIp {}"
+                        + " vpnName {} rd {} TaskState {} lastTaskState {} on port {} down", LOGGING_PREFIX,
+                        subnetId.getValue(), subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(),
+                        subOpBuilder.getVrfId(), subOpBuilder.getRouteAdvState(), subOpBuilder.getLastAdvState(),
+                        interfaceName);
+                BigInteger nhDpnId = subOpBuilder.getNhDpnId();
+                if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
+                    // select another NhDpnId
+                    if (last) {
+                        LOG.debug("{} onInterfaceDown: Last active port {} on the subnet {} subnetIp {} vpn {}"
+                                + " rd {}", LOGGING_PREFIX, interfaceName, subnetId.getValue(),
+                                subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId());
+                        // last port on this DPN, so we need to elect the new NHDpnId
+                        electNewDpnForSubnetRoute(subOpBuilder, dpnId, subnetId, null /*networkId*/,
+                                !VpnUtil.isExternalSubnetVpn(subnetOpDataEntry.getVpnName(), subnetId.getValue()));
+                        MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier,
+                                subOpBuilder.build());
+                        LOG.info("{} onInterfaceDown: Updated subnetopdataentry for subnet {} subnetIp {} vpnName {}"
+                                + " rd {} to OP Datastore on port {} down ", LOGGING_PREFIX, subnetId.getValue(),
+                                subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId(),
+                                interfaceName);
+                    }
+                }
+            } catch (Exception ex) {
+                LOG.error("{} onInterfaceDown: SubnetOpDataEntry update on interface {} down event for subnet {}"
+                        + " falied {}", LOGGING_PREFIX, interfaceName, subnetId.getValue(), ex);
+            } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
-            BigInteger dpnId = portOpEntry.getDpnId();
-            if (dpnId  == null) {
-                dpnId = InterfaceUtils.getDpIdFromInterface(intfState);
-                if (dpnId == null) {
-                    logger.error("onInterfaceDown: Unable to determine the DPNID for port " + intfName);
+        } catch (Exception e) {
+            LOG.error("{} onInterfaceDown: Unable to handle interface down event for port {} in subnet {} {}",
+                    LOGGING_PREFIX, interfaceName, subnetId.getValue(), e);
+        }
+    }
+
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void updateSubnetRouteOnTunnelUpEvent(Uuid subnetId, BigInteger dpnId) {
+        LOG.info("{} updateSubnetRouteOnTunnelUpEvent: Subnet {} Dpn {}", LOGGING_PREFIX, subnetId.getValue(),
+                dpnId.toString());
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
+            try {
+                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+                    InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
+                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
+                    LogicalDatastoreType.OPERATIONAL,
+                    subOpIdentifier);
+                if (!optionalSubs.isPresent()) {
+                    LOG.error("{} updateSubnetRouteOnTunnelUpEvent: SubnetOpDataEntry for subnet {} is not available",
+                            LOGGING_PREFIX, subnetId.getValue());
                     return;
                 }
+                LOG.info("{} updateSubnetRouteOnTunnelUpEvent: Subnet {} subnetIp {} vpnName {} rd {} TaskState {}"
+                        + " lastTaskState {} Dpn {}", LOGGING_PREFIX, subnetId.getValue(),
+                        optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
+                        optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState(),
+                        optionalSubs.get().getLastAdvState(), dpnId.toString());
+                SubnetOpDataEntry subOpEntry = optionalSubs.get();
+                SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(subOpEntry);
+                boolean isExternalSubnetVpn = VpnUtil.isExternalSubnetVpn(subOpEntry.getVpnName(), subnetId.getValue());
+                if (subOpBuilder.getRouteAdvState() != TaskState.Advertised) {
+                    if (subOpBuilder.getNhDpnId() == null) {
+                        // No nexthop selected yet, elect one now
+                        electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId,
+                                null /*networkId*/, !isExternalSubnetVpn);
+                    } else if (!isExternalSubnetVpn) {
+                        // Already nexthop has been selected, only publishing to bgp required, so publish to bgp
+                        getNexthopTepAndPublishRoute(subOpBuilder, subnetId);
+                    }
+                }
+                subOpEntry = subOpBuilder.build();
+                MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
+                LOG.info("{} updateSubnetRouteOnTunnelUpEvent: Updated subnetopdataentry to OP Datastore tunnel up"
+                        + " on dpn {} for subnet {} subnetIp {} vpnName {} rd {} TaskState {} lastTaskState {}",
+                        LOGGING_PREFIX, dpnId.toString(), subnetId.getValue(), subOpEntry.getSubnetCidr(),
+                        subOpEntry.getVpnName(), subOpEntry.getVrfId(), subOpEntry.getRouteAdvState(),
+                        subOpEntry.getLastAdvState());
+            } catch (Exception ex) {
+                LOG.error("{} updateSubnetRouteOnTunnelUpEvent: updating subnetRoute for subnet {} on dpn {}",
+                        LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(), ex);
+            } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
-            Uuid subnetId = portOpEntry.getSubnetId();
+        } catch (Exception e) {
+            LOG.error("{} updateSubnetRouteOnTunnelUpEvent: Unable to handle tunnel up event for subnetId {} dpnId {}"
+                    + " with exception {}", LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(), e);
+        }
+    }
+
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    public void updateSubnetRouteOnTunnelDownEvent(Uuid subnetId, BigInteger dpnId) {
+        LOG.info("updateSubnetRouteOnTunnelDownEvent: Subnet {} Dpn {}", subnetId.getValue(), dpnId.toString());
+        //TODO(vivek): Change this to use more granularized lock at subnetId level
+        try {
+            VpnUtil.lockSubnet(lockManager, subnetId.getValue());
             try {
-                logger.debug("onInterfaceDown: Updating the SubnetOpDataEntry node for subnet: " +  subnetId.getValue());
-                boolean last = subOpDpnManager.removeInterfaceFromDpn(subnetId, dpnId, intfName);
-                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier = InstanceIdentifier.builder(SubnetOpData.class).
-                        child(SubnetOpDataEntry.class, new SubnetOpDataEntryKey(subnetId)).build();
-                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(broker,
-                        LogicalDatastoreType.OPERATIONAL,
-                        subOpIdentifier);
+                InstanceIdentifier<SubnetOpDataEntry> subOpIdentifier =
+                    InstanceIdentifier.builder(SubnetOpData.class).child(SubnetOpDataEntry.class,
+                        new SubnetOpDataEntryKey(subnetId)).build();
+                Optional<SubnetOpDataEntry> optionalSubs = VpnUtil.read(dataBroker,
+                    LogicalDatastoreType.OPERATIONAL,
+                    subOpIdentifier);
                 if (!optionalSubs.isPresent()) {
-                    logger.error("onInterfaceDown: SubnetOpDataEntry for subnet " + subnetId.getValue() +
-                            " is not available");
+                    LOG.error("{} updateSubnetRouteOnTunnelDownEvent: SubnetOpDataEntry for subnet {}"
+                            + " is not available", LOGGING_PREFIX, subnetId.getValue());
                     return;
                 }
+                LOG.debug("{} updateSubnetRouteOnTunnelDownEvent: Dpn {} Subnet {} subnetIp {} vpnName {} rd {}"
+                        + " TaskState {} lastTaskState {}", LOGGING_PREFIX, dpnId.toString(), subnetId.getValue(),
+                        optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
+                        optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState(),
+                        optionalSubs.get().getLastAdvState());
                 SubnetOpDataEntry subOpEntry = null;
-                List<SubnetToDpn> subDpnList = null;
                 SubnetOpDataEntryBuilder subOpBuilder = new SubnetOpDataEntryBuilder(optionalSubs.get());
-                String rd = subOpBuilder.getVrfId();
-                String subnetIp = subOpBuilder.getSubnetCidr();
-                String vpnName = subOpBuilder.getVpnName();
-                Long elanTag = subOpBuilder.getElanTag();
                 BigInteger nhDpnId = subOpBuilder.getNhDpnId();
                 if ((nhDpnId != null) && (nhDpnId.equals(dpnId))) {
-                    // select another NhDpnId
-                    if (last) {
-                        logger.debug("onInterfaceDown: Last active port " + intfState.getName() + " on the subnet: " +  subnetId.getValue());
-                        // last port on this DPN, so we need to swap the NHDpnId
-                        subDpnList = subOpBuilder.getSubnetToDpn();
-                        if (subDpnList.isEmpty()) {
-                            subOpBuilder.setNhDpnId(null);
-                            try {
-                                // Withdraw route from BGP for this subnet
-                                deleteSubnetRouteFromFib(rd, subnetIp, vpnName);
-                                withdrawSubnetRoutefromBgp(rd, subnetIp);
-                                subOpBuilder.setRouteAdvState(TaskState.Na);
-                            } catch (Exception ex) {
-                                logger.error("onInterfaceDown: Withdrawing NextHopDPN " + dpnId + " information for subnet " +
-                                  subnetId.getValue() + " from BGP failed {}" + ex);
-                                subOpBuilder.setRouteAdvState(TaskState.Pending);
-                            }
-                        } else {
-                            nhDpnId = subDpnList.get(0).getDpnId();
-                            subOpBuilder.setNhDpnId(nhDpnId);
-                            logger.debug("onInterfaceDown: Swapping the Designated DPN to " + nhDpnId + " for subnet " + subnetId.getValue());
-                            try {
-                                // Best effort Withdrawal of route from BGP for this subnet
-                                //withdrawSubnetRoutefromBgp(rd, subnetIp);
-                                int label = getLabel(rd, subnetIp);
-                                addSubnetRouteToFib(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                                advertiseSubnetRouteToBgp(rd, subnetIp, nhDpnId, vpnName, elanTag, label);
-                                subOpBuilder.setRouteAdvState(TaskState.Done);
-                            } catch (Exception ex) {
-                                logger.error("onInterfaceDown: Swapping Withdrawing NextHopDPN " + dpnId + " information for subnet " +
-                                        subnetId.getValue() + " to BGP failed {}" + ex);
-                                subOpBuilder.setRouteAdvState(TaskState.Pending);
-                            }
-                        }
-                    }
+                    electNewDpnForSubnetRoute(subOpBuilder, dpnId, subnetId, null /*networkId*/, true);
+                    subOpEntry = subOpBuilder.build();
+                    MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
+                    LOG.info("{} updateSubnetRouteOnTunnelDownEvent: Subnet {} Dpn {} subnetIp {} vpnName {} rd {}"
+                            + " TaskState {} lastTaskState {}", LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(),
+                            optionalSubs.get().getSubnetCidr(), optionalSubs.get().getVpnName(),
+                            optionalSubs.get().getVrfId(), optionalSubs.get().getRouteAdvState(),
+                            optionalSubs.get().getLastAdvState());
                 }
-                subOpEntry = subOpBuilder.build();
-                MDSALUtil.syncWrite(broker, LogicalDatastoreType.OPERATIONAL, subOpIdentifier, subOpEntry);
-                logger.info("onInterfaceDown: Updated subnetopdataentry to OP Datastore port down " + intfName);
             } catch (Exception ex) {
-                logger.error("Creation of SubnetOpDataEntry for subnet " +
-                        subnetId.getValue() + " failed {}" + ex);
+                LOG.error("{} updateSubnetRouteOnTunnelDownEvent: Updation of SubnetOpDataEntry for subnet {}"
+                        + " on dpn {} failed {}", LOGGING_PREFIX, subnetId.getValue(), dpnId, ex);
             } finally {
+                VpnUtil.unlockSubnet(lockManager, subnetId.getValue());
             }
+        } catch (Exception e) {
+            LOG.error("{} updateSubnetRouteOnTunnelDownEvent: Unable to handle tunnel down event for subnetId {}"
+                    + " dpnId {} with exception {}", LOGGING_PREFIX, subnetId.getValue(), dpnId.toString(), e);
         }
     }
 
-    @Override
-    public void onRouterAssociatedToVpn(RouterAssociatedToVpn notification) {
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private void publishSubnetRouteToBgp(SubnetOpDataEntryBuilder subOpBuilder, String nextHopIp) {
+        try {
+            //BGP manager will handle withdraw and advertise internally if prefix
+            //already exist
+            long label = 0;
+            long l3vni = 0;
+
+            VrfEntry.EncapType encapType =  VpnUtil.getEncapType(VpnUtil.isL3VpnOverVxLan(l3vni));
+            if (encapType.equals(VrfEntry.EncapType.Vxlan)) {
+                l3vni = subOpBuilder.getL3vni();
+            } else {
+                label = subOpBuilder.getLabel();
+            }
+            bgpManager.advertisePrefix(subOpBuilder.getVrfId(), null /*macAddress*/, subOpBuilder.getSubnetCidr(),
+                    Arrays.asList(nextHopIp), encapType,  label, l3vni,
+                    0 /*l2vni*/, null /*gatewayMacAddress*/);
+            subOpBuilder.setLastAdvState(subOpBuilder.getRouteAdvState()).setRouteAdvState(TaskState.Advertised);
+        } catch (Exception e) {
+            LOG.error("{} publishSubnetRouteToBgp: Subnet route not advertised for subnet {} subnetIp {} vpn {} rd {}"
+                    + " with dpnid {}", LOGGING_PREFIX, subOpBuilder.getSubnetId().getValue(),
+                    subOpBuilder.getSubnetCidr(), subOpBuilder.getVpnName(), subOpBuilder.getVrfId(), nextHopIp, e);
+        }
     }
 
-    @Override
-    public void onRouterDisassociatedFromVpn(RouterDisassociatedFromVpn notification) {
+    private void getNexthopTepAndPublishRoute(SubnetOpDataEntryBuilder subOpBuilder, Uuid subnetId) {
+        String nhTepIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker,
+                subOpBuilder.getNhDpnId());
+        if (nhTepIp != null) {
+            publishSubnetRouteToBgp(subOpBuilder, nhTepIp);
+        } else {
+            LOG.warn("Unable to find nexthopip for rd {} subnetroute subnetip {} for dpnid {}",
+                    subOpBuilder.getVrfId(), subOpBuilder.getSubnetCidr(),
+                    subOpBuilder.getNhDpnId().toString());
+            electNewDpnForSubnetRoute(subOpBuilder, null /* oldDpnId */, subnetId, null /*networkId*/, true);
+        }
     }
 
-    private void addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
-                                     Long elanTag, int label) {
-        Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
-        Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
-        Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
-        Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
-        String nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId);
-        if(nexthopIp != null)
-            vpnInterfaceManager.addSubnetRouteFibEntryToDS(rd, vpnName, subnetIp, nexthopIp, label, elanTag, nhDpnId);
-        else
-            logger.info("Unable to get nextHop ip address for nextHop DPN {}. Abort adding subnet route to FIB table.", nhDpnId);
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private boolean addSubnetRouteToFib(String rd, String subnetIp, BigInteger nhDpnId, String nextHopIp,
+                                        String vpnName, Long elanTag, long label, long l3vni,
+                                        Uuid subnetId, boolean isBgpVpn, String networkName) {
+
+        Preconditions.checkNotNull(rd,
+                LOGGING_PREFIX + " addSubnetRouteToFib: RouteDistinguisher cannot be null or empty!");
+        Preconditions.checkNotNull(subnetIp,
+                LOGGING_PREFIX + " addSubnetRouteToFib: SubnetRouteIp cannot be null or empty!");
+        Preconditions.checkNotNull(vpnName, LOGGING_PREFIX + " addSubnetRouteToFib: vpnName cannot be null or empty!");
+        Preconditions.checkNotNull(elanTag, LOGGING_PREFIX + " addSubnetRouteToFib: elanTag cannot be null or empty!");
+        Preconditions.checkNotNull(label, LOGGING_PREFIX + " addSubnetRouteToFib: label cannot be null or empty!");
+        VrfEntry.EncapType encapType = VpnUtil.getEncapType(VpnUtil.isL3VpnOverVxLan(l3vni));
+        VpnPopulator vpnPopulator = L3vpnRegistry.getRegisteredPopulator(encapType);
+        LOG.info("{} addSubnetRouteToFib: Adding SubnetRoute fib entry for vpnName {}, subnetIP {}, elanTag {}",
+                LOGGING_PREFIX, vpnName, subnetIp, elanTag);
+        L3vpnInput input = new L3vpnInput().setRouteOrigin(RouteOrigin.CONNECTED).setRd(rd).setVpnName(vpnName)
+                .setSubnetIp(subnetIp).setNextHopIp(nextHopIp).setL3vni(l3vni).setLabel(label).setElanTag(elanTag)
+                .setDpnId(nhDpnId).setEncapType(encapType).setNetworkName(networkName).setPrimaryRd(rd);
+        if (!isBgpVpn) {
+            vpnPopulator.populateFib(input, null /*writeCfgTxn*/, null /*writeOperTxn*/);
+            return true;
+        }
+        Preconditions.checkNotNull(nextHopIp, LOGGING_PREFIX + "NextHopIp cannot be null or empty!");
+        VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, VpnUtil
+                .getPrefixToInterfaceIdentifier(VpnUtil.getVpnId(dataBroker, vpnName), subnetIp), VpnUtil
+                .getPrefixToInterface(nhDpnId, subnetId.getValue(), subnetIp, subnetId,
+                        Prefixes.PrefixCue.SubnetRoute));
+        vpnPopulator.populateFib(input, null /*writeCfgTxn*/, null /*writeOperTxn*/);
+        try {
+            // BGP manager will handle withdraw and advertise internally if prefix
+            // already exist
+            bgpManager.advertisePrefix(rd, null /*macAddress*/, subnetIp, Collections.singletonList(nextHopIp),
+                    encapType, label, l3vni, 0 /*l2vni*/, null /*gatewayMacAddress*/);
+        } catch (Exception e) {
+            LOG.error("{} addSubnetRouteToFib: Subnet route not advertised for subnet {} subnetIp {} vpnName {} rd {} "
+                    + "with dpnid {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName, rd, nhDpnId, e);
+            return false;
+        }
+        return true;
     }
 
     private int getLabel(String rd, String subnetIp) {
         int label = VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
-                                        VpnUtil.getNextHopLabelKey(rd, subnetIp));
-        logger.trace("Allocated subnetroute label {} for rd {} prefix {}", label, rd, subnetIp);
+            VpnUtil.getNextHopLabelKey(rd, subnetIp));
+        LOG.trace("{} getLabel: Allocated subnetroute label {} for rd {} prefix {}", LOGGING_PREFIX, label, rd,
+                subnetIp);
         return label;
     }
 
-    private void deleteSubnetRouteFromFib(String rd, String subnetIp, String vpnName) {
-        Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
-        Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private boolean deleteSubnetRouteFromFib(String rd, String subnetIp, String vpnName, boolean isBgpVpn) {
+        Preconditions.checkNotNull(rd,
+                LOGGING_PREFIX + " deleteSubnetRouteFromFib: RouteDistinguisher cannot be null or empty!");
+        Preconditions.checkNotNull(subnetIp,
+                LOGGING_PREFIX +  " deleteSubnetRouteFromFib: SubnetRouteIp cannot be null or empty!");
         vpnInterfaceManager.deleteSubnetRouteFibEntryFromDS(rd, subnetIp, vpnName);
+        if (isBgpVpn) {
+            try {
+                bgpManager.withdrawPrefix(rd, subnetIp);
+            } catch (Exception e) {
+                LOG.error("{} deleteSubnetRouteFromFib: Subnet route not withdrawn for subnetIp {} vpn {} rd {}"
+                        + "  due to exception {}", LOGGING_PREFIX, subnetIp, vpnName, rd, e);
+                return false;
+            }
+        }
+        return true;
     }
 
-    private void advertiseSubnetRouteToBgp(String rd, String subnetIp, BigInteger nhDpnId, String vpnName,
-                                           Long elanTag, int label) throws Exception {
-        Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
-        Preconditions.checkNotNull(subnetIp, "SubnetRouteIp cannot be null or empty!");
-        Preconditions.checkNotNull(elanTag, "elanTag cannot be null or empty!");
-        Preconditions.checkNotNull(nhDpnId, "nhDpnId cannot be null or empty!");
-        Preconditions.checkNotNull(vpnName, "vpnName cannot be null or empty!");
-        String nexthopIp = null;
-        nexthopIp = InterfaceUtils.getEndpointIpAddressForDPN(broker, nhDpnId);
-        if (nexthopIp == null) {
-            logger.error("createSubnetRouteInVpn: Unable to obtain endpointIp address for DPNId " + nhDpnId);
-            throw new Exception("Unable to obtain endpointIp address for DPNId " + nhDpnId);
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private void electNewDpnForSubnetRoute(SubnetOpDataEntryBuilder subOpBuilder, BigInteger oldDpnId, Uuid subnetId,
+                                           Uuid networkId, boolean isBgpVpn) {
+        List<SubnetToDpn> subDpnList = null;
+        boolean isRouteAdvertised = false;
+        subDpnList = subOpBuilder.getSubnetToDpn();
+        String rd = subOpBuilder.getVrfId();
+        String subnetIp = subOpBuilder.getSubnetCidr();
+        String vpnName = subOpBuilder.getVpnName();
+        long elanTag = subOpBuilder.getElanTag();
+        BigInteger nhDpnId = null;
+        String nhTepIp = null;
+        boolean isAlternateDpnSelected = false;
+        long l3vni = 0;
+        long label = 0;
+        if (VpnUtil.isL3VpnOverVxLan(subOpBuilder.getL3vni())) {
+            l3vni = subOpBuilder.getL3vni();
+        } else {
+            label = getLabel(rd, subnetIp);
+            subOpBuilder.setLabel(label);
         }
-        try {
-            // BGPManager (inside ODL) requires a withdraw followed by advertise
-            // due to bugs with ClusterDataChangeListener used by BGPManager.
-            //bgpManager.withdrawPrefix(rd, subnetIp);
-            bgpManager.advertisePrefix(rd, subnetIp, Arrays.asList(nexthopIp), label);
-        } catch (Exception e) {
-            logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
-            throw e;
+        LOG.info("{} electNewDpnForSubnetRoute: Handling subnet {} subnetIp {} vpn {} rd {} TaskState {}"
+                + " lastTaskState {}", LOGGING_PREFIX, subnetId.getValue(), subnetIp, subOpBuilder.getVpnName(),
+                subOpBuilder.getVrfId(), subOpBuilder.getRouteAdvState(), subOpBuilder.getLastAdvState());
+        if (!isBgpVpn) {
+            // Non-BGPVPN as it stands here represents use-case of External Subnets of VLAN-Provider-Network
+            //  TODO(Tomer):  Pulling in both external and internal VLAN-Provider-Network need to be
+            // blended more better into this design.
+            isRouteAdvertised = addSubnetRouteToFib(rd, subnetIp, nhDpnId, nhTepIp,
+                    vpnName, elanTag, label, l3vni, subnetId, isBgpVpn, networkId.getValue());
+            if (isRouteAdvertised) {
+                subOpBuilder.setRouteAdvState(TaskState.Advertised);
+            } else {
+                LOG.error("{} electNewDpnForSubnetRoute: Unable to find TepIp for subnet {} subnetip {} vpnName {}"
+                    + " rd {} for dpnid {}, attempt next dpn", LOGGING_PREFIX, subnetId.getValue(), subnetIp,
+                    vpnName, rd, nhDpnId.toString());
+                subOpBuilder.setRouteAdvState(TaskState.PendingAdvertise);
+            }
+            return;
+        }
+        Iterator<SubnetToDpn> subnetDpnIter = subDpnList.iterator();
+        while (subnetDpnIter.hasNext()) {
+            SubnetToDpn subnetToDpn = subnetDpnIter.next();
+            if (subnetToDpn.getDpnId().equals(oldDpnId)) {
+                // Is this same is as input dpnId, then ignore it
+                continue;
+            }
+            nhDpnId = subnetToDpn.getDpnId();
+            if (vpnNodeListener.isConnectedNode(nhDpnId)) {
+                // selected dpnId is connected to ODL
+                // but does it have a TEP configured at all?
+                try {
+                    nhTepIp = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, nhDpnId);
+                    if (nhTepIp != null) {
+                        isAlternateDpnSelected = true;
+                        break;
+                    }
+                } catch (Exception e) {
+                    LOG.warn("{} electNewDpnForSubnetRoute: Unable to find TepIp for rd {} subnetroute subnetip {}"
+                            + " for dpnid {}, attempt next", LOGGING_PREFIX, rd, subnetIp, nhDpnId.toString(), e);
+                    continue;
+                }
+            }
+        }
+        if (!isAlternateDpnSelected) {
+            //If no alternate Dpn is selected as nextHopDpn, withdraw the subnetroute if it had a nextHop already.
+            if (isRouteAdvertised(subOpBuilder) && (oldDpnId != null)) {
+                LOG.info("{} electNewDpnForSubnetRoute: No alternate DPN available for subnet {} subnetIp {} vpn {}"
+                        + " rd {} Prefix withdrawn from BGP", LOGGING_PREFIX, subnetId.getValue(), subnetIp, vpnName,
+                        rd);
+                // Withdraw route from BGP for this subnet
+                boolean routeWithdrawn = deleteSubnetRouteFromFib(rd, subnetIp, vpnName, isBgpVpn);
+                subOpBuilder.setNhDpnId(null);
+                subOpBuilder.setLastAdvState(subOpBuilder.getRouteAdvState());
+                if (routeWithdrawn) {
+                    subOpBuilder.setRouteAdvState(TaskState.Withdrawn);
+                } else {
+                    LOG.error("{} electNewDpnForSubnetRoute: Withdrawing NextHopDPN {} for subnet {} subnetIp {}"
+                        + " vpn {} rd {} from BGP failed", LOGGING_PREFIX, oldDpnId.toString(), subnetId.getValue(),
+                        subnetIp, vpnName, rd);
+                    subOpBuilder.setRouteAdvState(TaskState.PendingWithdraw);
+                }
+            }
+        } else {
+            //If alternate Dpn is selected as nextHopDpn, use that for subnetroute.
+            subOpBuilder.setNhDpnId(nhDpnId);
+            //update the VRF entry for the subnetroute.
+            isRouteAdvertised = addSubnetRouteToFib(rd, subnetIp, nhDpnId, nhTepIp,
+                    vpnName, elanTag, label, l3vni, subnetId, isBgpVpn, networkId.getValue());
+            subOpBuilder.setLastAdvState(subOpBuilder.getRouteAdvState());
+            if (isRouteAdvertised) {
+                subOpBuilder.setRouteAdvState(TaskState.Advertised);
+            } else {
+                LOG.error("{} electNewDpnForSubnetRoute: Swapping to add new NextHopDpn {} for subnet {} subnetIp {}"
+                        + " vpn {} rd {} failed", LOGGING_PREFIX, nhDpnId, subnetId.getValue(), subnetIp, vpnName, rd);
+                subOpBuilder.setRouteAdvState(TaskState.PendingAdvertise);
+            }
         }
     }
 
-    private void withdrawSubnetRoutefromBgp(String rd, String subnetIp) throws Exception {
-        Preconditions.checkNotNull(rd, "RouteDistinguisher cannot be null or empty!");
-        Preconditions.checkNotNull(subnetIp, "SubnetIp cannot be null or empty!");
-        try {
-            bgpManager.withdrawPrefix(rd, subnetIp);
-        } catch (Exception e) {
-            logger.error("Subnet route not advertised for rd " + rd + " failed ", e);
-            throw e;
-        }
+    private boolean isRouteAdvertised(SubnetOpDataEntryBuilder subOpBuilder) {
+        return ((subOpBuilder.getRouteAdvState() == TaskState.Advertised)
+                || (subOpBuilder.getRouteAdvState() == TaskState.PendingAdvertise));
     }
 }