NETVIRT-1037: Upstream Fixes --- Review-1.
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / TunnelInterfaceStateListener.java
index a4886c57c24b5066fcbda357e8fe94142cda0505..4ad3998ab2ee14d0831d96c1d42be4ce484b07c2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ * Copyright © 2016, 2017 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
  */
 package org.opendaylight.netvirt.vpnmanager;
 
+import static java.util.stream.Collectors.toList;
+
 import com.google.common.base.Optional;
+import com.google.common.base.Strings;
+import com.google.common.util.concurrent.ListenableFuture;
 import java.math.BigInteger;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
-
-import com.google.common.util.concurrent.ListenableFuture;
+import javax.annotation.PostConstruct;
+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.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.genius.datastoreutils.DataStoreJobCoordinator;
-import org.opendaylight.netvirt.bgpmanager.api.IBgpManager;
+import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
+import org.opendaylight.genius.mdsalutil.MDSALUtil;
+import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
 import org.opendaylight.netvirt.fibmanager.api.IFibManager;
-import org.opendaylight.netvirt.vpnmanager.utilities.InterfaceUtils;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInstances;
-import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
+import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
+import org.opendaylight.netvirt.vpnmanager.api.VpnExtraRouteHelper;
+import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
+import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.vpn._interface.VpnInstanceNames;
+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.ietf.interfaces.rev140508.interfaces.Interface;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.IfTunnel;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.ParentRefs;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeMplsOverGre;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rev160406.TunnelTypeVxlan;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpnInterfaceListInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.GetDpnInterfaceListOutput;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.interfacemanager.rpcs.rev160406.OdlInterfaceRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TepTypeExternal;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TepTypeHwvtep;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.op.rev160406.TepTypeInternal;
-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.TunnelOperStatus;
+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.rpcs.rev160406.IsDcgwPresentInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.IsDcgwPresentOutput;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rev160406.DcGatewayIpList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.extraroute.rds.map.extraroute.rds.DestPrefixes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentries.VrfEntry;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fibmanager.rev150330.vrfentrybase.RoutePaths;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.PortOpData;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntry;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.port.op.data.PortOpDataEntryKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.vpn.instance.op.data.entry.VpnToDpnList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn.instance.op.data.VpnInstanceOpDataEntry;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.RpcResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+@Singleton
 public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBase<StateTunnelList,
