fibmanager dead code removal
[netvirt.git] / fibmanager / impl / src / main / java / org / opendaylight / netvirt / fibmanager / VrfEntryListener.java
old mode 100755 (executable)
new mode 100644 (file)
index 62acf7f..540e06a
@@ -14,6 +14,7 @@ import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
@@ -27,12 +28,16 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.Callable;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.locks.ReentrantLock;
+import javax.annotation.Nullable;
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 import javax.inject.Singleton;
+import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
@@ -53,7 +58,6 @@ import org.opendaylight.genius.mdsalutil.MatchInfo;
 import org.opendaylight.genius.mdsalutil.MetaDataUtil;
 import org.opendaylight.genius.mdsalutil.NWUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
-import org.opendaylight.genius.mdsalutil.UpgradeState;
 import org.opendaylight.genius.mdsalutil.actions.ActionDrop;
 import org.opendaylight.genius.mdsalutil.actions.ActionGroup;
 import org.opendaylight.genius.mdsalutil.actions.ActionPopMpls;
@@ -66,6 +70,7 @@ import org.opendaylight.genius.mdsalutil.matches.MatchIpv4Destination;
 import org.opendaylight.genius.mdsalutil.matches.MatchMetadata;
 import org.opendaylight.genius.mdsalutil.matches.MatchMplsLabel;
 import org.opendaylight.genius.mdsalutil.matches.MatchTunnelId;
+import org.opendaylight.genius.utils.JvmGlobalLocks;
 import org.opendaylight.genius.utils.ServiceIndex;
 import org.opendaylight.genius.utils.batching.SubTransaction;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
@@ -78,6 +83,7 @@ import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkCache;
 import org.opendaylight.netvirt.vpnmanager.api.intervpnlink.InterVpnLinkDataComposite;
+import org.opendaylight.serviceutils.upgrade.UpgradeState;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
@@ -225,7 +231,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
             LOG.info("EVPN flows need to be programmed.");
             EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
-                    nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
+                    nextHopManager, jobCoordinator, fibUtil, upgradeState, eventCallbacks);
             evpnVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
             closeables.add(evpnVrfEntryHandler);
             return;
@@ -233,7 +239,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
         if (routerInt != null) {
             // ping responder for router interfaces
-            routerInterfaceVrfEntryHandler.createFlows(identifier, vrfEntry, rd);
+            routerInterfaceVrfEntryHandler.createFlows(vrfEntry, rd);
             return;
         }
         if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
@@ -259,7 +265,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         if (VrfEntry.EncapType.Vxlan.equals(vrfEntry.getEncapType())) {
             LOG.info("EVPN flows to be deleted");
             EvpnVrfEntryHandler evpnVrfEntryHandler = new EvpnVrfEntryHandler(dataBroker, this, bgpRouteVrfEntryHandler,
-                    nextHopManager, jobCoordinator, elanManager, fibUtil, upgradeState, eventCallbacks);
+                    nextHopManager, jobCoordinator, fibUtil, upgradeState, eventCallbacks);
             evpnVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
             closeables.add(evpnVrfEntryHandler);
             return;
@@ -267,7 +273,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         RouterInterface routerInt = vrfEntry.augmentation(RouterInterface.class);
         if (routerInt != null) {
             // ping responder for router interfaces
-            routerInterfaceVrfEntryHandler.removeFlows(identifier, vrfEntry, rd);
+            routerInterfaceVrfEntryHandler.removeFlows(vrfEntry, rd);
             return;
         }
         if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.BGP) {
@@ -431,7 +437,9 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         if (!localDpnIdList.isEmpty() && vpnToDpnList != null) {
             jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
-                    synchronized (vpnInstance.getVpnInstanceName().intern()) {
+                    final ReentrantLock lock = lockFor(vpnInstance);
+                    lock.lock();
+                    try {
                         for (VpnToDpnList vpnDpn : vpnToDpnList) {
                             if (!localDpnIdList.contains(vpnDpn.getDpnId())) {
                                 if (vpnDpn.getDpnState() == VpnToDpnList.DpnState.Active) {
@@ -452,6 +460,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                 }
                             }
                         }
+                    } finally {
+                        lock.unlock();
                     }
                 })), MAX_RETRIES);
         }
