Remove GENIUS UTIL references in FibManager Module
[netvirt.git] / fibmanager / impl / src / main / java / org / opendaylight / netvirt / fibmanager / NexthopManager.java
index 4747b80f6d4b7172e4719ec60ade70825168c73d..6cc279fb16eb6b6dad848b64c818b43f67b33c24 100644 (file)
@@ -7,26 +7,33 @@
  */
 package org.opendaylight.netvirt.fibmanager;
 
+import static java.util.stream.Collectors.toList;
 import static org.opendaylight.genius.mdsalutil.NWUtil.isIpv4Address;
+import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
+import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
 
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-import java.math.BigInteger;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 import javax.inject.Singleton;
-import org.opendaylight.controller.md.sal.binding.api.DataBroker;
-import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.genius.datastoreutils.SingleTransactionDataBroker;
+import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
 import org.opendaylight.genius.itm.globals.ITMConstants;
 import org.opendaylight.genius.mdsalutil.ActionInfo;
 import org.opendaylight.genius.mdsalutil.BucketInfo;
@@ -46,17 +53,20 @@ import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldTunnelId;
 import org.opendaylight.genius.mdsalutil.actions.ActionSetFieldVlanVid;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.mdsal.binding.api.DataBroker;
+import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
+import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
+import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
+import org.opendaylight.mdsal.common.api.ReadFailedException;
 import org.opendaylight.netvirt.elanmanager.api.IElanService;
+import org.opendaylight.netvirt.fibmanager.api.FibHelper;
 import org.opendaylight.netvirt.fibmanager.api.L3VPNTransportTypes;
 import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.L2vlan;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev140508.Tunnel;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.L2vlan;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.iana._if.type.rev170119.Tunnel;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfaceType;
-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.MacAddress;
-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.action.types.rev131112.action.action.OutputActionCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetFieldCase;
@@ -66,9 +76,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.AllocateIdOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.CreateIdPoolOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.ReleaseIdOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeBase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeGre;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
@@ -80,11 +92,16 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.Tun
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TunnelsState;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelList;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.tunnels_state.StateTunnelListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.DcGatewayIpList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.dc.gateway.ip.list.DcGatewayIp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetEgressActionsForTunnelOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetInternalOrExternalInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.GetTunnelInterfaceNameOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.lockmanager.rev160413.LockManagerService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupOutput;
@@ -107,13 +124,14 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthop;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.VpnNexthopKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacencies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3nexthop.rev150409.l3nexthop.vpnnexthops.vpnnexthop.IpAdjacenciesKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpn;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.ConfTransportTypeL3vpnBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.dpid.l3vpn.lb.nexthops.DpnLbNexthops;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.l3vpn.lb.nexthops.Nexthops;
 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.vpn.to.extraroutes.vpn.extra.routes.Routes;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg6;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.add.group.input.buckets.bucket.action.action.NxActionResubmitRpcAddGroupCase;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionRegLoadNodesNodeTableFlowApplyActionsCase;
@@ -121,6 +139,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.ni
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
 import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.Uint32;