-        TunnelInterfaceStateListener> implements AutoCloseable{
+        TunnelInterfaceStateListener> {
     private static final Logger LOG = LoggerFactory.getLogger(TunnelInterfaceStateListener.class);
     private final DataBroker dataBroker;
-    private final IBgpManager bgpManager;
     private final IFibManager fibManager;
-    private final ItmRpcService itmRpcService;
-    private OdlInterfaceRpcService intfRpcService;
-    private VpnInterfaceManager vpnInterfaceManager;
-    private VpnSubnetRouteHandler vpnSubnetRouteHandler;
+    private final OdlInterfaceRpcService intfRpcService;
+    private final VpnInterfaceManager vpnInterfaceManager;
+    private final VpnSubnetRouteHandler vpnSubnetRouteHandler;
+    private final JobCoordinator jobCoordinator;
+
     protected enum UpdateRouteAction {
         ADVERTISE_ROUTE, WITHDRAW_ROUTE
     }
 
-    protected  enum TunnelAction {
+    protected enum TunnelAction {
         TUNNEL_EP_ADD,
         TUNNEL_EP_DELETE,
         TUNNEL_EP_UPDATE
     }
+
     /**
-     * Responsible for listening to tunnel interface state change
-     *
-     * @param dataBroker dataBroker
-     * @param bgpManager bgpManager
-     * @param fibManager fibManager
-     * @param itmRpcService itmRpcService
+     * Responsible for listening to tunnel interface state change.
      */
+    @Inject
     public TunnelInterfaceStateListener(final DataBroker dataBroker,
-                                        final IBgpManager bgpManager,
-                                        final IFibManager fibManager,
-                                        final ItmRpcService itmRpcService,
-                                        final OdlInterfaceRpcService ifaceMgrRpcService,
-                                        final VpnInterfaceManager vpnInterfaceManager,
-                                        final VpnSubnetRouteHandler vpnSubnetRouteHandler) {
+        final IFibManager fibManager,
+        final OdlInterfaceRpcService ifaceMgrRpcService,
+        final VpnInterfaceManager vpnInterfaceManager,
+        final VpnSubnetRouteHandler vpnSubnetRouteHandler,
+        final JobCoordinator jobCoordinator) {
         super(StateTunnelList.class, TunnelInterfaceStateListener.class);
         this.dataBroker = dataBroker;
-        this.bgpManager = bgpManager;
         this.fibManager = fibManager;
-        this.itmRpcService = itmRpcService;
         this.intfRpcService = ifaceMgrRpcService;
         this.vpnInterfaceManager = vpnInterfaceManager;
         this.vpnSubnetRouteHandler = vpnSubnetRouteHandler;
+        this.jobCoordinator = jobCoordinator;
     }
 
+    @PostConstruct
     public void start() {
         LOG.info("{} start", getClass().getSimpleName());
         registerListener(LogicalDatastoreType.OPERATIONAL, dataBroker);
@@ -111,51 +129,110 @@ public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBas
 
     @Override
     protected void remove(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList del) {
-        LOG.trace("Tunnel deletion---- {}", del);
-        handleTunnelEventForDPN(del, UpdateRouteAction.WITHDRAW_ROUTE , TunnelAction.TUNNEL_EP_DELETE);
+        LOG.trace("remove: Tunnel deletion---- {}", del);
+        if (isGreTunnel(del)) {
+            programDcGwLoadBalancingGroup(del, NwConstants.DEL_FLOW);
+        }
+        handleTunnelEventForDPN(del, UpdateRouteAction.WITHDRAW_ROUTE, TunnelAction.TUNNEL_EP_DELETE);
     }
 
     @Override
-    protected void update(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList original, StateTunnelList update) {
-        LOG.trace("Tunnel updation---- {}", update);
-        LOG.trace("ITM Tunnel {} of type {} state event changed from :{} to :{}",
-                update.getTunnelInterfaceName(),
-                fibManager.getTransportTypeStr(update.getTransportType().toString()),
-                original.getOperState(), update.getOperState());
-        //withdraw all prefixes in all vpns for this dpn
+    protected void update(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList original,
+        StateTunnelList update) {
+        LOG.trace("update: Tunnel updation---- {}", update);
+        LOG.info("update: ITM Tunnel {} of type {} state event changed from :{} to :{}",
+            update.getTunnelInterfaceName(),
+            fibManager.getTransportTypeStr(update.getTransportType().toString()),
+            original.getOperState(), update.getOperState());
         TunnelOperStatus tunOpStatus = update.getOperState();
-        if ((tunOpStatus != TunnelOperStatus.Down) && (tunOpStatus != TunnelOperStatus.Up)) {
-            LOG.trace("Returning from unsupported tunnelOperStatus {}", tunOpStatus);
+        if (tunOpStatus != TunnelOperStatus.Down && tunOpStatus != TunnelOperStatus.Up) {
+            LOG.info("update: Returning from unsupported tunnelOperStatus {} for tunnel interface {}", tunOpStatus,
+                    update.getTunnelInterfaceName());
+            return;
+        }
+        if (isGreTunnel(update)) {
+            programDcGwLoadBalancingGroup(update, NwConstants.MOD_FLOW);
+        }
+
+        //Remove the corresponding nexthop from the routepath under extraroute in fibentries.
+        BigInteger srcDpnId = new BigInteger(update.getSrcInfo().getTepDeviceId());
+        String srcTepIp = String.valueOf(update.getSrcInfo().getTepIp().getValue());
+        List<VpnInstanceOpDataEntry> vpnInstanceOpData = VpnUtil.getAllVpnInstanceOpData(dataBroker);
+        if (vpnInstanceOpData == null) {
+            LOG.trace("update: No vpnInstanceOpdata present");
             return;
         }
-        boolean isTunnelUp = (tunOpStatus == TunnelOperStatus.Up);
-        handleTunnelEventForDPN(update,
-                                isTunnelUp ? UpdateRouteAction.ADVERTISE_ROUTE : UpdateRouteAction.WITHDRAW_ROUTE,
-                                TunnelAction.TUNNEL_EP_UPDATE);
+        WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
+        if (tunOpStatus == TunnelOperStatus.Up) {
+            handleTunnelEventForDPN(update, UpdateRouteAction.ADVERTISE_ROUTE, TunnelAction.TUNNEL_EP_ADD);
+        } else {
+            vpnInstanceOpData.stream().filter(opData -> {
+                if (opData.getVpnToDpnList() == null) {
+                    return false;
+                }
+                return opData.getVpnToDpnList().stream().anyMatch(vpnToDpn -> vpnToDpn.getDpnId().equals(srcDpnId));
+            }).forEach(opData -> {
+                List<DestPrefixes> prefixes = VpnExtraRouteHelper.getExtraRouteDestPrefixes(dataBroker,
+                        opData.getVpnId());
+                prefixes.forEach(destPrefix -> {
+                    VrfEntry vrfEntry = VpnUtil.getVrfEntry(dataBroker, opData.getVrfId(),
+                            destPrefix.getDestPrefix());
+                    if (vrfEntry == null || vrfEntry.getRoutePaths() == null) {
+                        return;
+                    }
+                    List<RoutePaths> routePaths = vrfEntry.getRoutePaths();
+                    routePaths.forEach(routePath -> {
+                        if (routePath.getNexthopAddress().equals(srcTepIp)) {
+                            fibManager.updateRoutePathForFibEntry(dataBroker, opData.getVrfId(),
+                                    destPrefix.getDestPrefix(), srcTepIp, routePath.getLabel(),
+                                    false, writeConfigTxn);
+                        }
+                    });
+                });
+            });
+        }
+        writeConfigTxn.submit();
     }
 
     @Override
     protected void add(InstanceIdentifier<StateTunnelList> identifier, StateTunnelList add) {
-        LOG.trace("Tunnel addition---- {}", add);
+        LOG.trace("add: Tunnel addition---- {}", add);
         TunnelOperStatus tunOpStatus = add.getOperState();
-        if ((tunOpStatus != TunnelOperStatus.Down) && (tunOpStatus != TunnelOperStatus.Up)) {
-            LOG.trace("Returning from unsupported tunnelOperStatus {}", tunOpStatus);
-            return;
-        }
-        boolean isTunnelUp = (tunOpStatus == TunnelOperStatus.Up);
-        if (!isTunnelUp) {
-            LOG.trace("Tunnel {} is not yet UP.",
+        if (tunOpStatus != TunnelOperStatus.Down && tunOpStatus != TunnelOperStatus.Up) {
+            LOG.info("add: Returning from unsupported tunnelOperStatus {} for tunnel interface {}", tunOpStatus,
                     add.getTunnelInterfaceName());
             return;
-        } else {
-            LOG.trace("ITM Tunnel ,type {} ,State is UP b/w src: {} and dest: {}",
-                    fibManager.getTransportTypeStr(add.getTransportType().toString()),
-                    add.getSrcInfo().getTepDeviceId(), add.getDstInfo().getTepDeviceId());
-            handleTunnelEventForDPN(add, UpdateRouteAction.ADVERTISE_ROUTE, TunnelAction.TUNNEL_EP_ADD);
+        }
+        if (tunOpStatus != TunnelOperStatus.Up) {
+            LOG.error("add: Tunnel {} is not yet UP.", add.getTunnelInterfaceName());
+        }
+        if (isGreTunnel(add)) {
+            programDcGwLoadBalancingGroup(add, NwConstants.ADD_FLOW);
+        }
+        LOG.info("add: ITM Tunnel ,type {} ,added between src: {} and dest: {}",
+            fibManager.getTransportTypeStr(add.getTransportType().toString()),
+            add.getSrcInfo().getTepDeviceId(), add.getDstInfo().getTepDeviceId());
+        handleTunnelEventForDPN(add, UpdateRouteAction.ADVERTISE_ROUTE, TunnelAction.TUNNEL_EP_ADD);
+    }
+
+    public enum TunnelEventProcessingMethod {
+        POPULATESUBNETS(0), MANAGEREMOTEROUTES(1);
+
+        private final int method;
+
+        TunnelEventProcessingMethod(int id) {
+            this.method = id;
+        }
+
+        public int getValue() {
+            return method;
         }
     }
 
-    private void handleTunnelEventForDPN(StateTunnelList stateTunnelList, UpdateRouteAction action, TunnelAction tunnelAction) {
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private void handleTunnelEventForDPN(StateTunnelList stateTunnelList, UpdateRouteAction action,
+        TunnelAction tunnelAction) {
         final BigInteger srcDpnId = new BigInteger(stateTunnelList.getSrcInfo().getTepDeviceId());
         final String srcTepIp = String.valueOf(stateTunnelList.getSrcInfo().getTepIp().getValue());
         String destTepIp = String.valueOf(stateTunnelList.getDstInfo().getTepIp().getValue());
@@ -163,16 +240,20 @@ public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBas
         BigInteger remoteDpnId = null;
         boolean isTepDeletedOnDpn = false;
 
-        LOG.trace("Handle tunnel event for srcDpn {} SrcTepIp {} DestTepIp {} ", srcDpnId, srcTepIp, destTepIp);
+        LOG.info("handleTunnelEventForDPN: Handle tunnel event for srcDpn {} SrcTepIp {} DestTepIp {} ",
+                srcDpnId, srcTepIp, destTepIp);
         int tunTypeVal = getTunnelType(stateTunnelList);
-
-        LOG.trace("tunTypeVal is {}", tunTypeVal);
-
+        LOG.trace("handleTunnelEventForDPN: tunTypeVal is {}", tunTypeVal);
         try {
             if (tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
-                LOG.trace(" Tunnel ADD event received for Dpn {} VTEP Ip {} ", srcDpnId, srcTepIp);
+                LOG.info("handleTunnelEventForDPN: Tunnel ADD event received for Dpn {} VTEP Ip {} destTepIp",
+                        srcDpnId, srcTepIp, destTepIp);
+                if (isTunnelInLogicalGroup(stateTunnelList)) {
+                    return;
+                }
             } else if (tunnelAction == TunnelAction.TUNNEL_EP_DELETE) {
-                LOG.trace(" Tunnel DELETE event received for Dpn {} VTEP Ip {} ", srcDpnId, srcTepIp);
+                LOG.info("handleTunnelEventForDPN: Tunnel DELETE event received for Dpn {} VTEP Ip {} DestTepIp",
+                        srcDpnId, srcTepIp, destTepIp);
                 // When tunnel EP is deleted on a DPN , VPN gets two deletion event.
                 // One for a DPN on which tunnel EP was deleted and another for other-end DPN.
                 // Update the adj for the vpninterfaces for a DPN on which TEP is deleted.
@@ -182,12 +263,14 @@ public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBas
                 try {
                     endpointIpForDPN = InterfaceUtils.getEndpointIpAddressForDPN(dataBroker, srcDpnId);
                 } catch (Exception e) {
+                    LOG.error("handleTunnelEventForDPN: Unable to resolve endpoint IP for srcDpn {}", srcDpnId);
                     /* this dpn does not have the VTEP */
                     endpointIpForDPN = null;
                 }
 
                 if (endpointIpForDPN == null) {
-                    LOG.trace("Tunnel TEP is deleted on Dpn {} VTEP Ip {}", srcDpnId, srcTepIp);
+                    LOG.info("handleTunnelEventForDPN: Tunnel TEP is deleted on Dpn {} VTEP Ip {} destTepIp", srcDpnId,
+                            srcTepIp, destTepIp);
                     isTepDeletedOnDpn = true;
                 }
             }
@@ -197,30 +280,38 @@ public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBas
             List<String> srcDpninterfacelist = new ArrayList<>();
             List<String> destDpninterfacelist = new ArrayList<>();
             try {
-                result = intfRpcService.getDpnInterfaceList(new GetDpnInterfaceListInputBuilder().setDpid(srcDpnId).build());
+                result =
+                    intfRpcService.getDpnInterfaceList(new GetDpnInterfaceListInputBuilder().setDpid(srcDpnId).build());
                 RpcResult<GetDpnInterfaceListOutput> rpcResult = result.get();
                 if (!rpcResult.isSuccessful()) {
-                    LOG.warn("RPC Call to GetDpnInterfaceList for dpnid {} returned with Errors {}", srcDpnId, rpcResult.getErrors());
+                    LOG.error("handleTunnelEventForDPN: RPC Call to GetDpnInterfaceList for srcDpnid {} srcTepIp {}"
+                            + " destTepIP {} returned with Errors {}", srcDpnId, srcTepIp, destTepIp,
+                            rpcResult.getErrors());
                 } else {
                     srcDpninterfacelist = rpcResult.getResult().getInterfacesList();
                 }
             } catch (Exception e) {
-                LOG.warn("Exception {} when querying for GetDpnInterfaceList for dpnid {}, trace {}", e, srcDpnId, e.getStackTrace());
+                LOG.error("handleTunnelEventForDPN: Exception {} when querying for GetDpnInterfaceList for srcDpnid {}"
+                        + " srcTepIp {} destTepIp {}, trace {}", e, srcDpnId, srcTepIp, destTepIp, e.getStackTrace());
             }
-
             // Get the list of VpnInterfaces from Intf Mgr for a destDPN only for internal tunnel.
             if (tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue()) {
                 remoteDpnId = new BigInteger(stateTunnelList.getDstInfo().getTepDeviceId());
                 try {
-                    result = intfRpcService.getDpnInterfaceList(new GetDpnInterfaceListInputBuilder().setDpid(remoteDpnId).build());
+                    result = intfRpcService.getDpnInterfaceList(
+                        new GetDpnInterfaceListInputBuilder().setDpid(remoteDpnId).build());
                     RpcResult<GetDpnInterfaceListOutput> rpcResult = result.get();
                     if (!rpcResult.isSuccessful()) {
-                        LOG.warn("RPC Call to GetDpnInterfaceList for dpnid {} returned with Errors {}", srcDpnId, rpcResult.getErrors());
+                        LOG.error("handleTunnelEventForDPN: RPC Call to GetDpnInterfaceList for remoteDpnid {}"
+                                + " srcTepIP {} destTepIp {} returned with Errors {}", remoteDpnId, srcTepIp,
+                                destTepIp, rpcResult.getErrors());
                     } else {
                         destDpninterfacelist = rpcResult.getResult().getInterfacesList();
                     }
                 } catch (Exception e) {
-                    LOG.warn("Exception {} when querying for GetDpnInterfaceList for dpnid {}, trace {}", e, srcDpnId, e.getStackTrace());
+                    LOG.error("handleTunnelEventForDPN: Exception {} when querying for GetDpnInterfaceList"
+                            + " for remoteDpnid {} srcTepIp {} destTepIp {}, trace {}", e, remoteDpnId,
+                            srcTepIp, destTepIp, e.getStackTrace());
                 }
             }
 
@@ -230,41 +321,23 @@ public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBas
              */
             Iterator<String> interfacelistIter = srcDpninterfacelist.iterator();
             String intfName = null;
-            List<Uuid> subnetList = new ArrayList<Uuid>();
-            Map<Long, String> vpnIdRdMap = new HashMap<Long, String>();
+            List<Uuid> subnetList = new ArrayList<>();
+            Map<Long, String> vpnIdRdMap = new HashMap<>();
+            Set<String> listVpnName = new HashSet<String>();
 
             while (interfacelistIter.hasNext()) {
                 intfName = interfacelistIter.next();
-                final VpnInterface vpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, intfName);
+                VpnInterface vpnInterface =
+                     VpnUtil.getConfiguredVpnInterface(dataBroker, intfName);
                 if (vpnInterface != null) {
-
-                    DataStoreJobCoordinator dataStoreCoordinator = DataStoreJobCoordinator.getInstance();
-                    dataStoreCoordinator.enqueueJob("VPNINTERFACE-" + intfName,
-                            new UpdateVpnInterfaceOnTunnelEvent(dataBroker,
-                                    vpnInterfaceManager,
-                                    tunnelAction,
-                                    vpnInterface,
-                                    stateTunnelList,
-                                    isTepDeletedOnDpn));
-
-                    // Populate the List of subnets
-                    InstanceIdentifier<PortOpDataEntry> portOpIdentifier = InstanceIdentifier.builder(PortOpData.class).
-                            child(PortOpDataEntry.class, new PortOpDataEntryKey(intfName)).build();
-                    Optional<PortOpDataEntry> optionalPortOp = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
-                    if (optionalPortOp.isPresent()) {
-                        Uuid subnetId = optionalPortOp.get().getSubnetId();
-                        if (!subnetList.contains(subnetId)) {
-                            subnetList.add(subnetId);
-                        }
-                    }
-
-                    //Populate the map for VpnId-to-Rd
-                    long vpnId = VpnUtil.getVpnId(dataBroker, vpnInterface.getVpnInstanceName());
-                    rd = VpnUtil.getVpnRd(dataBroker, vpnInterface.getVpnInstanceName());
-                    vpnIdRdMap.put(vpnId, rd);
+                    listVpnName.addAll(VpnHelper
+                        .getVpnInterfaceVpnInstanceNamesString(vpnInterface.getVpnInstanceNames()));
+                    handleTunnelEventForDPNVpn(stateTunnelList, action, vpnIdRdMap,
+                                         tunnelAction, isTepDeletedOnDpn,
+                                         subnetList, TunnelEventProcessingMethod.POPULATESUBNETS,
+                                         vpnInterface);
                 }
             }
-
             /*
              * Iterate over the list of VpnInterface for destDPN and get the prefix .
              * Create remote rule for each of those prefix on srcDPN.
@@ -272,103 +345,179 @@ public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBas
             interfacelistIter = destDpninterfacelist.iterator();
             while (interfacelistIter.hasNext()) {
                 intfName = interfacelistIter.next();
-                final VpnInterface vpnInterface = VpnUtil.getOperationalVpnInterface(dataBroker, intfName);
+                VpnInterface vpnInterface =
+                    VpnUtil.getConfiguredVpnInterface(dataBroker, intfName);
                 if (vpnInterface != null) {
-                    List<Adjacency> adjList = vpnInterface.getAugmentation(Adjacencies.class).getAdjacency();
-                    String prefix = null;
-                    long vpnId = VpnUtil.getVpnId(dataBroker, vpnInterface.getVpnInstanceName());
-                    if (vpnIdRdMap.containsKey(vpnId)) {
-                        rd = vpnIdRdMap.get(vpnId);
-                        LOG.trace(" Remote DpnId {} VpnId {} rd {} VpnInterface {}", remoteDpnId, vpnId, rd , vpnInterface);
-                        for (Adjacency adj : adjList) {
-                            prefix = adj.getIpAddress();
-                            if ((tunnelAction == TunnelAction.TUNNEL_EP_ADD) &&
-                                    (tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue())) {
-                                fibManager.manageRemoteRouteOnDPN(true, srcDpnId, vpnId, rd, prefix, destTepIp);
-                            }
-
-                            if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE) &&
-                                    (tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue())) {
-                                fibManager.manageRemoteRouteOnDPN(false, srcDpnId, vpnId, rd, prefix, destTepIp);
-                            }
-                        }
-                    }
+                    handleTunnelEventForDPNVpn(stateTunnelList, action, vpnIdRdMap,
+                                    tunnelAction, isTepDeletedOnDpn,
+                                    subnetList, TunnelEventProcessingMethod.MANAGEREMOTEROUTES,
+                                    vpnInterface);
                 }
             }
 
             //Iterate over the VpnId-to-Rd map.
-            Iterator<Map.Entry<Long, String>> entries = vpnIdRdMap.entrySet().iterator();
-            while (entries.hasNext()) {
-                Map.Entry<Long, String> entry = entries.next();
+            for (Map.Entry<Long, String> entry : vpnIdRdMap.entrySet()) {
                 Long vpnId = entry.getKey();
                 rd = entry.getValue();
-                if ((tunnelAction == TunnelAction.TUNNEL_EP_ADD) &&
-                        (tunTypeVal == VpnConstants.ITMTunnelLocType.External.getValue())) {
+                if (tunnelAction == TunnelAction.TUNNEL_EP_ADD
+                    && tunTypeVal == VpnConstants.ITMTunnelLocType.External.getValue()) {
                     fibManager.populateExternalRoutesOnDpn(srcDpnId, vpnId, rd, srcTepIp, destTepIp);
-                } else if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE) &&
-                        (tunTypeVal == VpnConstants.ITMTunnelLocType.External.getValue())) {
+                } else if (tunnelAction == TunnelAction.TUNNEL_EP_DELETE
+                    && tunTypeVal == VpnConstants.ITMTunnelLocType.External.getValue()) {
                     fibManager.cleanUpExternalRoutesOnDpn(srcDpnId, vpnId, rd, srcTepIp, destTepIp);
                 }
             }
-
-            if (tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
-                for (Uuid subnetId : subnetList) {
-                    // Populate the List of subnets
-                    vpnSubnetRouteHandler.updateSubnetRouteOnTunnelUpEvent(subnetId, srcDpnId);
+            if (listVpnName.size() >= 1) {
+                if (tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
+                    for (Uuid subnetId : subnetList) {
+                        // Populate the List of subnets
+                        vpnSubnetRouteHandler.updateSubnetRouteOnTunnelUpEvent(subnetId, srcDpnId);
+                    }
+                }
+                if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE) && isTepDeletedOnDpn) {
+                    for (Uuid subnetId : subnetList) {
+                        // Populate the List of subnets
+                        vpnSubnetRouteHandler.updateSubnetRouteOnTunnelDownEvent(subnetId, srcDpnId);
+                    }
                 }
             }
+        } catch (RuntimeException e) {
+            LOG.error("handleTunnelEventForDpn: Unable to handle the tunnel event for srcDpnId {} srcTepIp {}"
+                     + " remoteDpnId {} destTepIp {}", srcDpnId, srcTepIp, remoteDpnId, destTepIp, e);
+        }
+    }
+
+    // TODO Clean up the exception handling
+    @SuppressWarnings("checkstyle:IllegalCatch")
+    private void handleTunnelEventForDPNVpn(StateTunnelList stateTunnelList, UpdateRouteAction action,
+                                            Map<Long, String> vpnIdRdMap, TunnelAction tunnelAction,
+                                            boolean isTepDeletedOnDpn, List<Uuid> subnetList,
+                                            TunnelEventProcessingMethod method,
+                                            VpnInterface cfgVpnInterface) {
+        String rd;
+        String intfName = cfgVpnInterface.getName();
+        final BigInteger srcDpnId = new BigInteger(stateTunnelList.getSrcInfo().getTepDeviceId());
+        String destTepIp = String.valueOf(stateTunnelList.getDstInfo().getTepIp().getValue());
+        String srcTepIp = String.valueOf(stateTunnelList.getSrcInfo().getTepIp().getValue());
+        int tunTypeVal = getTunnelType(stateTunnelList);
+        BigInteger remoteDpnId = null;
+        if (tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue()) {
+            remoteDpnId = new BigInteger(stateTunnelList.getDstInfo().getTepDeviceId());
+        }
+        if (cfgVpnInterface.getVpnInstanceNames() == null) {
+            LOG.warn("handleTunnelEventForDpn: no vpnName found for interface {}", intfName);
+            return;
+        }
+        for (VpnInstanceNames vpnInstance : cfgVpnInterface.getVpnInstanceNames()) {
+            String vpnName = vpnInstance.getVpnName();
+            if (method == TunnelEventProcessingMethod.POPULATESUBNETS) {
+                Optional<VpnInterfaceOpDataEntry> opVpnInterface = VpnUtil
+                           .getVpnInterfaceOpDataEntry(dataBroker, intfName, vpnName);
+                if (opVpnInterface.isPresent() && !opVpnInterface.get().isScheduledForRemove()) {
+                    VpnInterfaceOpDataEntry vpnInterface  = opVpnInterface.get();
+                    jobCoordinator.enqueueJob("VPNINTERFACE-" + intfName,
+                            new UpdateVpnInterfaceOnTunnelEvent(tunnelAction,
+                                    vpnInterface,
+                                    stateTunnelList,
+                                    isTepDeletedOnDpn));
 
-            if ((tunnelAction == TunnelAction.TUNNEL_EP_DELETE) && isTepDeletedOnDpn) {
-                for (Uuid subnetId : subnetList) {
                     // Populate the List of subnets
-                    vpnSubnetRouteHandler.updateSubnetRouteOnTunnelDownEvent(subnetId, srcDpnId);
+                    InstanceIdentifier<PortOpDataEntry> portOpIdentifier =
+                        InstanceIdentifier.builder(PortOpData.class).child(PortOpDataEntry.class,
+                            new PortOpDataEntryKey(intfName)).build();
+                    Optional<PortOpDataEntry> optionalPortOp =
+                        VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, portOpIdentifier);
+                    if (optionalPortOp.isPresent()) {
+                        List<Uuid> subnetIdList = optionalPortOp.get().getSubnetIds();
+                        if (subnetIdList != null) {
+                            for (Uuid subnetId : subnetIdList) {
+                                if (!subnetList.contains(subnetId)) {
+                                    subnetList.add(subnetId);
+                                }
+                            }
+                        }
+                    }
+                    //Populate the map for VpnId-to-Rd
+                    long vpnId = VpnUtil.getVpnId(dataBroker, vpnName);
+                    rd = VpnUtil.getVpnRd(dataBroker, vpnName);
+                    vpnIdRdMap.put(vpnId, rd);
+                }
+            } else if (method == TunnelEventProcessingMethod.MANAGEREMOTEROUTES) {
+                Optional<VpnInterfaceOpDataEntry> opVpnInterface = VpnUtil.getVpnInterfaceOpDataEntry(dataBroker,
+                                                                                intfName, vpnName);
+                if (opVpnInterface.isPresent()) {
+                    VpnInterfaceOpDataEntry vpnInterface  = opVpnInterface.get();
+                    AdjacenciesOp adjacencies = vpnInterface.getAugmentation(AdjacenciesOp.class);
+                    List<Adjacency> adjList = adjacencies != null ? adjacencies.getAdjacency()
+                            : Collections.emptyList();
+                    String prefix = null;
+                    long vpnId = VpnUtil.getVpnId(dataBroker, vpnInterface.getVpnInstanceName());
+                    if (vpnIdRdMap.containsKey(vpnId)) {
+                        rd = vpnIdRdMap.get(vpnId);
+                        LOG.info("handleTunnelEventForDPN: Remote DpnId {} VpnId {} rd {} VpnInterface {} srcTepIp "
+                                + "{} destTepIp {}", remoteDpnId, vpnId, rd , vpnInterface, srcTepIp, destTepIp);
+                        for (Adjacency adj : adjList) {
+                            prefix = adj.getIpAddress();
+                            long label = adj.getLabel();
+                            if (tunnelAction == TunnelAction.TUNNEL_EP_ADD
+                                && tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue()) {
+                                fibManager.manageRemoteRouteOnDPN(true, srcDpnId, vpnId, rd, prefix, destTepIp, label);
+                            }
+
+                            if (tunnelAction == TunnelAction.TUNNEL_EP_DELETE
+                                && tunTypeVal == VpnConstants.ITMTunnelLocType.Internal.getValue()) {
+                                fibManager.manageRemoteRouteOnDPN(false, srcDpnId, vpnId, rd, prefix, destTepIp, label);
+                            }
+                        }
+                    }
                 }
             }
-        } catch (Exception e) {
-            LOG.error("Unable to handle the tunnel event.", e);
-            return;
         }
-
     }
 
-
     private class UpdateVpnInterfaceOnTunnelEvent implements Callable {
-        private VpnInterface vpnInterface;
-        private StateTunnelList stateTunnelList;
-        private VpnInterfaceManager vpnInterfaceManager;
-        private  DataBroker broker;
-        private TunnelAction tunnelAction;
-        private boolean isTepDeletedOnDpn;
-
-        UpdateVpnInterfaceOnTunnelEvent(DataBroker broker,
-                                        VpnInterfaceManager vpnInterfaceManager,
-                                        TunnelAction tunnelAction,
-                                        VpnInterface vpnInterface,
-                                        StateTunnelList stateTunnelList,
-                                        boolean isTepDeletedOnDpn) {
-            this.broker = broker;
-            this.vpnInterfaceManager = vpnInterfaceManager;
+        private final VpnInterfaceOpDataEntry vpnInterface;
+        private final StateTunnelList stateTunnelList;
+        private final TunnelAction tunnelAction;
+        private final boolean isTepDeletedOnDpn;
+
+        UpdateVpnInterfaceOnTunnelEvent(TunnelAction tunnelAction,
+            VpnInterfaceOpDataEntry vpnInterface,
+            StateTunnelList stateTunnelList,
+            boolean isTepDeletedOnDpn) {
             this.stateTunnelList = stateTunnelList;
             this.vpnInterface = vpnInterface;
             this.tunnelAction = tunnelAction;
             this.isTepDeletedOnDpn = isTepDeletedOnDpn;
         }
 
+        @Override
         public List<ListenableFuture<Void>> call() throws Exception {
+            WriteTransaction writeConfigTxn = dataBroker.newWriteOnlyTransaction();
+            WriteTransaction writeOperTxn = dataBroker.newWriteOnlyTransaction();
+            List<ListenableFuture<Void>> futures = new ArrayList<>();
 
-            if(tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
-                vpnInterfaceManager.updateVpnInterfaceOnTepAdd(vpnInterface, stateTunnelList);
+            if (tunnelAction == TunnelAction.TUNNEL_EP_ADD) {
+                vpnInterfaceManager.updateVpnInterfaceOnTepAdd(vpnInterface,
+                                                            stateTunnelList,
+                                                            writeConfigTxn,
+                                                            writeOperTxn);
             }
 
-            if((tunnelAction == TunnelAction.TUNNEL_EP_DELETE) && isTepDeletedOnDpn) {
-                vpnInterfaceManager.updateVpnInterfaceOnTepDelete(vpnInterface, stateTunnelList);
+            if (tunnelAction == TunnelAction.TUNNEL_EP_DELETE && isTepDeletedOnDpn) {
+                vpnInterfaceManager.updateVpnInterfaceOnTepDelete(vpnInterface,
+                                                                stateTunnelList,
+                                                                writeConfigTxn,
+                                                                writeOperTxn);
             }
 
-            return null;
+            futures.add(writeOperTxn.submit());
+            futures.add(writeConfigTxn.submit());
+            return futures;
         }
     }
 
-    private int getTunnelType (StateTunnelList stateTunnelList) {
+    private int getTunnelType(StateTunnelList stateTunnelList) {
         int tunTypeVal = 0;
         if (stateTunnelList.getDstInfo().getTepDeviceType() == TepTypeInternal.class) {
             tunTypeVal = VpnConstants.ITMTunnelLocType.Internal.getValue();
@@ -381,4 +530,48 @@ public class TunnelInterfaceStateListener extends AsyncDataTreeChangeListenerBas
         }
         return tunTypeVal;
     }
+
+    private boolean isGreTunnel(StateTunnelList del) {
+        return del.getTransportType() == TunnelTypeMplsOverGre.class;
+    }
+
+    private void programDcGwLoadBalancingGroup(StateTunnelList tunnelState, int addOrRemove) {
+        IpAddress dcGwIp = tunnelState.getDstInfo().getTepIp();
+        String dcGwIpAddress = String.valueOf(dcGwIp.getValue());
+        List<String> availableDcGws = getDcGwIps();
+        BigInteger dpId = new BigInteger(tunnelState.getSrcInfo().getTepDeviceId());
+        boolean isTunnelUp = TunnelOperStatus.Up == tunnelState.getOperState();
+        fibManager.programDcGwLoadBalancingGroup(availableDcGws, dpId, dcGwIpAddress, addOrRemove, isTunnelUp);
+    }
+
+    private List<String> getDcGwIps() {
+        InstanceIdentifier<DcGatewayIpList> dcGatewayIpListid =
+                InstanceIdentifier.builder(DcGatewayIpList.class).build();
+        DcGatewayIpList dcGatewayIpListConfig =
+                MDSALUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, dcGatewayIpListid).orNull();
+        if (dcGatewayIpListConfig == null) {
+            return Collections.EMPTY_LIST;
+        }
+        return dcGatewayIpListConfig.getDcGatewayIp()
+                .stream()
+                .filter(dcGwIp -> dcGwIp.getTunnnelType().equals(TunnelTypeMplsOverGre.class))
+                .map(dcGwIp -> String.valueOf(dcGwIp.getIpAddress().getValue())).sorted()
+                .collect(toList());
+    }
+
+    private boolean isTunnelInLogicalGroup(StateTunnelList stateTunnelList) {
+        String ifaceName = stateTunnelList.getTunnelInterfaceName();
+        if (getTunnelType(stateTunnelList) == VpnConstants.ITMTunnelLocType.Internal.getValue()) {
+            Interface configIface = InterfaceUtils.getInterface(dataBroker, stateTunnelList.getTunnelInterfaceName());
+            IfTunnel ifTunnel = configIface != null ? configIface.getAugmentation(IfTunnel.class) : null;
+            if (ifTunnel != null && ifTunnel.getTunnelInterfaceType().isAssignableFrom(TunnelTypeVxlan.class)) {
+                ParentRefs refs = configIface.getAugmentation(ParentRefs.class);
+                if (refs != null && !Strings.isNullOrEmpty(refs.getParentInterface())) {
+                    return true; //multiple VxLAN tunnels enabled, i.e. only logical tunnel should be treated
+                }
+            }
+        }
+        LOG.trace("isTunnelInLogicalGroup: MULTIPLE_VxLAN_TUNNELS: ignoring the tunnel event for {}", ifaceName);
+        return false;
+    }
 }