@@ -496,7 +506,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         LabelRouteInfoBuilder builder = new LabelRouteInfoBuilder(lri);
         if (!isPresentInList) {
             LOG.debug("vpnName {} is not present in LRI with label {}..", vpnInstanceName, lri.getLabel());
-            List<String> vpnInstanceNames = lri.getVpnInstanceList();
+            List<String> vpnInstanceNames =
+                lri.getVpnInstanceList() != null ? new ArrayList<>(lri.getVpnInstanceList()) : new ArrayList<>();
             vpnInstanceNames.add(vpnInstanceName);
             builder.setVpnInstanceList(vpnInstanceNames);
             MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, lriId, builder.build());
@@ -523,8 +534,11 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         }
         FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
             List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
-            synchronized (label.toString().intern()) {
-                LabelRouteInfo lri = getLabelRouteInfo(label);
+            final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
+            final ReentrantLock lock = lockFor(lriKey);
+            lock.lock();
+            try {
+                LabelRouteInfo lri = getLabelRouteInfo(lriKey);
                 if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
 
                     if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
@@ -540,6 +554,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                     LOG.debug("SUBNETROUTE: installSubnetRouteInFib: Fetched labelRouteInfo for label {} interface {}"
                             + " and got dpn {}", label, lri.getVpnInterfaceName(), lri.getDpnId());
                 }
+            } finally {
+                lock.unlock();
             }
         });
         final List<InstructionInfo> instructions = new ArrayList<>();
@@ -751,45 +767,60 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
         String vpnName = fibUtil.getVpnNameFromId(vpnId);
         if (localNextHopInfo == null) {
-            List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
-            List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
-                    vpnName, usedRds, localNextHopIP);
-            if (LOG.isDebugEnabled()) {
-                LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
-                        vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
-            }
             boolean localNextHopSeen = false;
-            //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
-            for (Routes vpnExtraRoute : vpnExtraRoutes) {
-                String ipPrefix;
-                if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
-                    ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
-                } else {
-                    ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
+            List<Routes> vpnExtraRoutes = null;
+            //Synchronized to prevent missing bucket action due to race condition between refreshFib and
+            // add/updateFib threads on missing nexthop in VpnToExtraroutes
+            // FIXME: use an Identifier structure?
+            final ReentrantLock lock = JvmGlobalLocks.getLockForString(localNextHopIP + FibConstants.SEPARATOR + rd);
+            lock.lock();
+            try {
+                List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, localNextHopIP);
+                vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
+                        vpnName, usedRds, localNextHopIP);
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("Creating Local fib entry with vpnName {} usedRds {} localNextHopIP {} vpnExtraRoutes {}",
+                            vpnName, usedRds, localNextHopIP, vpnExtraRoutes);
                 }