+import org.opendaylight.yangtools.yang.common.Uint64;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -129,16 +149,24 @@ public class NexthopManager implements AutoCloseable {
     private static final Logger LOG = LoggerFactory.getLogger(NexthopManager.class);
     private static final String NEXTHOP_ID_POOL_NAME = "nextHopPointerPool";
     private static final long WAIT_TIME_FOR_SYNC_INSTALL = Long.getLong("wait.time.sync.install", 300L);
+    //  We set the total wait time for lock to be obtained at 9 seconds since GC pauses can be upto 8 seconds
+    //in scale setups.
+    private static final long WAIT_TIME_TO_ACQUIRE_LOCK = 9000L;
+    private static final int SELECT_GROUP_WEIGHT = 1;
+    private static final int RETRY_COUNT = 6;
 
     private final DataBroker dataBroker;
+    private final ManagedNewTransactionRunner txRunner;
     private final IMdsalApiManager mdsalApiManager;
-    private final OdlInterfaceRpcService interfaceManager;
+    private final OdlInterfaceRpcService odlInterfaceRpcService;
     private final ItmRpcService itmManager;
     private final IdManagerService idManager;
     private final IElanService elanService;
+    private final LockManagerService lockManager;
     private final SalGroupService salGroupService;
     private final JobCoordinator jobCoordinator;
     private final FibUtil fibUtil;
+    private final IInterfaceManager interfaceManager;
     private volatile L3VPNTransportTypes configuredTransportTypeL3VPN = L3VPNTransportTypes.Invalid;
 
     /**
@@ -148,28 +176,33 @@ public class NexthopManager implements AutoCloseable {
      * @param dataBroker       - dataBroker reference
      * @param mdsalApiManager  - mdsalApiManager reference
      * @param idManager        - idManager reference
-     * @param interfaceManager - interfaceManager reference
+     * @param odlInterfaceRpcService - odlInterfaceRpcService reference
      * @param itmManager       - itmManager reference
      */
     @Inject
     public NexthopManager(final DataBroker dataBroker,
                           final IMdsalApiManager mdsalApiManager,
                           final IdManagerService idManager,
-                          final OdlInterfaceRpcService interfaceManager,
+                          final OdlInterfaceRpcService odlInterfaceRpcService,
                           final ItmRpcService itmManager,
+                          final LockManagerService lockManager,
                           final IElanService elanService,
                           final SalGroupService salGroupService,
                           final JobCoordinator jobCoordinator,
-                          final FibUtil fibUtil) {
+                          final FibUtil fibUtil,
+                          final IInterfaceManager interfaceManager) {
         this.dataBroker = dataBroker;
+        this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
         this.mdsalApiManager = mdsalApiManager;
         this.idManager = idManager;
-        this.interfaceManager = interfaceManager;
+        this.odlInterfaceRpcService = odlInterfaceRpcService;
         this.itmManager = itmManager;
         this.elanService = elanService;
         this.salGroupService = salGroupService;
         this.jobCoordinator = jobCoordinator;
         this.fibUtil = fibUtil;
+        this.lockManager = lockManager;
+        this.interfaceManager = interfaceManager;
         createIdPool();
     }
 
@@ -180,7 +213,7 @@ public class NexthopManager implements AutoCloseable {
             .setHigh(175000L)
             .build();
         try {
-            Future<RpcResult<Void>> result = idManager.createIdPool(createPool);
+            Future<RpcResult<CreateIdPoolOutput>> result = idManager.createIdPool(createPool);
             if (result != null && result.get().isSuccessful()) {
                 LOG.info("Created IdPool for NextHopPointerPool");
             }
@@ -189,12 +222,20 @@ public class NexthopManager implements AutoCloseable {
         }
     }
 
-    private String getNextHopKey(long vpnId, String ipAddress) {
+    private String getNextHopKey(Uint32 vpnId, String ipAddress) {
         return "nexthop." + vpnId + ipAddress;
     }
 
-    public OdlInterfaceRpcService getInterfaceManager() {
-        return interfaceManager;
+    String getRemoteSelectGroupKey(Uint32 vpnId, String ipAddress) {
+        return "remote.ecmp.nexthop." + vpnId + ipAddress;
+    }
+
+    String getLocalSelectGroupKey(Uint32 vpnId, String ipAddress) {
+        return "local.ecmp.nexthop." + vpnId + ipAddress;
+    }
+
+    public ItmRpcService getItmManager() {
+        return itmManager;
     }
 
     protected long createNextHopPointer(String nexthopKey) {
@@ -205,9 +246,10 @@ public class NexthopManager implements AutoCloseable {
         try {
             Future<RpcResult<AllocateIdOutput>> result = idManager.allocateId(getIdInput);
             RpcResult<AllocateIdOutput> rpcResult = result.get();
-            return rpcResult.getResult().getIdValue();
+            return rpcResult.getResult().getIdValue().toJava();
         } catch (NullPointerException | InterruptedException | ExecutionException e) {
-            LOG.trace("", e);
+            // FIXME: NPEs should not be caught but rather their root cause should be eliminated
+            LOG.trace("Failed to allocate {}", getIdInput, e);
         }
         return 0;
     }
@@ -217,8 +259,7 @@ public class NexthopManager implements AutoCloseable {
             .setPoolName(NEXTHOP_ID_POOL_NAME)
             .setIdKey(nexthopKey).build();
         try {
-            Future<RpcResult<Void>> result = idManager.releaseId(idInput);
-            RpcResult<Void> rpcResult = result.get();
+            RpcResult<ReleaseIdOutput> rpcResult = idManager.releaseId(idInput).get();
             if (!rpcResult.isSuccessful()) {
                 LOG.error("RPC Call to Get Unique Id for nexthopKey {} returned with Errors {}",
                         nexthopKey, rpcResult.getErrors());
@@ -228,53 +269,72 @@ public class NexthopManager implements AutoCloseable {
         }
     }
 
-    protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey) {
-        List<ActionInfo> listActionInfo = new ArrayList<>();
+    protected List<ActionInfo> getEgressActionsForInterface(final String ifName, int actionKey,
+                                                            boolean isTunnelInterface,
+                                                            Uint32 vpnId, String destIpPrefix) {
+        List<Action> actions;
         try {
-            Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
-                interfaceManager.getEgressActionsForInterface(
-                    new GetEgressActionsForInterfaceInputBuilder().setIntfName(ifName).build());
-            RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
-            if (!rpcResult.isSuccessful()) {
-                LOG.error("RPC Call to Get egress actions for interface {} returned with Errors {}",
-                    ifName, rpcResult.getErrors());
+            if (isTunnelInterface && interfaceManager.isItmDirectTunnelsEnabled()) {
+                RpcResult<GetEgressActionsForTunnelOutput> rpcResult =
+                        itmManager.getEgressActionsForTunnel(new GetEgressActionsForTunnelInputBuilder()
+                                .setIntfName(ifName).build()).get();
+                if (!rpcResult.isSuccessful()) {
+                    LOG.error("RPC Call to Get egress tunnel actions for interface {} returned with Errors {}",
+                            ifName, rpcResult.getErrors());
+                    return Collections.emptyList();
+                } else {
+                    actions = new ArrayList<Action>(rpcResult.getResult().nonnullAction().values());
+                }
             } else {
-                List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action> actions =
-                    rpcResult.getResult().getAction();
-                for (Action action : actions) {
-                    actionKey = action.getKey().getOrder() + actionKey;
-                    org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
-                        actionClass = action.getAction();
-                    if (actionClass instanceof OutputActionCase) {
-                        listActionInfo.add(new ActionOutput(actionKey,
-                            ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
-                    } else if (actionClass instanceof PushVlanActionCase) {
-                        listActionInfo.add(new ActionPushVlan(actionKey));
-                    } else if (actionClass instanceof SetFieldCase) {
-                        if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
-                            int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
-                                .getVlanId().getVlanId().getValue();
-                            listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
-                        }
-                    } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
-                        Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable();
-                        listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
-                    } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
-                        NxRegLoad nxRegLoad =
-                            ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
-                        listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
-                            nxRegLoad.getDst().getStart(), nxRegLoad.getDst().getEnd(),
-                            nxRegLoad.getValue().longValue()));
+                RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = odlInterfaceRpcService
+                        .getEgressActionsForInterface(new GetEgressActionsForInterfaceInputBuilder()
+                                .setIntfName(ifName).build()).get();
+                if (!rpcResult.isSuccessful()) {
+                    LOG.error("RPC Call to Get egress vm actions for interface {} vpnId {} ipPrefix {} returned with "
+                                    + "Errors {}", ifName, vpnId, destIpPrefix, rpcResult.getErrors());
+                    return Collections.emptyList();
+                } else {
+                    actions = new ArrayList<Action>(rpcResult.getResult().nonnullAction().values());
+                }
+            }
+            List<ActionInfo> listActionInfo = new ArrayList<>();
+            for (Action action : actions) {
+                actionKey = action.key().getOrder() + actionKey;
+                org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action
+                    actionClass = action.getAction();
+                if (actionClass instanceof OutputActionCase) {
+                    listActionInfo.add(new ActionOutput(actionKey,
+                        ((OutputActionCase) actionClass).getOutputAction().getOutputNodeConnector()));
+                } else if (actionClass instanceof PushVlanActionCase) {
+                    listActionInfo.add(new ActionPushVlan(actionKey));
+                } else if (actionClass instanceof SetFieldCase) {
+                    if (((SetFieldCase) actionClass).getSetField().getVlanMatch() != null) {
+                        int vlanVid = ((SetFieldCase) actionClass).getSetField().getVlanMatch()
+                            .getVlanId().getVlanId().getValue().toJava();
+                        listActionInfo.add(new ActionSetFieldVlanVid(actionKey, vlanVid));
                     }
+                } else if (actionClass instanceof NxActionResubmitRpcAddGroupCase) {
+                    Short tableId = ((NxActionResubmitRpcAddGroupCase) actionClass).getNxResubmit().getTable().toJava();
+                    listActionInfo.add(new ActionNxResubmit(actionKey, tableId));
+                } else if (actionClass instanceof NxActionRegLoadNodesNodeTableFlowApplyActionsCase) {
+                    NxRegLoad nxRegLoad =
+                        ((NxActionRegLoadNodesNodeTableFlowApplyActionsCase) actionClass).getNxRegLoad();
+                    listActionInfo.add(new ActionRegLoad(actionKey, NxmNxReg6.class,
+                        nxRegLoad.getDst().getStart().toJava(), nxRegLoad.getDst().getEnd().toJava(),
+                        nxRegLoad.getValue().longValue()));
                 }
             }
-        } catch (InterruptedException | ExecutionException e) {
-            LOG.warn("Exception when egress actions for interface {}", ifName, e);
+            return listActionInfo;
+        } catch (InterruptedException | ExecutionException | NullPointerException e) {
+            LOG.error("Exception when egress actions for interface {} isTunnel {} vpnId {} ipPrefix {}", ifName,
+                    isTunnelInterface, vpnId, destIpPrefix, e);
         }
-        return listActionInfo;
+        LOG.warn("Exception when egress actions for interface {}", ifName);
+        return Collections.emptyList();
     }
 
-    protected String getTunnelInterfaceName(BigInteger srcDpId, BigInteger dstDpId) {
+    @Nullable
+    protected String getTunnelInterfaceName(Uint64 srcDpId, Uint64 dstDpId) {
         Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
         Future<RpcResult<GetTunnelInterfaceNameOutput>> result;
         try {
@@ -295,15 +355,17 @@ public class NexthopManager implements AutoCloseable {
         return null;
     }
 
-    protected String getTunnelInterfaceName(BigInteger srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
-        .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp) {
-        Class<? extends TunnelTypeBase> tunType = getReqTunType(getReqTransType().toUpperCase(Locale.getDefault()));
+    @Nullable
+    protected String getTunnelInterfaceName(Uint64 srcDpId, org.opendaylight.yang.gen.v1.urn.ietf.params
+        .xml.ns.yang.ietf.inet.types.rev130715.IpAddress dstIp, Class<? extends TunnelTypeBase> tunnelType) {
         Future<RpcResult<GetInternalOrExternalInterfaceNameOutput>> result;
         try {
+            LOG.debug("Trying to fetch tunnel interface name for source dpn {} destIp {} tunType {}", srcDpId,
+                    dstIp.stringValue(), tunnelType.getName());
             result = itmManager.getInternalOrExternalInterfaceName(new GetInternalOrExternalInterfaceNameInputBuilder()
                 .setSourceDpid(srcDpId)
                 .setDestinationIp(dstIp)
-                .setTunnelType(tunType)
+                .setTunnelType(tunnelType)
                 .build());
             RpcResult<GetInternalOrExternalInterfaceNameOutput> rpcResult = result.get();
             if (!rpcResult.isSuccessful()) {
@@ -312,13 +374,13 @@ public class NexthopManager implements AutoCloseable {
                 return rpcResult.getResult().getInterfaceName();
             }
         } catch (InterruptedException | ExecutionException e) {
-            LOG.warn("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstIp, e);
+            LOG.error("Exception when getting tunnel interface Id for tunnel between {} and  {}", srcDpId, dstIp, e);
         }
         return null;
     }
 
 
-    public long getLocalNextHopGroup(long vpnId,
+    public long getLocalNextHopGroup(Uint32 vpnId,
             String ipNextHopAddress) {
         long groupId = createNextHopPointer(getNextHopKey(vpnId, ipNextHopAddress));
         if (groupId == FibConstants.INVALID_GROUP_ID) {
@@ -327,81 +389,108 @@ public class NexthopManager implements AutoCloseable {
         return groupId;
     }
 
-    public long createLocalNextHop(long vpnId, BigInteger dpnId, String ifName,
-            String ipNextHopAddress, String ipPrefixAddress, String gwMacAddress, String jobKey) {
-        String vpnName = fibUtil.getVpnNameFromId(vpnId);
+    public long getLocalSelectGroup(Uint32 vpnId,
+            String ipNextHopAddress) {
+        long groupId = createNextHopPointer(getLocalSelectGroupKey(vpnId, ipNextHopAddress));
+        if (groupId == FibConstants.INVALID_GROUP_ID) {
+            LOG.error("Unable to allocate groupId for vpnId {} , prefix {}", vpnId, ipNextHopAddress);
+        }
+        return groupId;
+    }
+
+    public long createLocalNextHop(Uint32 vpnId, Uint64 dpnId, String ifName,
+                                   String primaryIpAddress, String currDestIpPrefix,
+                                   String gwMacAddress, Uint32 parentVpnId) {
+        //For VPN Imported routes, getting VPN Instance name using parentVpnId
+        String vpnName = parentVpnId != null ? fibUtil.getVpnNameFromId(parentVpnId) : fibUtil.getVpnNameFromId(vpnId);
         if (vpnName == null) {
             return 0;
         }
-        String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, ipPrefixAddress);
-        String ipAddress = macAddress != null ? ipPrefixAddress : ipNextHopAddress;
-        long groupId = createNextHopPointer(getNextHopKey(vpnId, ipAddress));
+        String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress);
+
+        long groupId = createNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
         if (groupId == 0) {
-            LOG.error("Unable to allocate groupId for vpnId {} , prefix {}  IntfName {}, nextHopAddr {}",
-                    vpnId, ipAddress, ifName, ipNextHopAddress);
+            LOG.error("Unable to allocate groupId for vpnId {} , IntfName {}, primaryIpAddress {} curIpPrefix {}",
+                    vpnId, ifName, primaryIpAddress, currDestIpPrefix);
             return groupId;
         }
-        String nextHopLockStr = vpnId + ipAddress;
+        String nextHopLockStr = vpnId + primaryIpAddress;
+        String jobKey = FibUtil.getCreateLocalNextHopJobKey(vpnId, dpnId, currDestIpPrefix);
         jobCoordinator.enqueueJob(jobKey, () -> {
-            synchronized (nextHopLockStr.intern()) {
-                VpnNexthop nexthop = getVpnNexthop(vpnId, ipAddress);
-                LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
-                        ipAddress, ifName, dpnId);
-                if (nexthop == null) {
-                    String encMacAddress = macAddress == null
-                        ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, ipAddress) : macAddress;
-                    List<BucketInfo> listBucketInfo = new ArrayList<>();
-                    List<ActionInfo> listActionInfo = new ArrayList<>();
-                    int actionKey = 0;
-                    // MAC re-write
-                    if (encMacAddress != null) {
-                        if (gwMacAddress != null) {
-                            LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
-                                    gwMacAddress, ifName, vpnId);
-                            listActionInfo
-                                    .add(new ActionSetFieldEthernetSource(actionKey++, new MacAddress(gwMacAddress)));
+            try {
+                if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
+                    VpnNexthop nexthop = getVpnNexthop(vpnId, primaryIpAddress);
+                    LOG.trace("nexthop: {} retrieved for vpnId {}, prefix {}, ifName {} on dpn {}", nexthop, vpnId,
+                            primaryIpAddress, ifName, dpnId);
+                    if (nexthop == null) {
+                        String encMacAddress = macAddress == null
+                                ? fibUtil.getMacAddressFromPrefix(ifName, vpnName, primaryIpAddress) : macAddress;
+                        List<ActionInfo> listActionInfo = new ArrayList<>();
+                        int actionKey = 0;
+                        // MAC re-write
+                        if (encMacAddress != null) {
+                            if (gwMacAddress != null) {
+                                LOG.trace("The Local NextHop Group Source Mac {} for VpnInterface {} on VPN {}",
+                                        gwMacAddress, ifName, vpnId);
+                                listActionInfo.add(new ActionSetFieldEthernetSource(actionKey++,
+                                        new MacAddress(gwMacAddress)));
+                            }
+                            listActionInfo.add(new ActionSetFieldEthernetDestination(actionKey++,
+                                    new MacAddress(encMacAddress)));
+                            // listActionInfo.add(0, new ActionPopMpls());
+                        } else {
+                            LOG.error("mac address for new local nexthop group {} is null for vpnId {}, prefix {}, "
+                                    + "ifName {} on dpn {}", groupId, vpnId, primaryIpAddress, ifName, dpnId);
+                        }
+                        List<ActionInfo> nhActionInfoList = getEgressActionsForInterface(ifName, actionKey, false,
+                                vpnId, currDestIpPrefix);
+                        if (nhActionInfoList.isEmpty()) {
+                            LOG.error("createLocalNextHop: Skipping, Empty list of egress actions received for "
+                                    + "interface {} on dpn {} for vpn {} prefix {}", ifName, dpnId, vpnId,
+                                    currDestIpPrefix);
                         }
-                        listActionInfo
-                                .add(new ActionSetFieldEthernetDestination(actionKey++, new MacAddress(encMacAddress)));
-                        // listActionInfo.add(0, new ActionPopMpls());
+                        listActionInfo.addAll(nhActionInfoList);
+                        BucketInfo bucket = new BucketInfo(listActionInfo);
+                        List<BucketInfo> listBucketInfo = new ArrayList<>();
+                        listBucketInfo.add(bucket);
+                        GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, primaryIpAddress,
+                                GroupTypes.GroupAll, listBucketInfo);
+                        LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
+                                encMacAddress, ifName, primaryIpAddress);
+                        //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
+                        // group to get installed before programming the flows
+                        installGroupOnDpn(groupId, dpnId, primaryIpAddress, listBucketInfo,
+                                getNextHopKey(vpnId, primaryIpAddress), GroupTypes.GroupAll);
+                        // install Group
+                        mdsalApiManager.syncInstallGroup(groupEntity);
+                        // update MD-SAL DS
+                        addVpnNexthopToDS(dpnId, vpnId, primaryIpAddress, currDestIpPrefix, groupId);
+
                     } else {
-                        // FIXME: Log message here.
-                        LOG.debug("mac address for new local nexthop is null");
+                        // Ignore adding new prefix , if it already exists
+                        Map<IpAdjacenciesKey, IpAdjacencies> keyIpAdjacenciesMap = nexthop.getIpAdjacencies();
+                        IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
+                        if (keyIpAdjacenciesMap != null && keyIpAdjacenciesMap.values().contains(prefix)) {
+                            LOG.trace("Prefix {} is already present in l3nextHop {} ", currDestIpPrefix, nexthop);
+                        } else {
+                            IpAdjacenciesBuilder ipPrefixesBuilder =
+                                    new IpAdjacenciesBuilder().withKey(new IpAdjacenciesKey(currDestIpPrefix));
+                            LOG.trace("Updating prefix {} to vpnNextHop {} Operational DS", currDestIpPrefix, nexthop);
+                            MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                                    getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix),
+                                    ipPrefixesBuilder.build());
+                        }
                     }
-                    listActionInfo.addAll(getEgressActionsForInterface(ifName, actionKey));
-                    BucketInfo bucket = new BucketInfo(listActionInfo);
-
-                    listBucketInfo.add(bucket);
-                    GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, groupId, ipAddress, GroupTypes.GroupAll,
-                            listBucketInfo);
-                    LOG.trace("Install LNH Group: id {}, mac address {}, interface {} for prefix {}", groupId,
-                            encMacAddress, ifName, ipAddress);
-                    //Try to install group directly on the DPN bypassing the FRM, in order to avoid waiting for the
-                    // group to get installed before programming the flows
-                    installGroupOnDpn(groupId, dpnId, ipAddress, listBucketInfo, getNextHopKey(vpnId, ipAddress),
-                            GroupTypes.GroupAll);
-                    // install Group
-                    mdsalApiManager.syncInstallGroup(groupEntity);
-                    // update MD-SAL DS
-                    addVpnNexthopToDS(dpnId, vpnId, ipAddress, groupId);
-
-                } else {
-                    // nexthop exists already; a new flow is going to point to
-                    // it, increment the flowrefCount by 1
-                    int flowrefCnt = nexthop.getFlowrefCount() + 1;
-                    VpnNexthop nh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress))
-                            .setFlowrefCount(flowrefCnt).build();
-                    LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", nh, flowrefCnt);
-                    MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId,
-                            ipAddress), nh);
                 }
+            } finally {
+                FibUtil.unlockCluster(lockManager, nextHopLockStr);
             }
             return Collections.emptyList();
         });
         return groupId;
     }
 
-    private void installGroupOnDpn(long groupId, BigInteger dpnId, String groupName, List<BucketInfo> bucketsInfo,
+    private void installGroupOnDpn(long groupId, Uint64 dpnId, String groupName, List<BucketInfo> bucketsInfo,
                                      String nextHopKey, GroupTypes groupType) {
         NodeRef nodeRef = FibUtil.buildNodeRef(dpnId);
         Buckets buckets = FibUtil.buildBuckets(bucketsInfo);
@@ -412,58 +501,74 @@ public class NexthopManager implements AutoCloseable {
         Future<RpcResult<AddGroupOutput>> groupStats = salGroupService.addGroup(input);
         RpcResult<AddGroupOutput> rpcResult = null;
         try {
-            rpcResult = groupStats.get();
+            rpcResult = groupStats.get(WAIT_TIME_FOR_SYNC_INSTALL, TimeUnit.MILLISECONDS);
             if (rpcResult != null && rpcResult.isSuccessful()) {
-                LOG.info("Group {} with key {} has been successfully installed directly on dpn {}.", groupId,
-                        nextHopKey, dpnId);
+                LOG.info("installGroupOnDpn: Group {} with key {} has been successfully installed directly on dpn {}.",
+                        groupId, nextHopKey, dpnId);
             } else {
-                LOG.error("Unable to install group {} with key {} directly on dpn {} due to {}.", groupId, nextHopKey,
-                        dpnId, rpcResult != null ? rpcResult.getErrors() : null);
+                LOG.error("installGroupOnDpn: Unable to install group {} with key {} directly on dpn {} due to {}.",
+                        groupId, nextHopKey, dpnId, rpcResult != null ? rpcResult.getErrors() : null);
             }
         } catch (InterruptedException | ExecutionException e) {
-            LOG.error("Error while installing group {} directly on dpn {}", groupId, dpnId);
+            LOG.error("installGroupOnDpn: Error while installing group {} directly on dpn {}", groupId, dpnId);
+        } catch (TimeoutException e) {
+            LOG.error("installGroupOnDpn: Group {} installation on dpn {} timed out.", groupId, dpnId);
         }
     }
 
-    protected void addVpnNexthopToDS(BigInteger dpnId, long vpnId, String ipPrefix, long egressPointer) {
+    protected void addVpnNexthopToDS(Uint64 dpnId, Uint32 vpnId, String primaryIpAddr,
+                                     String currIpAddr, long egressPointer) {
         InstanceIdentifierBuilder<VpnNexthops> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId));
 
+        List<IpAdjacencies> ipPrefixesList = new ArrayList<>();
+        IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currIpAddr).build();
+        ipPrefixesList.add(prefix);
         // Add nexthop to vpn node
         VpnNexthop nh = new VpnNexthopBuilder()
-            .setKey(new VpnNexthopKey(ipPrefix))
+            .withKey(new VpnNexthopKey(primaryIpAddr))
             .setDpnId(dpnId)
-            .setIpAddress(ipPrefix)
-            .setFlowrefCount(1)
+            .setIpAdjacencies(ipPrefixesList)
             .setEgressPointer(egressPointer).build();
 
         InstanceIdentifier<VpnNexthop> id1 = idBuilder
-            .child(VpnNexthop.class, new VpnNexthopKey(ipPrefix)).build();
+            .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddr)).build();
         LOG.trace("Adding vpnnextHop {} to Operational DS", nh);
         MDSALUtil.syncWrite(dataBroker, LogicalDatastoreType.OPERATIONAL, id1, nh);
 
     }
 
-    protected InstanceIdentifier<VpnNexthop> getVpnNextHopIdentifier(long vpnId, String ipAddress) {
-        InstanceIdentifier<VpnNexthop> id = InstanceIdentifier.builder(L3nexthop.class)
-            .child(VpnNexthops.class, new VpnNexthopsKey(vpnId)).child(VpnNexthop.class,
-                new VpnNexthopKey(ipAddress)).build();
+    protected InstanceIdentifier<IpAdjacencies> getVpnNextHopIpPrefixIdentifier(Uint32 vpnId, String primaryIpAddress,
+                                                                                String ipPrefix) {
+        InstanceIdentifier<IpAdjacencies> id = InstanceIdentifier.builder(L3nexthop.class)
+                .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
+                .child(VpnNexthop.class, new VpnNexthopKey(primaryIpAddress))
+                .child(IpAdjacencies.class, new IpAdjacenciesKey(ipPrefix)).build();
         return id;
     }
 
-    protected VpnNexthop getVpnNexthop(long vpnId, String ipAddress) {
+    @Nullable
+    protected VpnNexthop getVpnNexthop(Uint32 vpnId, String ipAddress) {
 
         // check if vpn node is there
         InstanceIdentifierBuilder<VpnNexthops> idBuilder =
             InstanceIdentifier.builder(L3nexthop.class).child(VpnNexthops.class,
                 new VpnNexthopsKey(vpnId));
         InstanceIdentifier<VpnNexthops> id = idBuilder.build();
-        Optional<VpnNexthops> vpnNexthops = MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
+        Optional<VpnNexthops> vpnNexthops;
+        try {
+            vpnNexthops = SingleTransactionDataBroker.syncReadOptional(dataBroker,
+                    LogicalDatastoreType.OPERATIONAL, id);
+        } catch (ExecutionException | InterruptedException e) {
+            LOG.error("getVpnNexthop: Exception while reading VpnNexthops DS for the address {} vpn {}", ipAddress,
+                    vpnId, e);
+            return null;
+        }
         if (vpnNexthops.isPresent()) {
-            // get nexthops list for vpn
-            List<VpnNexthop> nexthops = vpnNexthops.get().getVpnNexthop();
-            for (VpnNexthop nexthop : nexthops) {
-                if (nexthop.getIpAddress().equals(ipAddress)) {
+            // get keyVpnNexthopMap list for vpn
+            Map<VpnNexthopKey, VpnNexthop> keyVpnNexthopMap = vpnNexthops.get().nonnullVpnNexthop();
+            for (VpnNexthop nexthop : keyVpnNexthopMap.values()) {
+                if (Objects.equals(nexthop.getIpAddress(), ipAddress)) {
                     // return nexthop
                     LOG.trace("VpnNextHop : {}", nexthop);
                     return nexthop;
@@ -474,8 +579,9 @@ public class NexthopManager implements AutoCloseable {
         return null;
     }
 
-    public AdjacencyResult getRemoteNextHopPointer(BigInteger remoteDpnId, long vpnId, String prefixIp,
-            String nextHopIp) {
+    @Nullable
+    public AdjacencyResult getRemoteNextHopPointer(Uint64 remoteDpnId, Uint32 vpnId, String prefixIp,
+            @Nullable String nextHopIp, Class<? extends TunnelTypeBase> tunnelType) {
         String egressIfName = null;
         LOG.trace("getRemoteNextHopPointer: input [remoteDpnId {}, vpnId {}, prefixIp {}, nextHopIp {} ]", remoteDpnId,
             vpnId, prefixIp, nextHopIp);
@@ -490,7 +596,7 @@ public class NexthopManager implements AutoCloseable {
         }
 
         if (Tunnel.class.equals(egressIfType)) {
-            egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp);
+            egressIfName = getTunnelRemoteNextHopPointer(remoteDpnId, nextHopIp, tunnelType);
         } else {
             egressIfName = getExtPortRemoteNextHopPointer(remoteDpnId, elanInstance);
         }
@@ -501,13 +607,7 @@ public class NexthopManager implements AutoCloseable {
                 prefixIp) : null;
     }
 
-    public BigInteger getDpnForPrefix(long vpnId, String prefixIp) {
-        VpnNexthop vpnNexthop = getVpnNexthop(vpnId, prefixIp);
-        BigInteger localDpnId = vpnNexthop == null ? null : vpnNexthop.getDpnId();
-        return localDpnId;
-    }
-
-    private void removeVpnNexthopFromDS(long vpnId, String ipPrefix) {
+    private void removeVpnNexthopFromDS(Uint32 vpnId, String ipPrefix) {
 
         InstanceIdentifierBuilder<VpnNexthop> idBuilder = InstanceIdentifier.builder(L3nexthop.class)
             .child(VpnNexthops.class, new VpnNexthopsKey(vpnId))
@@ -518,43 +618,40 @@ public class NexthopManager implements AutoCloseable {
         MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL, id);
     }
 
-    public void removeLocalNextHop(BigInteger dpnId, Long vpnId, String ipNextHopAddress, String ipPrefixAddress) {
-        String ipPrefixStr = vpnId + ipPrefixAddress;
-        VpnNexthop prefixNh = null;
-        synchronized (ipPrefixStr.intern()) {
-            prefixNh = getVpnNexthop(vpnId, ipPrefixAddress);
-        }
-        String ipAddress = prefixNh != null ? ipPrefixAddress : ipNextHopAddress;
-
-        String nextHopLockStr = vpnId + ipAddress;
-        synchronized (nextHopLockStr.intern()) {
-            VpnNexthop nh = getVpnNexthop(vpnId, ipAddress);
-            if (nh != null) {
-                int newFlowrefCnt = nh.getFlowrefCount() - 1;
-                if (newFlowrefCnt == 0) { //remove the group only if there are no more flows using this group
-                    GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
-                        dpnId, nh.getEgressPointer(), ipAddress, GroupTypes.GroupAll,
-                            Collections.EMPTY_LIST /*listBucketInfo*/);
-                    // remove Group ...
-                    mdsalApiManager.removeGroup(groupEntity);
-                    //update MD-SAL DS
-                    removeVpnNexthopFromDS(vpnId, ipAddress);
-                    //release groupId
-                    removeNextHopPointer(getNextHopKey(vpnId, ipAddress));
-                    LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
-                        nh.getEgressPointer(), vpnId, ipAddress, dpnId);
+    public void removeLocalNextHop(Uint64 dpnId, Uint32 vpnId, String primaryIpAddress, String currDestIpPrefix) {
+        String nextHopLockStr = vpnId + primaryIpAddress;
+        try {
+            if (FibUtil.lockCluster(lockManager, nextHopLockStr, WAIT_TIME_TO_ACQUIRE_LOCK)) {
+                VpnNexthop nh = getVpnNexthop(vpnId, primaryIpAddress);
+                if (nh != null) {
+                    List<IpAdjacencies> prefixesList = new ArrayList<IpAdjacencies>(nh.nonnullIpAdjacencies().values());
+                    IpAdjacencies prefix = new IpAdjacenciesBuilder().setIpAdjacency(currDestIpPrefix).build();
+                    prefixesList.remove(prefix);
+                    if (prefixesList.isEmpty()) { //remove the group only if there are no more flows using this group
+                        GroupEntity groupEntity = MDSALUtil.buildGroupEntity(dpnId, nh.getEgressPointer().toJava(),
+                                primaryIpAddress, GroupTypes.GroupAll, Collections.emptyList());
+                        // remove Group ...
+                        mdsalApiManager.removeGroup(groupEntity);
+                        //update MD-SAL DS
+                        removeVpnNexthopFromDS(vpnId, primaryIpAddress);
+                        //release groupId
+                        removeNextHopPointer(getNextHopKey(vpnId, primaryIpAddress));
+                        LOG.debug("Local Next hop {} for {} {} on dpn {} successfully deleted",
+                                nh.getEgressPointer(), vpnId, primaryIpAddress, dpnId);
+                    } else {
+                        //remove the currIpPrefx from IpPrefixList of the vpnNexthop
+                        LOG.trace("Removing the prefix {} from vpnNextHop {} Operational DS", currDestIpPrefix, nh);
+                        MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                                getVpnNextHopIpPrefixIdentifier(vpnId, primaryIpAddress, currDestIpPrefix));
+                    }
                 } else {
-                    //just update the flowrefCount of the vpnNexthop
-                    VpnNexthop currNh = new VpnNexthopBuilder().setKey(new VpnNexthopKey(ipAddress))
-                        .setFlowrefCount(newFlowrefCnt).build();
-                    LOG.trace("Updating vpnnextHop {} for refCount {} to Operational DS", currNh, newFlowrefCnt);
-                    MDSALUtil.syncUpdate(dataBroker, LogicalDatastoreType.OPERATIONAL, getVpnNextHopIdentifier(vpnId,
-                            ipAddress), currNh);
+                    //throw error
+                    LOG.error("Local NextHop for VpnId {} curIpPrefix {} on dpn {} primaryIpAddress {} not deleted",
+                            vpnId, currDestIpPrefix, dpnId, primaryIpAddress);
                 }
-            } else {
-                //throw error
-                LOG.error("Local Next hop for Prefix {} VpnId {} on dpn {} not deleted", ipAddress, vpnId, dpnId);
             }
+        } finally {
+            FibUtil.unlockCluster(lockManager, nextHopLockStr);
         }
     }
 
@@ -589,11 +686,16 @@ public class NexthopManager implements AutoCloseable {
              * if the value is Unset, cache value as VxLAN.
              */
             LOG.trace("configureTransportType is not yet set.");
-            Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig =
-                MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
-
+            Optional<ConfTransportTypeL3vpn> configuredTransTypeFromConfig;
+            try {
+                configuredTransTypeFromConfig = SingleTransactionDataBroker.syncReadOptional(dataBroker,
+                        LogicalDatastoreType.CONFIGURATION, getConfTransportTypeIdentifier());
+            } catch (ExecutionException | InterruptedException e) {
+                LOG.error("getReqTransType: Exception while reading ConfTransportTypeL3vpn DS", e);
+                return null;
+            }
             if (configuredTransTypeFromConfig.isPresent()) {
-                if (configuredTransTypeFromConfig.get().getTransportType().equals(TunnelTypeGre.class)) {
+                if (TunnelTypeGre.class.equals(configuredTransTypeFromConfig.get().getTransportType())) {
                     configuredTransportTypeL3VPN = L3VPNTransportTypes.GRE;
                 } else {
                     configuredTransportTypeL3VPN = L3VPNTransportTypes.VxLAN;
@@ -664,13 +766,15 @@ public class NexthopManager implements AutoCloseable {
 
     // TODO Clean up the exception handling
     @SuppressWarnings("checkstyle:IllegalCatch")
-    private String getTunnelRemoteNextHopPointer(BigInteger remoteDpnId, String nextHopIp) {
+    @Nullable
+    private String getTunnelRemoteNextHopPointer(Uint64 remoteDpnId, String nextHopIp,
+                                                 Class<? extends TunnelTypeBase> tunnelType) {
         if (nextHopIp != null && !nextHopIp.isEmpty()) {
             try {
                 // here use the config for tunnel type param
                 return getTunnelInterfaceName(remoteDpnId,
                     org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder
-                        .getDefaultInstance(nextHopIp));
+                        .getDefaultInstance(nextHopIp), tunnelType);
             } catch (Exception ex) {
                 LOG.error("Error while retrieving nexthop pointer for nexthop {} remoteDpn {}",
                         nextHopIp, remoteDpnId, ex);
@@ -680,7 +784,7 @@ public class NexthopManager implements AutoCloseable {
         return null;
     }
 
-    private String getExtPortRemoteNextHopPointer(BigInteger remoteDpnId, ElanInstance elanInstance) {
+    private String getExtPortRemoteNextHopPointer(Uint64 remoteDpnId, ElanInstance elanInstance) {
         return elanService.getExternalElanInterface(elanInstance.getElanInstanceName(), remoteDpnId);
     }
 
@@ -700,16 +804,12 @@ public class NexthopManager implements AutoCloseable {
         return Tunnel.class;
     }
 
-    private ElanInstance getElanInstanceForPrefix(long vpnId, String prefixIp) {
+    private ElanInstance getElanInstanceForPrefix(Uint32 vpnId, String prefixIp) {
         ElanInstance elanInstance = null;
         Prefixes prefix = fibUtil.getPrefixToInterface(vpnId, prefixIp);
         if (prefix != null) {
-            Uuid subnetId = prefix.getSubnetId();
-            if (subnetId != null) {
-                Subnetmap subnetMap = fibUtil.getSubnetMap(subnetId);
-                if (subnetMap != null && subnetMap.getNetworkId() != null) {
-                    elanInstance = elanService.getElanInstance(subnetMap.getNetworkId().getValue());
-                }
+            if (prefix.getNetworkId() != null) {
+                elanInstance = elanService.getElanInstance(prefix.getNetworkId().getValue());
             }
         }
 
@@ -760,200 +860,316 @@ public class NexthopManager implements AutoCloseable {
                 return false;
             }
 
-            boolean result = false;
             if (getClass() != obj.getClass()) {
-                return result;
+                return false;
             } else {
                 AdjacencyResult other = (AdjacencyResult) obj;
-                result = interfaceName.equals(other.interfaceName);
+                return interfaceName.equals(other.interfaceName);
             }
-            return result;
         }
     }
 
-    protected long setupLoadBalancingNextHop(Long parentVpnId, BigInteger dpnId,
-            String destPrefix, List<BucketInfo> listBucketInfo, boolean addOrRemove) {
-        long groupId = createNextHopPointer(getNextHopKey(parentVpnId, destPrefix));
-        if (groupId == FibConstants.INVALID_GROUP_ID) {
-            LOG.error("Unable to allocate/retrieve groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
-            return groupId;
+    protected long setupLoadBalancingNextHop(Uint32 parentVpnId, Uint64 dpnId,
+            String destPrefix, List<BucketInfo> localBucketInfo, List<BucketInfo> remoteBucketInfo) {
+        long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
+        if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
+            LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
+            return remoteGroupId;
         }
-        GroupEntity groupEntity = MDSALUtil.buildGroupEntity(
-                dpnId, groupId, destPrefix, GroupTypes.GroupSelect, listBucketInfo);
-        if (addOrRemove) {
-            mdsalApiManager.syncInstallGroup(groupEntity);
-            try {
-                Thread.sleep(WAIT_TIME_FOR_SYNC_INSTALL);
-            } catch (InterruptedException e1) {
-                LOG.warn("Thread got interrupted while programming LB group {}", groupEntity);
-                Thread.currentThread().interrupt();
+        long localGroupId =  createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
+        if (localGroupId == FibConstants.INVALID_GROUP_ID) {
+            LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}",
+                parentVpnId, destPrefix);
+            return remoteGroupId;
+        }
+        List<BucketInfo> combinedBucketInfo = new ArrayList<>();
+        combinedBucketInfo.addAll(localBucketInfo);
+        combinedBucketInfo.addAll(remoteBucketInfo);
+        GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
+                dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, combinedBucketInfo);
+        GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
+                dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, localBucketInfo);
+        String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
+        jobCoordinator.enqueueJob(jobKey, () -> {
+            mdsalApiManager.syncInstallGroup(remoteGroupEntity);
+            if (!localBucketInfo.isEmpty()) {
+                mdsalApiManager.syncInstallGroup(localGroupEntity);
             }
-        } else {
-            mdsalApiManager.removeGroup(groupEntity);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Finished installing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
+                        + "localGroupEntity.groupId {}  groupEntity.groupType {}", jobKey,
+                        remoteGroupEntity.getGroupId(), localGroupEntity.getGroupId(),
+                        remoteGroupEntity.getGroupType());
+            }
+            // Delete local group(if exists) if there is no local info.
+            // Local group has to be deleted if all VMs in a compute is deleted.
+            if (localBucketInfo.isEmpty()) {
+                LOG.debug("Deleting local group {} since no local nhs present for "
+                        + "prefix {}", localGroupEntity.getGroupId(), destPrefix);
+                mdsalApiManager.syncRemoveGroup(localGroupEntity);
+            }
+            return Collections.emptyList();
+        });
+        return remoteGroupId;
+    }
+
+    protected void deleteLoadBalancingNextHop(Uint32 parentVpnId, Uint64 dpnId, String destPrefix) {
+        long remoteGroupId = createNextHopPointer(getRemoteSelectGroupKey(parentVpnId, destPrefix));
+        if (remoteGroupId == FibConstants.INVALID_GROUP_ID) {
+            LOG.error("Unable to allocate/retrieve remote groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
         }
-        return groupId;
+        long localGroupId = createNextHopPointer(getLocalSelectGroupKey(parentVpnId, destPrefix));
+        if (localGroupId == FibConstants.INVALID_GROUP_ID) {
+            LOG.error("Unable to allocate/retrieve local groupId for vpnId {} , prefix {}", parentVpnId, destPrefix);
+        }
+        GroupEntity remoteGroupEntity = MDSALUtil.buildGroupEntity(
+                dpnId, remoteGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
+        GroupEntity localGroupEntity = MDSALUtil.buildGroupEntity(
+                dpnId, localGroupId, destPrefix, GroupTypes.GroupSelect, Collections.emptyList());
+        String jobKey = FibUtil.getCreateLocalNextHopJobKey(parentVpnId, dpnId, destPrefix);
+        jobCoordinator.enqueueJob(jobKey, () -> {
+            mdsalApiManager.syncRemoveGroup(remoteGroupEntity);
+            mdsalApiManager.syncRemoveGroup(localGroupEntity);
+            if (LOG.isDebugEnabled()) {
+                LOG.debug("Finished removing GroupEntity with jobCoordinator key {} remoteGroupEntity.groupId {}"
+                    + "localGroupEntity.groupId {}", jobKey, remoteGroupId, localGroupId);
+            }
+            return Collections.emptyList();
+        });
     }
 
-    long createNextHopGroups(Long vpnId, String rd, BigInteger dpnId, VrfEntry vrfEntry,
-            Routes routes, List<Routes> vpnExtraRoutes) {
-        List<BucketInfo> listBucketInfo = new ArrayList<>();
+    long createNextHopGroups(Uint32 vpnId, String rd, Uint64 dpnId, VrfEntry vrfEntry,
+            @Nullable Routes routes, List<Routes> vpnExtraRoutes) {
+        List<BucketInfo> localBucketInfo = new ArrayList<>();
         List<Routes> clonedVpnExtraRoutes  = new ArrayList<>(vpnExtraRoutes);
         if (clonedVpnExtraRoutes.contains(routes)) {
-            listBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
+            localBucketInfo.addAll(getBucketsForLocalNexthop(vpnId, dpnId, vrfEntry, routes));
             clonedVpnExtraRoutes.remove(routes);
         }
-        listBucketInfo.addAll(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
-        return setupLoadBalancingNextHop(vpnId, dpnId, vrfEntry.getDestPrefix(), listBucketInfo, true);
+        List<BucketInfo> remoteBucketInfo =
+            new ArrayList<>(getBucketsForRemoteNexthop(vpnId, dpnId, vrfEntry, rd, clonedVpnExtraRoutes));
+        return setupLoadBalancingNextHop(vpnId, dpnId,
+            vrfEntry.getDestPrefix(), localBucketInfo, remoteBucketInfo);
     }
 
-    private List<BucketInfo> getBucketsForLocalNexthop(Long vpnId, BigInteger dpnId,
+    private List<BucketInfo> getBucketsForLocalNexthop(Uint32 vpnId, Uint64 dpnId,
             VrfEntry vrfEntry, Routes routes) {
+        @Nullable List<String> nexthopIpList = routes.getNexthopIpList();
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("NexthopManager.getBucketsForLocalNexthop invoked with vpnId {} dpnId {} "
+                            + " vrfEntry.routePaths {}, routes.nexthopList {}", vpnId, dpnId, vrfEntry.getRoutePaths(),
+                nexthopIpList);
+        }
         List<BucketInfo> listBucketInfo = new CopyOnWriteArrayList<>();
-        routes.getNexthopIpList().parallelStream().forEach(nextHopIp -> {
-            String localNextHopIP;
-            if (isIpv4Address(nextHopIp)) {
-                localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
-            } else {
-                localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
-            }
-            Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
-            if (localNextHopInfo != null) {
-                long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
-                if (groupId == FibConstants.INVALID_GROUP_ID) {
-                    LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
-                            vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
-                    return;
+        if (nexthopIpList != null) {
+            nexthopIpList.parallelStream().forEach(nextHopIp -> {
+                String localNextHopIP;
+                if (isIpv4Address(nextHopIp)) {
+                    localNextHopIP = nextHopIp + NwConstants.IPV4PREFIX;
+                } else {
+                    localNextHopIP = nextHopIp + NwConstants.IPV6PREFIX;
                 }
-                List<ActionInfo> actionsInfos =
+                Prefixes localNextHopInfo = fibUtil.getPrefixToInterface(vpnId, localNextHopIP);
+                if (localNextHopInfo != null) {
+                    long groupId = getLocalNextHopGroup(vpnId, localNextHopIP);
+                    if (groupId == FibConstants.INVALID_GROUP_ID) {
+                        LOG.error("Unable to allocate groupId for vpnId {} , prefix {} , interface {}", vpnId,
+                            vrfEntry.getDestPrefix(), localNextHopInfo.getVpnInterfaceName());
+                        return;
+                    }
+                    List<ActionInfo> actionsInfos =
                         Collections.singletonList(new ActionGroup(groupId));
-                BucketInfo bucket = new BucketInfo(actionsInfos);
-                bucket.setWeight(1);
-                listBucketInfo.add(bucket);
-            }
-        });
+                    BucketInfo bucket = new BucketInfo(actionsInfos);
+                    bucket.setWeight(1);
+                    listBucketInfo.add(bucket);
+                }
+            });
+        }
         LOG.trace("LOCAL: listbucket {}, vpnId {}, dpnId {}, routes {}", listBucketInfo, vpnId, dpnId, routes);
         return listBucketInfo;
     }
 
-    private List<BucketInfo> getBucketsForRemoteNexthop(Long vpnId, BigInteger dpnId, VrfEntry vrfEntry, String rd,
+    private List<BucketInfo> getBucketsForRemoteNexthop(Uint32 vpnId, Uint64 dpnId, VrfEntry vrfEntry, String rd,
             List<Routes> vpnExtraRoutes) {
         List<BucketInfo> listBucketInfo = new ArrayList<>();
         Map<String, List<ActionInfo>> egressActionMap = new HashMap<>();
-        vpnExtraRoutes.forEach(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
-            String nextHopPrefixIp;
-            if (isIpv4Address(nextHopIp)) {
-                nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
-            } else {
-                nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
-            }
-            List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
-            if (tepIpAddresses.isEmpty()) {
-                return;
-            }
-            // There would be only one nexthop address for a VM ip which would be the tep Ip
-            String tepIp = tepIpAddresses.get(0);
-            AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
-                    vrfEntry.getDestPrefix(), tepIp);
-            if (adjacencyResult == null) {
-                return;
-            }
-            String egressInterface = adjacencyResult.getInterfaceName();
-            if (!FibUtil.isTunnelInterface(adjacencyResult)) {
-                return;
-            }
-            Class<? extends TunnelTypeBase> tunnelType = VpnExtraRouteHelper
-                    .getTunnelType(interfaceManager,
-                            egressInterface);
-            Interface ifState = fibUtil.getInterfaceStateFromOperDS(egressInterface);
-            if (ifState == null || ifState.getOperStatus() != OperStatus.Up) {
-                LOG.trace("Tunnel not up {}", egressInterface);
-                return;
-            }
-            if (!TunnelTypeVxlan.class.equals(tunnelType)) {
-                return;
-            }
-            Long label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
-            Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
-            BigInteger tunnelId;
-            if (fibUtil.enforceVxlanDatapathSemanticsforInternalRouterVpn(prefixInfo.getSubnetId(), vpnId,
-                    rd)) {
-                java.util.Optional<Long> optionalVni = fibUtil.getVniForVxlanNetwork(prefixInfo.getSubnetId());
-                if (!optionalVni.isPresent()) {
-                    LOG.error("VNI not found for nexthop {} vrfEntry {} with subnetId {}", nextHopIp,
-                            vrfEntry, prefixInfo.getSubnetId());
+        vpnExtraRoutes.stream().filter(vpnExtraRoute -> vpnExtraRoute.getNexthopIpList() != null).forEach(
+            vpnExtraRoute -> vpnExtraRoute.getNexthopIpList().forEach(nextHopIp -> {
+                String nextHopPrefixIp;
+                if (isIpv4Address(nextHopIp)) {
+                    nextHopPrefixIp = nextHopIp + NwConstants.IPV4PREFIX;
+                } else {
+                    nextHopPrefixIp = nextHopIp + NwConstants.IPV6PREFIX;
+                }
+                List<String> tepIpAddresses = fibUtil.getNextHopAddresses(rd, nextHopPrefixIp);
+                if (tepIpAddresses.isEmpty()) {
                     return;
                 }
-                tunnelId = BigInteger.valueOf(optionalVni.get());
-            } else {
-                tunnelId = BigInteger.valueOf(label);
-            }
-            List<ActionInfo> actionInfos = new ArrayList<>();
-            actionInfos.add(new ActionSetFieldTunnelId(tunnelId));
-            String ifName = prefixInfo.getVpnInterfaceName();
-            String vpnName = fibUtil.getVpnNameFromId(vpnId);
-            if (vpnName == null) {
-                return;
-            }
-            String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
-            actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
+                // There would be only one nexthop address for a VM ip which would be the tep Ip
+                String tepIp = tepIpAddresses.get(0);
+                AdjacencyResult adjacencyResult = getRemoteNextHopPointer(dpnId, vpnId,
+                    vrfEntry.getDestPrefix(), tepIp, TunnelTypeVxlan.class);
+                if (adjacencyResult == null) {
+                    return;
+                }
+                String egressInterface = adjacencyResult.getInterfaceName();
+                if (!FibUtil.isTunnelInterface(adjacencyResult)) {
+                    return;
+                }
+                Class<? extends TunnelTypeBase> tunnelType =
+                    VpnExtraRouteHelper.getTunnelType(itmManager, egressInterface);
+                StateTunnelList ifState = null;
+                try {
+                    ifState = fibUtil.getTunnelState(egressInterface);
+                    if (ifState == null || ifState.getOperState() != TunnelOperStatus.Up) {
+                        LOG.trace("Tunnel is not up for interface {}", egressInterface);
+                        return;
+                    }
+                } catch (ReadFailedException e) {
+                    LOG.error("getBucketsForRemoteNexthop: error in fetching tunnel state for interface {}",
+                        egressInterface, e);
+                    return;
+                }
+                if (!TunnelTypeVxlan.class.equals(tunnelType)) {
+                    return;
+                }
+                Uint32 label = FibUtil.getLabelFromRoutePaths(vrfEntry).get();
+                Prefixes prefixInfo = fibUtil.getPrefixToInterface(vpnId, nextHopPrefixIp);
+                if (prefixInfo == null) {
+                    LOG.error("No prefix info found for prefix {} in rd {} for VPN {}", nextHopPrefixIp, rd,
+                        vpnId);
+                    return;
+                }
+                Uint32 tunnelId;
+                if (FibUtil.isVxlanNetwork(prefixInfo.getNetworkType())) {
+                    tunnelId = prefixInfo.getSegmentationId();
+                } else {
+                    LOG.warn("Network is not of type VXLAN for prefix {}."
+                        + "Going with default Lport Tag.", prefixInfo.toString());
+                    tunnelId = label;
+                }
+                List<ActionInfo> actionInfos = new ArrayList<>();
+                actionInfos.add(new ActionSetFieldTunnelId(Uint64.valueOf(tunnelId.longValue())));
+                String ifName = prefixInfo.getVpnInterfaceName();
+                String vpnName = fibUtil.getVpnNameFromId(vpnId);
+                if (vpnName == null) {
+                    return;
+                }
+                String macAddress = fibUtil.getMacAddressFromPrefix(ifName, vpnName, nextHopPrefixIp);
+                actionInfos.add(new ActionSetFieldEthernetDestination(actionInfos.size(),
                     new MacAddress(macAddress)));
-            List<ActionInfo> egressActions;
-            if (egressActionMap.containsKey(egressInterface)) {
-                egressActions = egressActionMap.get(egressInterface);
-            } else {
-                egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size());
-                egressActionMap.put(egressInterface, egressActions);
-            }
-            if (egressActions.isEmpty()) {
-                LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
-                        + " interface {}." + " Aborting remote FIB entry creation.",
+                List<ActionInfo> egressActions;
+                if (egressActionMap.containsKey(egressInterface)) {
+                    egressActions = egressActionMap.get(egressInterface);
+                } else {
+                    egressActions = getEgressActionsForInterface(egressInterface, actionInfos.size(),
+                            true, vpnId, vrfEntry.getDestPrefix());
+                    if (egressActions.isEmpty()) {
+                        LOG.error("Skipping getBucketsForRemoteNexthop: Empty list of egress actions received for "
+                                        + "interface {} on dpn {} for vpn {} prefix {} nextHop {}", ifName, dpnId,
+                                vpnId, vrfEntry.getDestPrefix(), nextHopPrefixIp);
+                    }
+                    egressActionMap.put(egressInterface, egressActions);
+                }
+                if (egressActions.isEmpty()) {
+                    LOG.error("Failed to retrieve egress action for prefix {} route-paths {}"
+                            + " interface {}." + " Aborting remote FIB entry creation.",
                         vrfEntry.getDestPrefix(), vrfEntry.getRoutePaths(), egressInterface);
-            }
-            actionInfos.addAll(egressActions);
-            BucketInfo bucket = new BucketInfo(actionInfos);
-            bucket.setWeight(1);
-            listBucketInfo.add(bucket);
-        }));
+                }
+                actionInfos.addAll(egressActions);
+                BucketInfo bucket = new BucketInfo(actionInfos);
+                bucket.setWeight(1);
+                listBucketInfo.add(bucket);
+            }));
         LOG.trace("LOCAL: listbucket {}, rd {}, dpnId {}, routes {}", listBucketInfo, rd, dpnId, vpnExtraRoutes);
         return listBucketInfo;
     }
 
-    public void createDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId, String destinationIp) {
-        Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
-        int noOfDcGws = availableDcGws.size();
-        if (noOfDcGws == 1) {
-            LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
-            return;
+    public void createDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
+                                             Class<? extends TunnelTypeBase> tunnelType) {
+        jobCoordinator.enqueueJob(FibHelper.getJobKeyForDcGwLoadBalancingGroup(dpnId), () -> {
+            List<ListenableFuture<?>> futures = new ArrayList<>();
+            futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operationalTx -> {
+                synchronized (getDcGateWaySyncKey(destinationIp)) {
+                    FibUtil.addL3vpnDcGateWay(destinationIp, operationalTx);
+                }
+                futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, configTx -> {
+                    List<String> availableDcGws = getDcGwIps();
+                    Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
+                    int noOfDcGws = availableDcGws.size();
+                    if (noOfDcGws == 1) {
+                        LOG.trace("There are no enough DC GateWays {} present to program LB group", availableDcGws);
+                        return;
+                    }
+                    if (availableDcGws.contains(destinationIp)) {
+                        availableDcGws.remove(destinationIp);
+                    }
+                    availableDcGws.forEach(dcGwIp -> {
+                        List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
+                        Collections.sort(dcGws);
+                        String groupIdKey = FibUtil.getGreLbGroupKey(dcGws);
+                        Long groupId = createNextHopPointer(groupIdKey);
+                        List<Bucket> listBucket = new ArrayList<>();
+                        for (int index = 0; index < dcGws.size(); index++) {
+                            if (isTunnelUp(dcGws.get(index), dpnId, tunnelType)) {
+                                listBucket.add(buildBucketForDcGwLbGroup(dcGws.get(index),
+                                        dpnId, index, tunnelType, true));
+                            }
+                        }
+                        Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
+                                        MDSALUtil.buildBucketLists(listBucket));
+                        mdsalApiManager.addGroup(configTx, dpnId, group);
+                        FibUtil.updateLbGroupInfo(dpnId, groupIdKey, groupId.toString(), operationalTx);
+                        LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}",
+                                groupIdKey, dpnId, group);
+                    });
+                }));
+            }));
+            return futures;
+        }, RETRY_COUNT);
+    }
+
+    private String getDcGateWaySyncKey(String destinationIp) {
+        String mutex = new StringBuilder().append("L3vpncDcGateWay").append(destinationIp).toString();
+        return mutex.intern();
+    }
+
+    private List<String> getDcGwIps() {
+        InstanceIdentifier<DcGatewayIpList> dcGatewayIpListid =
+                InstanceIdentifier.builder(DcGatewayIpList.class).build();
+        DcGatewayIpList dcGatewayIpListConfig;
+        try {
+            dcGatewayIpListConfig = SingleTransactionDataBroker.syncReadOptional(dataBroker,
+                    LogicalDatastoreType.CONFIGURATION, dcGatewayIpListid).orElse(null);
+        } catch (ExecutionException | InterruptedException e) {
+            LOG.error("getDcGwIps: Exception while reading DcGatewayIpList DS", e);
+            return Collections.emptyList();
         }
-        // TODO : Place the logic to construct all possible DC-GW combination here.
-        String groupIdKey = FibUtil.getGreLbGroupKey(availableDcGws);
-        Long groupId = createNextHopPointer(groupIdKey);
-        List<Bucket> listBucket = new ArrayList<>();
-        for (int index = 0; index < noOfDcGws; index++) {
-            if (isTunnelUp(availableDcGws.get(index), dpnId)) {
-                listBucket.add(buildBucketForDcGwLbGroup(availableDcGws.get(index), dpnId, index));
-            }
+        if (dcGatewayIpListConfig == null) {
+            return Collections.emptyList();
         }
-        Group group = MDSALUtil.buildGroup(groupId, groupIdKey, GroupTypes.GroupSelect,
-                        MDSALUtil.buildBucketLists(listBucket));
-        WriteTransaction configTx = dataBroker.newWriteOnlyTransaction();
-        WriteTransaction operationalTx = dataBroker.newWriteOnlyTransaction();
-        mdsalApiManager.addGroupToTx(dpnId, group, configTx);
-        FibUtil.updateLbGroupInfo(dpnId, destinationIp, groupIdKey, groupId.toString(), operationalTx);
-        configTx.submit();
-        operationalTx.submit();
-        LOG.trace("LB group {} towards DC-GW installed on dpn {}. Group - {}", groupIdKey, dpnId, group);
+        return new ArrayList<DcGatewayIp>(dcGatewayIpListConfig.nonnullDcGatewayIp().values())
+                .stream()
+                .filter(dcGwIp -> dcGwIp.getTunnnelType().equals(TunnelTypeMplsOverGre.class))
+                .map(dcGwIp -> dcGwIp.getIpAddress().stringValue()).sorted()
+                .collect(toList());
     }
 
-    private boolean isTunnelUp(String dcGwIp, BigInteger dpnId) {
-        String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp);
+    private boolean isTunnelUp(String dcGwIp, Uint64 dpnId, Class<? extends TunnelTypeBase> tunnelType) {
+        String tunnelName = getTunnelRemoteNextHopPointer(dpnId, dcGwIp, tunnelType);
         if (tunnelName != null) {
             InstanceIdentifier<StateTunnelList> tunnelStateId =
                     InstanceIdentifier.builder(TunnelsState.class).child(
                             StateTunnelList.class, new StateTunnelListKey(tunnelName)).build();
-            return MDSALUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, tunnelStateId)
-                    .toJavaUtil().map(StateTunnelList::getOperState)
-                    .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
+            try {
+                return SingleTransactionDataBroker.syncReadOptional(dataBroker, LogicalDatastoreType.OPERATIONAL,
+                        tunnelStateId).map(StateTunnelList::getOperState)
+                        .orElse(TunnelOperStatus.Down) == TunnelOperStatus.Up;
+            } catch (ExecutionException | InterruptedException e) {
+                LOG.error("isTunnelUp: Exception while reading StateTunnelList DS for tunnel {} tunnelType {}",
+                        tunnelName, tunnelType, e);
+                return false;
+            }
         }
         return false;
     }
@@ -964,13 +1180,13 @@ public class NexthopManager implements AutoCloseable {
             GetEgressActionsForInterfaceInputBuilder egressAction =
                     new GetEgressActionsForInterfaceInputBuilder().setIntfName(interfaceName).setActionKey(actionKey);
             Future<RpcResult<GetEgressActionsForInterfaceOutput>> result =
-                    interfaceManager.getEgressActionsForInterface(egressAction.build());
+                    odlInterfaceRpcService.getEgressActionsForInterface(egressAction.build());
             RpcResult<GetEgressActionsForInterfaceOutput> rpcResult = result.get();
             if (!rpcResult.isSuccessful()) {
                 LOG.warn("RPC Call to Get egress actions for interface {} returned with Errors {}",
                         interfaceName, rpcResult.getErrors());
             } else {
-                actions = rpcResult.getResult().getAction();
+                actions = new ArrayList<Action>(rpcResult.getResult().nonnullAction().values());
             }
         } catch (InterruptedException | ExecutionException e) {
             LOG.warn("Exception when egress actions for interface {}", interfaceName, e);
@@ -979,101 +1195,98 @@ public class NexthopManager implements AutoCloseable {
     }
 
     /**
-     * This method is invoked when the tunnel state is removed from DS.
-     * If the there is just one DC-GW left in configuration then the LB groups can be deleted.
-     * Otherwise, the groups are just updated.
+     * This method is invoked when the neighbor is removed from DS.
+     * All the LB groups which point to the given destination will be deleted.
      */
-    public void removeOrUpdateDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
+    public void removeDcGwLoadBalancingGroup(Uint64 dpnId,
             String destinationIp) {
-        Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
-        WriteTransaction configTx = dataBroker.newWriteOnlyTransaction();
-        WriteTransaction operationalTx = dataBroker.newWriteOnlyTransaction();
-        int noOfDcGws = availableDcGws.size();
-        // If availableDcGws does not contain the destination Ip it means this is a configuration delete.
-        if (!availableDcGws.contains(destinationIp)) {
-            availableDcGws.add(destinationIp);
-            Collections.sort(availableDcGws);
-        }
-        // TODO : Place the logic to construct all possible DC-GW combination here.
-        int bucketId = availableDcGws.indexOf(destinationIp);
-        Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
-        if (!dpnLbNextHops.isPresent()) {
-            return;
-        }
-        List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
-        nextHopKeys.forEach(nextHopKey -> {
-            Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
-            if (!optionalNextHops.isPresent()) {
-                return;
-            }
-            Nexthops nexthops = optionalNextHops.get();
-            final String groupId = nexthops.getGroupId();
-            final long groupIdValue = Long.parseLong(groupId);
-            if (noOfDcGws > 1) {
-                mdsalApiManager.removeBucketToTx(dpnId, groupIdValue, bucketId, configTx);
-            } else {
-                Group group = MDSALUtil.buildGroup(groupIdValue, nextHopKey, GroupTypes.GroupSelect,
-                        MDSALUtil.buildBucketLists(Collections.emptyList()));
-                LOG.trace("Removed LB group {} on dpn {}", group, dpnId);
-                mdsalApiManager.removeGroupToTx(dpnId, group, configTx);
-                removeNextHopPointer(nextHopKey);
-            }
-            // When the DC-GW is removed from configuration.
-            if (noOfDcGws != availableDcGws.size()) {
-                FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operationalTx);
+        jobCoordinator.enqueueJob(FibHelper.getJobKeyForDcGwLoadBalancingGroup(dpnId), () -> {
+            List<String> availableDcGws = fibUtil.getL3VpnDcGateWays();
+            if (availableDcGws.contains(destinationIp)) {
+                availableDcGws.remove(destinationIp);
             }
-        });
-        FibUtil.removeDpnIdToNextHopInfo(destinationIp, dpnId, operationalTx);
-        configTx.submit();
-        operationalTx.submit();
-        return;
+            List<ListenableFuture<?>> futures = new ArrayList<>();
+            futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operationalTx -> {
+                futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
+                    availableDcGws.forEach(dcGwIp -> {
+                        List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
+                        Collections.sort(dcGws);
+                        String nextHopKey = FibUtil.getGreLbGroupKey(dcGws);
+                        Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
+                        if (!optionalNextHops.isPresent()) {
+                            return;
+                        }
+                        Nexthops nexthops = optionalNextHops.get();
+                        final String groupId = nexthops.getGroupId();
+                        final long groupIdValue = Long.parseLong(groupId);
+                        Group group = MDSALUtil.buildGroup(groupIdValue, nextHopKey, GroupTypes.GroupSelect,
+                                MDSALUtil.buildBucketLists(Collections.emptyList()));
+                        LOG.trace("Removed LB group {} on dpn {}", group, dpnId);
+                        try {
+                            mdsalApiManager.removeGroup(configTx, dpnId, group);
+                        } catch (ExecutionException | InterruptedException e) {
+                            LOG.error("Group removal failed for group {} with exception", groupId, e);
+                        }
+                        removeNextHopPointer(nextHopKey);
+                        FibUtil.removeOrUpdateNextHopInfo(dpnId, nextHopKey, groupId, nexthops, operationalTx);
+                    });
+                    synchronized (getDcGateWaySyncKey(destinationIp)) {
+                        FibUtil.removeL3vpnDcGateWay(destinationIp, operationalTx);
+                    }
+                }));
+            }));
+            return futures;
+        }, RETRY_COUNT);
     }
 
     /**
-     * This method is invoked when the tunnel status is updated.
-     * The bucket is directly removed/added based on the operational status of the tunnel.
+     * This method is invoked when the tunnel status is deleted.
+     * All the buckets which point to given destination will be marked down.
      */
-    public void updateDcGwLoadBalancingGroup(List<String> availableDcGws,
-            BigInteger dpnId, String destinationIp, boolean isTunnelUp) {
-        Preconditions.checkNotNull(availableDcGws, "There are no dc-gws present");
-        WriteTransaction configTx = dataBroker.newWriteOnlyTransaction();
-        // TODO : Place the logic to construct all possible DC-GW combination here.
-        int bucketId = availableDcGws.indexOf(destinationIp);
-        Optional<DpnLbNexthops> dpnLbNextHops = fibUtil.getDpnLbNexthops(dpnId, destinationIp);
-        if (!dpnLbNextHops.isPresent()) {
-            return;
-        }
-        List<String> nextHopKeys = dpnLbNextHops.get().getNexthopKey();
-        nextHopKeys.forEach(nextHopKey -> {
-            Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
-            if (!optionalNextHops.isPresent()) {
-                return;
-            }
-            Nexthops nexthops = optionalNextHops.get();
-            final String groupId = nexthops.getGroupId();
-            final long groupIdValue = Long.parseLong(groupId);
-            if (isTunnelUp) {
-                Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId);
-                LOG.trace("Added bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
-                mdsalApiManager.addBucketToTx(dpnId, groupIdValue, bucket , configTx);
-            } else {
-                LOG.trace("Removed bucketId {} from group {} on dpn {}.", bucketId, groupId, dpnId);
-                mdsalApiManager.removeBucketToTx(dpnId, groupIdValue, bucketId, configTx);
+    public void updateDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
+            boolean isTunnelUp, Class<? extends TunnelTypeBase> tunnelType) {
+        jobCoordinator.enqueueJob(FibHelper.getJobKeyForDcGwLoadBalancingGroup(dpnId), () -> {
+            List<String> availableDcGws = fibUtil.getL3VpnDcGateWays();
+            if (availableDcGws.contains(destinationIp)) {
+                availableDcGws.remove(destinationIp);
             }
-        });
-        configTx.submit();
-        return;
+            List<ListenableFuture<?>> futures = new ArrayList<>();
+            futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, configTx -> {
+                availableDcGws.forEach(dcGwIp -> {
+                    List<String> dcGws = Arrays.asList(dcGwIp, destinationIp);
+                    Collections.sort(dcGws);
+                    String nextHopKey = FibUtil.getGreLbGroupKey(dcGws);
+                    int bucketId = dcGws.indexOf(destinationIp);
+                    Optional<Nexthops> optionalNextHops = fibUtil.getNexthops(nextHopKey);
+                    if (!optionalNextHops.isPresent()) {
+                        return;
+                    }
+                    Nexthops nexthops = optionalNextHops.get();
+                    final String groupId = nexthops.getGroupId();
+                    final long groupIdValue = Long.parseLong(groupId);
+                    Bucket bucket = buildBucketForDcGwLbGroup(destinationIp, dpnId, bucketId, tunnelType, isTunnelUp);
+                    LOG.trace("updated bucket {} to group {} on dpn {}.", bucket, groupId, dpnId);
+                    try {
+                        mdsalApiManager.addBucket(configTx, dpnId, groupIdValue, bucket);
+                    } catch (ExecutionException | InterruptedException e) {
+                        LOG.error("Bucket addition failed for bucket {} with exception", bucketId, e);
+                    }
+                });
+            }));
+            return futures;
+        }, RETRY_COUNT);
     }
 
-    private Bucket buildBucketForDcGwLbGroup(String ipAddress, BigInteger dpnId,
-            int index) {
+    private Bucket buildBucketForDcGwLbGroup(String ipAddress, Uint64 dpnId,
+            int index, Class<? extends TunnelTypeBase> tunnelType, boolean isTunnelUp) {
         List<Action> listAction = new ArrayList<>();
         // ActionKey 0 goes to mpls label.
         int actionKey = 1;
         listAction.add(new ActionPushMpls().buildAction());
         listAction.add(new ActionRegMove(actionKey++, FibConstants.NXM_REG_MAPPING
                 .get(index), 0, 19).buildAction());
-        String tunnelInterfaceName = getTunnelInterfaceName(dpnId, new IpAddress(ipAddress.toCharArray()));
+        String tunnelInterfaceName = getTunnelInterfaceName(dpnId, IpAddressBuilder.getDefaultInstance(ipAddress),
+            tunnelType);
         List<Action> egressActions = getEgressActions(tunnelInterfaceName, actionKey++);
         if (!egressActions.isEmpty()) {
             listAction.addAll(getEgressActions(tunnelInterfaceName, actionKey++));
@@ -1081,18 +1294,24 @@ public class NexthopManager implements AutoCloseable {
             // clear off actions if there is no egress actions.
             listAction = Collections.emptyList();
         }
-        return MDSALUtil.buildBucket(listAction, MDSALUtil.GROUP_WEIGHT, index,
-                MDSALUtil.WATCH_PORT, MDSALUtil.WATCH_GROUP);
+        long watchPort = MDSALUtil.WATCH_PORT;
+        if (!isTunnelUp) {
+            watchPort = 0xFFFFFFFEL;
+        }
+        //OVS expects a non-zero weight value for load balancing to happen in select groups
+        return MDSALUtil.buildBucket(listAction, SELECT_GROUP_WEIGHT, index,
+                watchPort, MDSALUtil.WATCH_GROUP);
     }
 
-    public void programDcGwLoadBalancingGroup(List<String> availableDcGws, BigInteger dpnId,
-            String destinationIp, int addRemoveOrUpdate, boolean isTunnelUp) {
+    public void programDcGwLoadBalancingGroup(Uint64 dpnId, String destinationIp,
+                                              int addRemoveOrUpdate, boolean isTunnelUp,
+                                              Class<? extends TunnelTypeBase> tunnelType) {
         if (NwConstants.ADD_FLOW == addRemoveOrUpdate) {
-            createDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
+            createDcGwLoadBalancingGroup(dpnId, destinationIp, tunnelType);
         } else if (NwConstants.DEL_FLOW == addRemoveOrUpdate) {
-            removeOrUpdateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp);
+            removeDcGwLoadBalancingGroup(dpnId, destinationIp);
         } else if (NwConstants.MOD_FLOW == addRemoveOrUpdate) {
-            updateDcGwLoadBalancingGroup(availableDcGws, dpnId, destinationIp, isTunnelUp);
+            updateDcGwLoadBalancingGroup(dpnId, destinationIp, isTunnelUp, tunnelType);
         }
     }
 }