-                Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
-                    ipPrefix);
-                if (localNextHopInfoLocal != null) {
-                    localNextHopSeen = true;
-                    BigInteger dpnId =
-                            checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
-                                    vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
-                    returnLocalDpnId.add(dpnId);
+
+                //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
+                for (Routes vpnExtraRoute : vpnExtraRoutes) {
+                    String ipPrefix;
+                    if (isIpv4Address(vpnExtraRoute.getNexthopIpList().get(0))) {
+                        ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV4PREFIX;
+                    } else {
+                        ipPrefix = vpnExtraRoute.getNexthopIpList().get(0) + NwConstants.IPV6PREFIX;
+                    }
+                    Prefixes localNextHopInfoLocal = fibUtil.getPrefixToInterface(vpnId,
+                            ipPrefix);
+                    if (localNextHopInfoLocal != null) {
+                        localNextHopSeen = true;
+                        BigInteger dpnId =
+                                checkCreateLocalFibEntry(localNextHopInfoLocal, localNextHopInfoLocal.getIpAddress(),
+                                        vpnId, rd, vrfEntry, vpnExtraRoute, vpnExtraRoutes, etherType);
+                        returnLocalDpnId.add(dpnId);
+                    }
                 }
+            } finally {
+                lock.unlock();
             }
             if (!localNextHopSeen && RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.SELF_IMPORTED) {
                 java.util.Optional<Long> optionalLabel = FibUtil.getLabelFromRoutePaths(vrfEntry);
                 if (optionalLabel.isPresent()) {
                     Long label = optionalLabel.get();
                     List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
-                    synchronized (label.toString().intern()) {
-                        LabelRouteInfo lri = getLabelRouteInfo(label);
+                    final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
+                    final ReentrantLock labelLock = lockFor(lriKey);
+                    labelLock.lock();
+                    try {
+                        LabelRouteInfo lri = getLabelRouteInfo(lriKey);
                         if (isPrefixAndNextHopPresentInLri(localNextHopIP, nextHopAddressList, lri)) {
                             Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
                                     fibUtil.getVpnInstanceOpData(rd);
                             if (vpnInstanceOpDataEntryOptional.isPresent()) {
                                 String vpnInstanceName = vpnInstanceOpDataEntryOptional.get().getVpnInstanceName();
-                                if (lri.getVpnInstanceList().contains(vpnInstanceName)) {
+                                if (lri.getVpnInstanceList() != null && lri.getVpnInstanceList().contains(
+                                       vpnInstanceName)) {
                                     localNextHopInfo = updateVpnReferencesInLri(lri, vpnInstanceName, true);
                                     localNextHopIP = lri.getPrefix();
                                 } else {
@@ -813,6 +844,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                 }
                             }
                         }
+                    } finally {
+                        labelLock.unlock();
                     }
                 }
             }
@@ -832,7 +865,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
     private BigInteger checkCreateLocalFibEntry(Prefixes localNextHopInfo, String localNextHopIP,
                                                 final Long vpnId, final String rd,
                                                 final VrfEntry vrfEntry,
-                                                Routes routes, List<Routes> vpnExtraRoutes,
+                                                @Nullable Routes routes, @Nullable List<Routes> vpnExtraRoutes,
                                                 int etherType) {
         String vpnName = fibUtil.getVpnNameFromId(vpnId);
         if (localNextHopInfo != null) {
@@ -859,7 +892,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
             String gwMacAddress = vrfEntry.getGatewayMacAddress();
             //The loadbalancing group is created only if the extra route has multiple nexthops
             //to avoid loadbalancing the discovered routes
-            if (vpnExtraRoutes != null && routes != null) {
+            if (RouteOrigin.STATIC.getValue().equals(vrfEntry.getOrigin()) && vpnExtraRoutes != null
+                    && routes != null) {
                 if (vpnExtraRoutes.size() > 1) {
                     groupId = nextHopManager.createNextHopGroups(vpnId, rd, dpnId, vrfEntry, routes, vpnExtraRoutes);
                     localGroupId = nextHopManager.getLocalSelectGroup(vpnId, vrfEntry.getDestPrefix());
@@ -919,15 +953,18 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
     private boolean isVpnPresentInDpn(String rd, BigInteger dpnId)  {
         InstanceIdentifier<VpnToDpnList> id = VpnHelper.getVpnToDpnListIdentifier(rd, dpnId);
         Optional<VpnToDpnList> dpnInVpn = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
-        if (dpnInVpn.isPresent()) {
-            return true;
-        }
-        return false;
+        return dpnInVpn.isPresent();
     }
 
+    @Nullable
     private LabelRouteInfo getLabelRouteInfo(Long label) {
+        return getLabelRouteInfo(new LabelRouteInfoKey(label));
+    }
+
+    @Nullable
+    private LabelRouteInfo getLabelRouteInfo(LabelRouteInfoKey label) {
         InstanceIdentifier<LabelRouteInfo> lriIid = InstanceIdentifier.builder(LabelRouteMap.class)
-            .child(LabelRouteInfo.class, new LabelRouteInfoKey(label)).build();
+            .child(LabelRouteInfo.class, label).build();
         Optional<LabelRouteInfo> opResult = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, lriIid);
         if (opResult.isPresent()) {
             return opResult.get();
@@ -936,7 +973,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
     }
 
     private boolean deleteLabelRouteInfo(LabelRouteInfo lri, String vpnInstanceName,
-            TypedWriteTransaction<Operational> tx) {
+            @Nullable TypedWriteTransaction<Operational> tx) {
         if (lri == null) {
             return true;
         }
@@ -1156,55 +1193,71 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         }
 
         String vpnName = fibUtil.getVpnNameFromId(vpnId);
-        LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}",
-                vrfEntry.getDestPrefix(), rd, remoteDpnId);
+        LOG.debug("createremotefibentry: adding route {} for rd {} on remoteDpnId {}", vrfEntry.getDestPrefix(), rd,
+                remoteDpnId);
 
-        List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
-        if (adjacencyResults.isEmpty()) {
-            LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}",
-                    vrfEntry.getRoutePaths(), rd, remoteDpnId);
-            LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
+        if (RouteOrigin.value(vrfEntry.getOrigin()) != RouteOrigin.STATIC) {
+            programRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, tx);
             return;
         }
-
+        // Handling static VRF entries
         List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
-        List<Routes> vpnExtraRoutes = VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker,
-                vpnName, usedRds, vrfEntry.getDestPrefix());
-        // create loadbalancing groups for extra routes only when the extra route is present behind
-        // multiple VMs
+        List<Routes> vpnExtraRoutes =
+                VpnExtraRouteHelper.getAllVpnExtraRoutes(dataBroker, vpnName, usedRds, vrfEntry.getDestPrefix());
         if (!vpnExtraRoutes.isEmpty()) {
-            List<InstructionInfo> instructions = new ArrayList<>();
-            // Obtain the local routes for this particular dpn.
-            java.util.Optional<Routes> routes = vpnExtraRoutes
-                    .stream()
-                    .filter(route -> {
-                        Prefixes prefixToInterface = fibUtil.getPrefixToInterface(vpnId,
-                                fibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
-                        if (prefixToInterface == null) {
-                            return false;
-                        }
-                        return remoteDpnId.equals(prefixToInterface.getDpnId());
-                    }).findFirst();
-            long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
-                    routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
-            if (groupId == FibConstants.INVALID_GROUP_ID) {
-                LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}",
-                        vrfEntry.getDestPrefix(), rd, remoteDpnId.toString());
-                return;
-            }
-            List<ActionInfo> actionInfos =
-                    Collections.singletonList(new ActionGroup(groupId));
-            instructions.add(new InstructionApplyActions(actionInfos));
-            baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
-                    NwConstants.ADD_FLOW, TransactionAdapter.toWriteTransaction(tx), null);
+            programRemoteFibWithLoadBalancingGroups(remoteDpnId, vpnId, rd, vrfEntry, vpnExtraRoutes);
         } else {
-            baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry,
-                TransactionAdapter.toWriteTransaction(tx), rd, adjacencyResults, null);
+            // Program in case of other static VRF entries like floating IPs
+            programRemoteFibEntry(remoteDpnId, vpnId, rd, vrfEntry, tx);
+        }
+    }
+
+    private void programRemoteFibWithLoadBalancingGroups(final BigInteger remoteDpnId, final long vpnId, String rd,
+            final VrfEntry vrfEntry, List<Routes> vpnExtraRoutes) {
+        // create loadbalancing groups for extra routes only when the extra route is
+        // present behind multiple VMs
+        // Obtain the local routes for this particular dpn.
+        java.util.Optional<Routes> routes = vpnExtraRoutes.stream().filter(route -> {
+            Prefixes prefixToInterface =
+                    fibUtil.getPrefixToInterface(vpnId, FibUtil.getIpPrefix(route.getNexthopIpList().get(0)));
+            if (prefixToInterface == null) {
+                return false;
+            }
+            return remoteDpnId.equals(prefixToInterface.getDpnId());
+        }).findFirst();
+        long groupId = nextHopManager.createNextHopGroups(vpnId, rd, remoteDpnId, vrfEntry,
+                routes.isPresent() ? routes.get() : null, vpnExtraRoutes);
+        if (groupId == FibConstants.INVALID_GROUP_ID) {
+            LOG.error("Unable to create Group for local prefix {} on rd {} on Node {}", vrfEntry.getDestPrefix(), rd,
+                    remoteDpnId);
+            return;
         }
+        List<ActionInfo> actionInfos = Collections.singletonList(new ActionGroup(groupId));
+        List<InstructionInfo> instructions = Lists.newArrayList(new InstructionApplyActions(actionInfos));
+        String jobKey = FibUtil.getCreateRemoteNextHopJobKey(vpnId, remoteDpnId, vrfEntry.getDestPrefix());
+        jobCoordinator.enqueueJob(jobKey,
+            () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(txn -> {
+                baseVrfEntryHandler.makeConnectedRoute(remoteDpnId, vpnId, vrfEntry, rd, instructions,
+                        NwConstants.ADD_FLOW, txn, null);
+            })));
 
         LOG.debug("Successfully added FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
     }
 
+    private void programRemoteFibEntry(final BigInteger remoteDpnId, final long vpnId, String rd,
+            final VrfEntry vrfEntry, TypedWriteTransaction<Configuration> tx) {
+        List<AdjacencyResult> adjacencyResults = baseVrfEntryHandler.resolveAdjacency(remoteDpnId, vpnId, vrfEntry, rd);
+        if (adjacencyResults.isEmpty()) {
+            LOG.error("Could not get interface for route-paths: {} in vpn {} on DPN {}", vrfEntry.getRoutePaths(), rd,
+                    remoteDpnId);
+            LOG.error("Failed to add Route: {} in vpn: {}", vrfEntry.getDestPrefix(), rd);
+            return;
+        }
+        baseVrfEntryHandler.programRemoteFib(remoteDpnId, vpnId, vrfEntry, TransactionAdapter.toWriteTransaction(tx),
+                rd, adjacencyResults, null);
+        LOG.debug("Successfully programmed FIB entry for prefix {} in vpnId {}", vrfEntry.getDestPrefix(), vpnId);
+    }
+
     protected void cleanUpOpDataForFib(Long vpnId, String primaryRd, final VrfEntry vrfEntry) {
     /* Get interface info from prefix to interface mapping;
         Use the interface info to get the corresponding vpn interface op DS entry,
@@ -1220,7 +1273,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
             List<String> usedRds = VpnExtraRouteHelper.getUsedRds(dataBroker, vpnId, vrfEntry.getDestPrefix());
             String usedRd = usedRds.isEmpty() ? primaryRd : usedRds.get(0);
             Routes extraRoute = baseVrfEntryHandler.getVpnToExtraroute(vpnId, usedRd, vrfEntry.getDestPrefix());
-            if (extraRoute != null) {
+            if (extraRoute != null && extraRoute.getNexthopIpList() != null) {
                 for (String nextHopIp : extraRoute.getNexthopIpList()) {
                     LOG.debug("NextHop IP for destination {} is {}", vrfEntry.getDestPrefix(), nextHopIp);
                     if (nextHopIp != null) {
@@ -1259,7 +1312,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
     }
 
     private void checkCleanUpOpDataForFib(final Prefixes prefixInfo, final Long vpnId, final String rd,
-                                          final VrfEntry vrfEntry, final Routes extraRoute) {
+                                          final VrfEntry vrfEntry, @Nullable final Routes extraRoute) {
 
         if (prefixInfo == null) {
             LOG.error("Cleanup VPN Data Failed as unable to find prefix Info for prefix {} VpnId {} rd {}",
@@ -1305,9 +1358,12 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                 if (VrfEntry.EncapType.Mplsgre.equals(vrfEntry.getEncapType())) {
                     FibUtil.getLabelFromRoutePaths(vrfEntry).ifPresent(label -> {
                         List<String> nextHopAddressList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
-                        synchronized (label.toString().intern()) {
-                            LabelRouteInfo lri = getLabelRouteInfo(label);
-                            if (lri != null && lri.getPrefix().equals(vrfEntry.getDestPrefix())
+                        final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
+                        final ReentrantLock lock = lockFor(lriKey);
+                        lock.lock();
+                        try {
+                            LabelRouteInfo lri = getLabelRouteInfo(lriKey);
+                            if (lri != null && Objects.equals(lri.getPrefix(), vrfEntry.getDestPrefix())
                                     && nextHopAddressList.contains(lri.getNextHopIpList().get(0))) {
                                 Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
                                         fibUtil.getVpnInstanceOpData(rd);
@@ -1325,6 +1381,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                 fibUtil.releaseId(FibConstants.VPN_IDPOOL_NAME, FibUtil.getNextHopLabelKey(
                                         rd, vrfEntry.getDestPrefix()));
                             }
+                        } finally {
+                            lock.unlock();
                         }
                     });
                 }
@@ -1365,10 +1423,6 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                         vrfEntry.getDestPrefix()));
                         txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx ->
                             configTx.delete(VpnExtraRouteHelper.getUsedRdsIdentifier(vpnId, vrfEntry.getDestPrefix())));
-                        nextHopManager.removeNextHopPointer(nextHopManager
-                                .getRemoteSelectGroupKey(vpnId, vrfEntry.getDestPrefix()));
-                        nextHopManager.removeNextHopPointer(nextHopManager
-                                .getLocalSelectGroupKey(vpnId, vrfEntry.getDestPrefix()));
                     }
                 }
                 handleAdjacencyAndVpnOpInterfaceDeletion(vrfEntry, ifName, vpnName, tx);
@@ -1409,8 +1463,9 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
             return;
         }
 
-        if (optAdjacencies.get().getAdjacency().stream().count() <= 2
-                && optAdjacencies.get().getAdjacency().stream().allMatch(adjacency ->
+        @NonNull List<Adjacency> adjacencies = optAdjacencies.get().nonnullAdjacency();
+        if (adjacencies.size() <= 2
+                && adjacencies.stream().allMatch(adjacency ->
                 adjacency.getAdjacencyType() == Adjacency.AdjacencyType.PrimaryAdjacency
                         && adjacency.isMarkedForDeletion() != null
                         && adjacency.isMarkedForDeletion()
@@ -1469,8 +1524,11 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                         })));
             }
             optionalLabel.ifPresent(label -> {
-                synchronized (label.toString().intern()) {
-                    LabelRouteInfo lri = getLabelRouteInfo(label);
+                final LabelRouteInfoKey lriKey = new LabelRouteInfoKey(label);
+                final ReentrantLock lock = lockFor(lriKey);
+                lock.lock();
+                try {
+                    LabelRouteInfo lri = getLabelRouteInfo(lriKey);
                     if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopAddressList, lri)) {
                         Optional<VpnInstanceOpDataEntry> vpnInstanceOpDataEntryOptional =
                                 fibUtil.getVpnInstanceOpData(rd);
@@ -1492,6 +1550,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                         LOG.trace("SUBNETROUTE: deleteFibEntries: Released subnetroute label {} for rd {} prefix {}",
                                 label, rd, vrfEntry.getDestPrefix());
                     }
+                } finally {
+                    lock.unlock();
                 }
             });
             return;
@@ -1506,7 +1566,6 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
             Optional<Routes> extraRouteOptional;
             //Is this fib route an extra route? If yes, get the nexthop which would be an adjacency in the vpn
             if (usedRds != null && !usedRds.isEmpty()) {
-                jobKey = FibUtil.getJobKeyForRdPrefix(usedRds.get(0), vrfEntry.getDestPrefix());
                 if (usedRds.size() > 1) {
                     LOG.error("The extra route prefix is still present in some DPNs");
                     return ;
@@ -1517,11 +1576,10 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                             .getVpnExtraroutes(dataBroker, vpnName, usedRds.get(0), vrfEntry.getDestPrefix());
                 }
             } else {
-                jobKey = FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix());
                 extraRouteOptional = Optional.absent();
             }
 
-            jobCoordinator.enqueueJob(jobKey,
+            jobCoordinator.enqueueJob(FibUtil.getJobKeyForRdPrefix(rd, vrfEntry.getDestPrefix()),
                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
                     if (localDpnIdList.size() <= 0) {
                         for (VpnToDpnList curDpn : vpnToDpnList) {
@@ -1532,7 +1590,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                     } else {
                         for (BigInteger localDpnId : localDpnIdList) {
                             for (VpnToDpnList curDpn : vpnToDpnList) {
-                                if (!curDpn.getDpnId().equals(localDpnId)) {
+                                if (!Objects.equals(curDpn.getDpnId(), localDpnId)) {
                                     baseVrfEntryHandler.deleteRemoteRoute(localDpnId, curDpn.getDpnId(),
                                         vpnInstance.getVpnId(), vrfTableKey, vrfEntry, extraRouteOptional,
                                         TransactionAdapter.toWriteTransaction(tx));
@@ -1540,6 +1598,13 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                             }
                         }
                     }
+                    if (extraRouteOptional.isPresent()) {
+                        //Remove select groups only for extra-routes
+                        nextHopManager.removeNextHopPointer(nextHopManager
+                                .getRemoteSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
+                        nextHopManager.removeNextHopPointer(nextHopManager
+                                .getLocalSelectGroupKey(vpnInstance.getVpnId(), vrfEntry.getDestPrefix()));
+                    }
                 })), MAX_RETRIES);
         }
 
@@ -1568,8 +1633,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
 
     }
 
-    private void makeLFibTableEntry(BigInteger dpId, long label, List<InstructionInfo> instructions, int priority,
-                                    int addOrRemove, TypedWriteTransaction<Configuration> tx) {
+    private void makeLFibTableEntry(BigInteger dpId, long label, @Nullable List<InstructionInfo> instructions,
+                                    int priority, int addOrRemove, TypedWriteTransaction<Configuration> tx) {
         if (tx == null) {
             ListenableFutures.addErrorLogging(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
                 newTx -> makeLFibTableEntry(dpId, label, instructions, priority, addOrRemove, newTx)), LOG,
@@ -1623,9 +1688,12 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                     }
                     return futures;
                 }
-                synchronized (vpnInstance.getVpnInstanceName().intern()) {
+
+                final ReentrantLock lock = lockFor(vpnInstance);
+                lock.lock();
+                try {
                     futures.add(retryingTxRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, tx -> {
-                        for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
+                        for (final VrfEntry vrfEntry : vrfTable.get().nonnullVrfEntry()) {
                             SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
                             if (subnetRoute != null) {
                                 long elanTag = subnetRoute.getElantag();
@@ -1650,7 +1718,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                     List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
                                     LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
                                     if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList, lri)) {
-                                        if (lri.getDpnId().equals(dpnId)) {
+                                        if (Objects.equals(lri.getDpnId(), dpnId)) {
                                             try {
                                                 int etherType = NWUtil.getEtherTypeFromIpPrefix(
                                                         vrfEntry.getDestPrefix());
@@ -1684,6 +1752,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                         ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
                         Futures.addCallback(listenableFuture, callback, MoreExecutors.directExecutor());
                     }
+                } finally {
+                    lock.unlock();
                 }
                 return futures;
             });
@@ -1700,11 +1770,15 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         if (vrfTable.isPresent()) {
             jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
                 () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
-                    synchronized (vpnInstance.getVpnInstanceName().intern()) {
-                        vrfTable.get().getVrfEntry().stream()
+                    final ReentrantLock lock = lockFor(vpnInstance);
+                    lock.lock();
+                    try {
+                        vrfTable.get().nonnullVrfEntry().stream()
                             .filter(vrfEntry -> RouteOrigin.BGP == RouteOrigin.value(vrfEntry.getOrigin()))
                             .forEach(bgpRouteVrfEntryHandler.getConsumerForCreatingRemoteFib(dpnId, vpnId,
                                 rd, remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx), txnObjects));
+                    } finally {
+                        lock.unlock();
                     }
                 })));
         }
@@ -1726,7 +1800,9 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
 
         jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, localDpnId),
             () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
-                synchronized (vpnInstance.getVpnInstanceName().intern()) {
+                final ReentrantLock lock = lockFor(vpnInstance);
+                lock.lock();
+                try {
                     VrfTablesKey vrfTablesKey = new VrfTablesKey(rd);
                     VrfEntry vrfEntry = getVrfEntry(dataBroker, rd, destPrefix);
                     if (vrfEntry == null) {
@@ -1759,7 +1835,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                         //Is this fib route an extra route? If yes, get the nexthop which would be
                         //an adjacency in the vpn
                         Optional<Routes> extraRouteOptional = Optional.absent();
-                        if (usedRds.size() != 0) {
+                        if (RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.STATIC && usedRds.size() != 0) {
                             extraRouteOptional = VpnExtraRouteHelper.getVpnExtraroutes(dataBroker,
                                     fibUtil.getVpnNameFromId(vpnInstance.getVpnId()),
                                     usedRds.get(0), vrfEntry.getDestPrefix());
@@ -1767,6 +1843,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                         baseVrfEntryHandler.deleteRemoteRoute(null, localDpnId, vpnId, vrfTablesKey, modVrfEntry,
                                 extraRouteOptional, TransactionAdapter.toWriteTransaction(tx));
                     }
+                } finally {
+                    lock.unlock();
                 }
             })));
     }
@@ -1783,14 +1861,22 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                         LogicalDatastoreType.CONFIGURATION, id);
                 List<ListenableFuture<Void>> futures = new ArrayList<>();
                 if (vrfTable.isPresent()) {
-                    synchronized (vpnInstance.getVpnInstanceName().intern()) {
+                    final ReentrantLock lock = lockFor(vpnInstance);
+                    lock.lock();
+                    try {
                         futures.add(retryingTxRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
                             String vpnName = fibUtil.getVpnNameFromId(vpnInstance.getVpnId());
-                            for (final VrfEntry vrfEntry : vrfTable.get().getVrfEntry()) {
+                            for (final VrfEntry vrfEntry : vrfTable.get().nonnullVrfEntry()) {
+                                /* parentRd is only filled for external PNF cases where the interface on the external
+                                 * network VPN are used to cleanup the flows. For all other cases, use "rd" for
+                                 * #fibUtil.isInterfacePresentInDpn().
+                                * */
+                                String parentRd = vrfEntry.getParentVpnRd() != null ? vrfEntry.getParentVpnRd()
+                                        : rd;
                                 /* Handle subnet routes here */
                                 SubnetRoute subnetRoute = vrfEntry.augmentation(SubnetRoute.class);
                                 if (subnetRoute != null && !fibUtil
-                                        .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
+                                        .isInterfacePresentInDpn(parentRd, dpnId)) {
                                     LOG.trace("SUBNETROUTE: cleanUpDpnForVpn: Cleaning subnetroute {} on dpn {}"
                                             + " for vpn {}", vrfEntry.getDestPrefix(), dpnId, rd);
                                     baseVrfEntryHandler.makeConnectedRoute(dpnId, vpnId, vrfEntry, rd, null,
@@ -1829,7 +1915,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                         List<String> nextHopList = FibHelper.getNextHopListFromRoutePaths(vrfEntry);
                                         LabelRouteInfo lri = getLabelRouteInfo(optionalLabel.get());
                                         if (isPrefixAndNextHopPresentInLri(vrfEntry.getDestPrefix(), nextHopList,
-                                                lri) && lri.getDpnId().equals(dpnId)) {
+                                                lri) && Objects.equals(lri.getDpnId(), dpnId)) {
                                             deleteLocalFibEntry(vpnId, rd, vrfEntry);
                                         }
                                     }
@@ -1860,7 +1946,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                         TransactionAdapter.toWriteTransaction(tx), txnObjects);
                                 } else {
                                     if (subnetRoute == null || !fibUtil
-                                            .isInterfacePresentInDpn(vrfEntry.getParentVpnRd(), dpnId)) {
+                                            .isInterfacePresentInDpn(parentRd, dpnId)) {
                                         baseVrfEntryHandler.deleteRemoteRoute(null, dpnId, vpnId,
                                             vrfTable.get().key(), vrfEntry, extraRouteOptional,
                                             TransactionAdapter.toWriteTransaction(tx));
@@ -1868,6 +1954,8 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                                 }
                             }
                         }));
+                    } finally {
+                        lock.unlock();
                     }
                     if (callback != null) {
                         ListenableFuture<List<Void>> listenableFuture = Futures.allAsList(futures);
@@ -1893,14 +1981,18 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         if (vrfTable.isPresent()) {
             jobCoordinator.enqueueJob(FibUtil.getJobKeyForVpnIdDpnId(vpnId, dpnId),
                 () -> {
-                    synchronized (vpnInstance.getVpnInstanceName().intern()) {
+                    final ReentrantLock lock = lockFor(vpnInstance);
+                    lock.lock();
+                    try {
                         return Collections.singletonList(
                             txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
-                                tx -> vrfTable.get().getVrfEntry().stream()
+                                tx -> vrfTable.get().nonnullVrfEntry().stream()
                                     .filter(vrfEntry -> RouteOrigin.value(vrfEntry.getOrigin()) == RouteOrigin.BGP)
                                     .forEach(bgpRouteVrfEntryHandler.getConsumerForDeletingRemoteFib(dpnId, vpnId,
                                         remoteNextHopIp, vrfTable, TransactionAdapter.toWriteTransaction(tx),
                                         txnObjects))));
+                    } finally {
+                        lock.unlock();
                     }
                 });
         }
@@ -1922,6 +2014,7 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
                 + tableMiss + FLOWID_PREFIX;
     }
 
+    @Nullable
     private VrfEntry getVrfEntry(DataBroker broker, String rd, String ipPrefix) {
         InstanceIdentifier<VrfEntry> vrfEntryId = InstanceIdentifier.builder(FibEntries.class)
             .child(VrfTables.class, new VrfTablesKey(rd))
@@ -1982,9 +2075,9 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         });
     }
 
-    private boolean isPrefixAndNextHopPresentInLri(String prefix,
+    private static boolean isPrefixAndNextHopPresentInLri(String prefix,
             List<String> nextHopAddressList, LabelRouteInfo lri) {
-        return lri != null && lri.getPrefix().equals(prefix)
+        return lri != null && Objects.equals(lri.getPrefix(), prefix)
                 && nextHopAddressList.contains(lri.getNextHopIpList().get(0));
     }
 
@@ -2004,4 +2097,13 @@ public class VrfEntryListener extends AsyncDataTreeChangeListenerBase<VrfEntry,
         }
         return true;
     }
+
+    private static ReentrantLock lockFor(final VpnInstanceOpDataEntry vpnInstance) {
+        // FIXME: use vpnInstance.key() instead?
+        return JvmGlobalLocks.getLockForString(vpnInstance.getVpnInstanceName());
+    }
+
+    private static ReentrantLock lockFor(LabelRouteInfoKey label) {
+        return JvmGlobalLocks.getLockFor(label);
+    }
 }