Fix For ECMP bucket missing on deleting l3vpn 08/74908/5
authoreswanit <swati.udhavrao.niture@ericsson.com>
Wed, 21 Nov 2018 10:53:36 +0000 (16:23 +0530)
committerSam Hague <shague@redhat.com>
Wed, 23 Jan 2019 11:11:00 +0000 (11:11 +0000)
Description:Adding refreshFib on submitting write
config transaction to avoid timing issues

Change-Id: Ief4d56ae0e18348ff16d29981a8bd28eeb0bbc34
Signed-off-by: eswanit <swati.udhavrao.niture@ericsson.com>
vpnmanager/api/src/main/java/org/opendaylight/netvirt/vpnmanager/api/IVpnManager.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/InterfaceStateChangeListener.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/TunnelEndPointChangeListener.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnInterfaceManager.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnManagerImpl.java
vpnmanager/impl/src/main/java/org/opendaylight/netvirt/vpnmanager/VpnRpcServiceImpl.java

index c9c57209e079fb6e33d52da49db1f4e6a7987bb5..aea160974f4811ca43b03587852ee7dc4f218c62 100644 (file)
@@ -35,7 +35,8 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev15060
 public interface IVpnManager {
     void addExtraRoute(String vpnName, String destination, String nextHop, String rd, @Nullable String routerID,
         Long l3vni, RouteOrigin origin, @Nullable String intfName, @Nullable Adjacency operationalAdj,
-        VrfEntry.EncapType encapType, @Nonnull TypedWriteTransaction<Configuration> confTx);
+        VrfEntry.EncapType encapType, Set<String> prefixListForRefreshFib,
+        @Nonnull TypedWriteTransaction<Configuration> confTx);
 
     void delExtraRoute(String vpnName, String destination, String nextHop, String rd, @Nullable String routerID,
         @Nullable String intfName, @Nonnull TypedWriteTransaction<Configuration> confTx,
index c5b17eb91a770eac514711664f1f72e58ec6e32e..c06fc1c2d6d30c1f292ac23e4ea429777ce978ce 100644 (file)
@@ -17,19 +17,27 @@ import com.google.common.collect.Table;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 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.common.api.data.LogicalDatastoreType;
 import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.netvirt.fibmanager.api.IFibManager;
 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
 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;
@@ -53,6 +61,7 @@ public class InterfaceStateChangeListener
     private final VpnInterfaceManager vpnInterfaceManager;
     private final VpnUtil vpnUtil;
     private final JobCoordinator jobCoordinator;
+    private final IFibManager fibManager;
 
     Table<OperStatus, OperStatus, IntfTransitionState> stateTable = HashBasedTable.create();
 
@@ -78,13 +87,14 @@ public class InterfaceStateChangeListener
 
     @Inject
     public InterfaceStateChangeListener(final DataBroker dataBroker, final VpnInterfaceManager vpnInterfaceManager,
-            final VpnUtil vpnUtil, final JobCoordinator jobCoordinator) {
+            final VpnUtil vpnUtil, final JobCoordinator jobCoordinator, final IFibManager fibManager) {
         super(Interface.class, InterfaceStateChangeListener.class);
         this.dataBroker = dataBroker;
         this.txRunner = new ManagedNewTransactionRunnerImpl(dataBroker);
         this.vpnInterfaceManager = vpnInterfaceManager;
         this.vpnUtil = vpnUtil;
         this.jobCoordinator = jobCoordinator;
+        this.fibManager = fibManager;
         initialize();
     }
 
@@ -117,6 +127,10 @@ public class InterfaceStateChangeListener
                 jobCoordinator.enqueueJob("VPNINTERFACE-" + intrf.getName(), () -> {
                     List<ListenableFuture<Void>> futures = new ArrayList<>(3);
                     futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, writeInvTxn -> {
+                        //map of prefix and vpn name used, as entry in prefix-to-interface datastore
+                        // is prerequisite for refresh Fib to avoid race condition leading to missing remote next hop
+                        // in bucket actions on bgp-vpn delete
+                        Map<String, Set<String>> mapOfRdAndPrefixesForRefreshFib = new HashMap<>();
                         ListenableFuture<Void> configFuture
                             = txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, writeConfigTxn -> {
                                 ListenableFuture<Void> operFuture
@@ -152,10 +166,11 @@ public class InterfaceStateChangeListener
                                                     final int ifIndex = intrf.getIfIndex();
                                                     LOG.info("VPN Interface add event - intfName {} onto vpnName {}"
                                                             + " running oper-driven", vpnIf.getName(), vpnName);
+                                                    Set<String> prefixes = new HashSet<>();
                                                     vpnInterfaceManager.processVpnInterfaceUp(dpnId, vpnIf, primaryRd,
                                                             ifIndex, false, writeConfigTxn, writeOperTxn, writeInvTxn,
-                                                            intrf, vpnName);
-
+                                                            intrf, vpnName, prefixes);
+                                                    mapOfRdAndPrefixesForRefreshFib.put(primaryRd, prefixes);
                                                 }
                                             }
 
@@ -164,6 +179,9 @@ public class InterfaceStateChangeListener
                                 futures.add(operFuture);
                                 operFuture.get(); //Synchronous submit of operTxn
                             });
+                        Futures.addCallback(configFuture,
+                                new VpnInterfaceCallBackHandler(mapOfRdAndPrefixesForRefreshFib),
+                                MoreExecutors.directExecutor());
                         futures.add(configFuture);
                         //TODO: Allow immediateFailedFuture from writeCfgTxn to cancel writeInvTxn as well.
                         Futures.addCallback(configFuture, new PostVpnInterfaceThreadWorker(intrf.getName(), true,
@@ -258,80 +276,92 @@ public class InterfaceStateChangeListener
                         update.getName());
                 jobCoordinator.enqueueJob("VPNINTERFACE-" + ifName, () -> {
                     List<ListenableFuture<Void>> futures = new ArrayList<>(3);
-                    futures.add(
-                        txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, writeOperTxn -> futures.add(
-                            txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
-                                writeConfigTxn -> futures.add(
-                                    txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, writeInvTxn -> {
-                                        final VpnInterface vpnIf =
-                                            vpnUtil.getConfiguredVpnInterface(ifName);
-                                        if (vpnIf != null) {
-                                            final int ifIndex = update.getIfIndex();
-                                            BigInteger dpnId;
-                                            try {
-                                                dpnId = InterfaceUtils.getDpIdFromInterface(update);
-                                            } catch (Exception e) {
-                                                LOG.error("remove: Unable to retrieve dpnId for interface {}", ifName,
-                                                    e);
-                                                return;
-                                            }
-                                            IntfTransitionState state = getTransitionState(original.getOperStatus(),
-                                                update.getOperStatus());
-                                            if (state.equals(IntfTransitionState.STATE_IGNORE)) {
-                                                LOG.info("InterfaceStateChangeListener: Interface {} state original {}"
-                                                        + "updated {} not handled", ifName, original.getOperStatus(),
-                                                    update.getOperStatus());
-                                                return;
-                                            }
-                                            if (state.equals(IntfTransitionState.STATE_UP)) {
-                                                for (VpnInstanceNames vpnInterfaceVpnInstance :
+                    futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, writeOperTxn -> {
+                        //map of prefix and vpn name used, as entry in prefix-to-interface datastore
+                        // is prerequisite for refresh Fib to avoid race condition leading to missing remote
+                        // next hop in bucket actions on bgp-vpn delete
+                        Map<String, Set<String>> mapOfRdAndPrefixesForRefreshFib = new HashMap<>();
+                        ListenableFuture<Void> configTxFuture =
+                                txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, writeConfigTxn ->
+                                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
+                                        writeInvTxn -> {
+                                            final VpnInterface vpnIf = vpnUtil.getConfiguredVpnInterface(ifName);
+                                            if (vpnIf != null) {
+                                                final int ifIndex = update.getIfIndex();
+                                                BigInteger dpnId;
+                                                try {
+                                                    dpnId = InterfaceUtils.getDpIdFromInterface(update);
+                                                } catch (Exception e) {
+                                                    LOG.error("remove: Unable to retrieve dpnId for interface {}",
+                                                        ifName, e);
+                                                    return;
+                                                }
+                                                IntfTransitionState state = getTransitionState(
+                                                        original.getOperStatus(), update.getOperStatus());
+                                                if (state.equals(IntfTransitionState.STATE_IGNORE)) {
+                                                    LOG.info("InterfaceStateChangeListener: Interface {} state "
+                                                         + "original {}" + "updated {} not handled", ifName,
+                                                         original.getOperStatus(), update.getOperStatus());
+                                                    return;
+                                                }
+                                                if (state.equals(IntfTransitionState.STATE_UP)) {
+                                                    for (VpnInstanceNames vpnInterfaceVpnInstance :
                                                         requireNonNullElse(vpnIf.getVpnInstanceNames(),
-                                                            Collections.<VpnInstanceNames>emptyList())) {
-                                                    String vpnName = vpnInterfaceVpnInstance.getVpnName();
-                                                    String primaryRd = vpnUtil.getPrimaryRd(vpnName);
-                                                    if (!vpnInterfaceManager.isVpnInstanceReady(vpnName)) {
-                                                        LOG.error(
-                                                            "VPN Interface update event - intfName {} onto vpnName {} "
-                                                                + "running oper-driven UP, VpnInstance not ready,"
-                                                                + " holding on", vpnIf.getName(), vpnName);
-                                                    } else if (vpnUtil.isVpnPendingDelete(primaryRd)) {
-                                                        LOG.error("update: Ignoring UP event for vpnInterface {}, as "
-                                                            + "vpnInstance {} with primaryRd {} is already marked for"
-                                                            + " deletion", vpnIf.getName(), vpnName, primaryRd);
-                                                    } else {
-                                                        vpnInterfaceManager.processVpnInterfaceUp(dpnId, vpnIf,
-                                                            primaryRd,
-                                                            ifIndex, true, writeConfigTxn, writeOperTxn, writeInvTxn,
-                                                            update, vpnName);
+                                                        Collections.<VpnInstanceNames>emptyList())) {
+                                                        String vpnName = vpnInterfaceVpnInstance.getVpnName();
+                                                        String primaryRd = vpnUtil.getPrimaryRd(vpnName);
+                                                        Set<String> prefixes = new HashSet<>();
+                                                        if (!vpnInterfaceManager.isVpnInstanceReady(vpnName)) {
+                                                            LOG.error("VPN Interface update event - intfName {} "
+                                                                + "onto vpnName {} running oper-driven UP, "
+                                                                + "VpnInstance not ready, holding on",
+                                                                vpnIf.getName(), vpnName);
+                                                        } else if (vpnUtil.isVpnPendingDelete(primaryRd)) {
+                                                            LOG.error("update: Ignoring UP event for vpnInterface "
+                                                                + "{}, as vpnInstance {} with primaryRd {} is "
+                                                                + "already marked for deletion ",
+                                                                vpnIf.getName(), vpnName, primaryRd);
+                                                        } else {
+                                                            vpnInterfaceManager.processVpnInterfaceUp(dpnId, vpnIf,
+                                                                primaryRd, ifIndex, true, writeConfigTxn,
+                                                                writeOperTxn, writeInvTxn, update, vpnName, prefixes);
+                                                            mapOfRdAndPrefixesForRefreshFib.put(primaryRd, prefixes);
+                                                        }
                                                     }
-                                                }
-                                            } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
-                                                for (VpnInstanceNames vpnInterfaceVpnInstance :
+                                                } else if (state.equals(IntfTransitionState.STATE_DOWN)) {
+                                                    for (VpnInstanceNames vpnInterfaceVpnInstance :
                                                         requireNonNullElse(vpnIf.getVpnInstanceNames(),
-                                                            Collections.<VpnInstanceNames>emptyList())) {
-                                                    String vpnName = vpnInterfaceVpnInstance.getVpnName();
-                                                    LOG.info("VPN Interface update event - intfName {} onto vpnName {}"
-                                                        + " running oper-driven DOWN", vpnIf.getName(), vpnName);
-                                                    Optional<VpnInterfaceOpDataEntry> optVpnInterface =
-                                                        vpnUtil.getVpnInterfaceOpDataEntry(vpnIf.getName(), vpnName);
-                                                    if (optVpnInterface.isPresent()) {
-                                                        VpnInterfaceOpDataEntry vpnOpInterface = optVpnInterface.get();
-                                                        vpnInterfaceManager.processVpnInterfaceDown(dpnId,
-                                                            vpnIf.getName(),
-                                                            ifIndex, update.getPhysAddress().getValue(), vpnOpInterface,
-                                                            true, writeConfigTxn, writeOperTxn, writeInvTxn);
-                                                    } else {
-                                                        LOG.error(
-                                                            "InterfaceStateChangeListener Update DOWN - vpnInterface {}"
-                                                                + " not available, ignoring event", vpnIf.getName());
-                                                        continue;
+                                                        Collections.<VpnInstanceNames>emptyList())) {
+                                                        String vpnName = vpnInterfaceVpnInstance.getVpnName();
+                                                        LOG.info("VPN Interface update event - intfName {} "
+                                                            + " onto vpnName {} running oper-driven DOWN",
+                                                            vpnIf.getName(), vpnName);
+                                                        Optional<VpnInterfaceOpDataEntry> optVpnInterface = vpnUtil
+                                                            .getVpnInterfaceOpDataEntry(vpnIf.getName(), vpnName);
+                                                        if (optVpnInterface.isPresent()) {
+                                                            VpnInterfaceOpDataEntry vpnOpInterface =
+                                                                optVpnInterface.get();
+                                                            vpnInterfaceManager.processVpnInterfaceDown(dpnId,
+                                                                vpnIf.getName(), ifIndex, update.getPhysAddress()
+                                                                .getValue(), vpnOpInterface, true,
+                                                                writeConfigTxn, writeOperTxn, writeInvTxn);
+                                                        } else {
+                                                            LOG.error("InterfaceStateChangeListener Update DOWN - "
+                                                                + " vpnInterface {}not available, ignoring event",
+                                                                vpnIf.getName());
+                                                            continue;
+                                                        }
                                                     }
                                                 }
+                                            } else {
+                                                LOG.debug("Interface {} is not a vpninterface, ignoring.", ifName);
                                             }
-                                        } else {
-                                            LOG.debug("Interface {} is not a vpninterface, ignoring.", ifName);
-                                        }
-                                    }))))));
+                                        })));
+                        Futures.addCallback(configTxFuture,
+                            new VpnInterfaceCallBackHandler(mapOfRdAndPrefixesForRefreshFib),
+                            MoreExecutors.directExecutor());
+                        futures.add(configTxFuture);
+                    }));
                     return futures;
                 });
             }
@@ -381,4 +411,26 @@ public class InterfaceStateChangeListener
         }
         return transitionState;
     }
+
+    private class VpnInterfaceCallBackHandler implements FutureCallback<Void> {
+        private final Map<String, Set<String>> mapOfRdAndPrefixesForRefreshFib;
+
+        VpnInterfaceCallBackHandler(Map<String, Set<String>> mapOfRdAndPrefixesForRefreshFib) {
+            this.mapOfRdAndPrefixesForRefreshFib = mapOfRdAndPrefixesForRefreshFib;
+        }
+
+        @Override
+        public void onSuccess(Void voidObj) {
+            mapOfRdAndPrefixesForRefreshFib.forEach((primaryRd, prefixes) -> {
+                prefixes.forEach(prefix -> {
+                    fibManager.refreshVrfEntry(primaryRd, prefix);
+                });
+            });
+        }
+
+        @Override
+        public void onFailure(Throwable throwable) {
+            LOG.debug("write Tx config operation failed {}", throwable);
+        }
+    }
 }
index ddebe88c4c9fc1b68cb106dfcc7e17a08ceb417d..e751fd0b9138b7a2c90dbf644604ff1436a49225 100644 (file)
@@ -11,11 +11,17 @@ package org.opendaylight.netvirt.vpnmanager;
 import static org.opendaylight.genius.infra.Datastore.CONFIGURATION;
 import static org.opendaylight.genius.infra.Datastore.OPERATIONAL;
 
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import javax.annotation.PostConstruct;
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -25,6 +31,7 @@ import org.opendaylight.genius.datastoreutils.AsyncDataTreeChangeListenerBase;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunner;
 import org.opendaylight.genius.infra.ManagedNewTransactionRunnerImpl;
 import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
+import org.opendaylight.netvirt.fibmanager.api.IFibManager;
 import org.opendaylight.netvirt.vpnmanager.api.InterfaceUtils;
 import org.opendaylight.netvirt.vpnmanager.api.VpnHelper;
 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.instances.VpnInstance;
@@ -46,16 +53,18 @@ public class TunnelEndPointChangeListener
     private final VpnInterfaceManager vpnInterfaceManager;
     private final JobCoordinator jobCoordinator;
     private final VpnUtil vpnUtil;
+    private final IFibManager fibManager;
 
     @Inject
     public TunnelEndPointChangeListener(final DataBroker broker, final VpnInterfaceManager vpnInterfaceManager,
-            final JobCoordinator jobCoordinator, VpnUtil vpnUtil) {
+            final JobCoordinator jobCoordinator, VpnUtil vpnUtil, final IFibManager fibManager) {
         super(TunnelEndPoints.class, TunnelEndPointChangeListener.class);
         this.broker = broker;
         this.txRunner = new ManagedNewTransactionRunnerImpl(broker);
         this.vpnInterfaceManager = vpnInterfaceManager;
         this.jobCoordinator = jobCoordinator;
         this.vpnUtil = vpnUtil;
+        this.fibManager = fibManager;
     }
 
     @PostConstruct
@@ -116,15 +125,33 @@ public class TunnelEndPointChangeListener
                                 }
                                 final int lPortTag = interfaceState.getIfIndex();
                                 List<ListenableFuture<Void>> futures = new ArrayList<>();
-                                futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
-                                    writeConfigTxn -> futures.add(
-                                        txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
-                                            writeOperTxn -> futures.add(
-                                                txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
-                                                    writeInvTxn -> vpnInterfaceManager.processVpnInterfaceAdjacencies(
-                                                        dpnId, lPortTag, vpnName, primaryRd, vpnInterfaceName, vpnId,
-                                                        writeConfigTxn, writeOperTxn, writeInvTxn,
-                                                        interfaceState)))))));
+                                Set<String> prefixesForRefreshFib = new HashSet<>();
+                                ListenableFuture<Void> writeConfigFuture =
+                                    txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
+                                        writeConfigTxn -> futures.add(
+                                            txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
+                                                writeOperTxn -> futures.add(
+                                                    txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
+                                                        writeInvTxn ->
+                                                            vpnInterfaceManager.processVpnInterfaceAdjacencies(dpnId,
+                                                                lPortTag, vpnName, primaryRd, vpnInterfaceName, vpnId,
+                                                                writeConfigTxn, writeOperTxn, writeInvTxn,
+                                                                interfaceState, prefixesForRefreshFib)
+                                                    )))));
+                                Futures.addCallback(writeConfigFuture, new FutureCallback<Void>() {
+                                    @Override
+                                    public void onSuccess(Void voidObj) {
+                                        prefixesForRefreshFib.forEach(prefix -> {
+                                            fibManager.refreshVrfEntry(primaryRd, prefix);
+                                        });
+                                    }
+
+                                    @Override
+                                    public void onFailure(Throwable throwable) {
+                                        LOG.debug("addVpnInterface: write Tx config execution failed {}", throwable);
+                                    }
+                                }, MoreExecutors.directExecutor());
+                                futures.add(writeConfigFuture);
                                 LOG.trace("add: Handled TEP {} add for VPN instance {} VPN interface {}",
                                         tep.getInterfaceName(), vpnName, vpnInterfaceName);
                                 return futures;
index cebe58d3f6475eb86272c5daee5c04226444d589..b332d6963201ead4bbd691e8a0a43eacf48e0c3f 100755 (executable)
@@ -24,9 +24,11 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.ExecutionException;
@@ -283,6 +285,10 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                         // TODO Deal with sequencing â€” the config tx must only submitted if the oper tx goes in
                         // (the inventory tx goes in last)
                         List<ListenableFuture<Void>> futures = new ArrayList<>();
+                        //set of prefix used, as entry in prefix-to-interface datastore
+                        // is prerequisite for refresh Fib to avoid race condition leading to
+                        // missing remote next hop in bucket actions on bgp-vpn delete
+                        Set<String> prefixListForRefreshFib = new HashSet<>();
                         ListenableFuture<Void> confFuture =
                             txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
                                 confTx -> futures.add(txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL,
@@ -293,7 +299,8 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                                                     + " on dpn {}",
                                                 vpnInterface.getName(), vpnName, vpnInterface.getDpnId());
                                             processVpnInterfaceUp(dpnId, vpnInterface, primaryRd, ifIndex, false,
-                                                confTx, operTx, invTx, interfaceState, vpnName);
+                                                confTx, operTx, invTx, interfaceState, vpnName,
+                                                prefixListForRefreshFib);
                                             if (oldAdjs != null && !oldAdjs.equals(newAdjs)) {
                                                 LOG.info("addVpnInterface: Adjacency changed upon VPNInterface {}"
                                                     + " Update for swapping VPN {} case.", interfaceName, vpnName);
@@ -305,7 +312,8 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                                                             if (!isBgpVpnInternetVpn
                                                                 || vpnUtil.isAdjacencyEligibleToVpnInternet(adj)) {
                                                                 addNewAdjToVpnInterface(vpnInterfaceOpIdentifier,
-                                                                    primaryRd, adj, dpnId, operTx, confTx, invTx);
+                                                                    primaryRd, adj, dpnId, operTx, confTx, invTx,
+                                                                    prefixListForRefreshFib);
                                                             }
                                                         }
                                                     }
@@ -319,6 +327,9 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                                                 }
                                             }
                                         })))));
+                        Futures.addCallback(confFuture,
+                            new VpnInterfaceCallBackHandler(primaryRd, prefixListForRefreshFib),
+                            MoreExecutors.directExecutor());
                         futures.add(confFuture);
                         Futures.addCallback(confFuture, new PostVpnInterfaceWorker(interfaceName, true, "Config"),
                             MoreExecutors.directExecutor());
@@ -365,8 +376,8 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
             TypedWriteTransaction<Configuration> writeConfigTxn,
             TypedWriteTransaction<Operational> writeOperTxn,
             TypedReadWriteTransaction<Configuration> writeInvTxn,
-            Interface interfaceState,
-            final String vpnName) throws ExecutionException, InterruptedException {
+            Interface interfaceState, final String vpnName,
+            Set<String> prefixListForRefreshFib) throws ExecutionException, InterruptedException {
         final String interfaceName = vpnInterface.getName();
         Optional<VpnInterfaceOpDataEntry> optOpVpnInterface = vpnUtil.getVpnInterfaceOpDataEntry(interfaceName,
                 vpnName);
@@ -430,7 +441,7 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                         null/*ipAddressSourceValuePair*/,
                         true /* add */);
                 processVpnInterfaceAdjacencies(dpId, lportTag, vpnName, primaryRd, interfaceName,
-                        vpnId, writeConfigTxn, writeOperTxn, writeInvTxn, interfaceState);
+                        vpnId, writeConfigTxn, writeOperTxn, writeInvTxn, interfaceState, prefixListForRefreshFib);
                 if (!isBgpVpnInternetVpn) {
                     vpnUtil.bindService(vpnName, interfaceName, false /*isTunnelInterface*/);
                 }
@@ -479,7 +490,7 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                     null/*ipAddressSourceValuePair*/,
                     true /* add */);
             processVpnInterfaceAdjacencies(dpId, lportTag, vpnName, primaryRd, interfaceName,
-                    vpnId, writeConfigTxn, writeOperTxn, writeInvTxn, interfaceState);
+                    vpnId, writeConfigTxn, writeOperTxn, writeInvTxn, interfaceState, prefixListForRefreshFib);
             if (!isBgpVpnInternetVpn) {
                 vpnUtil.bindService(vpnName, interfaceName, false/*isTunnelInterface*/);
             }
@@ -516,7 +527,7 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                         continue;
                     }
                     addNewAdjToVpnInterface(vpnInterfaceOpIdentifier, primaryRd, adjacency,
-                            dpId, writeOperTxn, writeConfigTxn, writeInvTxn);
+                            dpId, writeOperTxn, writeConfigTxn, writeInvTxn, prefixListForRefreshFib);
                 }
             } catch (ReadFailedException e) {
                 LOG.error("processVpnInterfaceUp: Failed to read data store for interface {} vpn {} rd {} dpn {}",
@@ -720,7 +731,7 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                                                   TypedWriteTransaction<Configuration> writeConfigTxn,
                                                   TypedWriteTransaction<Operational> writeOperTxn,
                                                   TypedReadWriteTransaction<Configuration> writeInvTxn,
-                                                  Interface interfaceState)
+                                                  Interface interfaceState, Set<String> prefixListForRefreshFib)
             throws ExecutionException, InterruptedException {
         InstanceIdentifier<VpnInterface> identifier = VpnUtil.getVpnInterfaceIdentifier(interfaceName);
         // Read NextHops
@@ -879,8 +890,8 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
             }
             if (nextHop.getAdjacencyType() != AdjacencyType.PrimaryAdjacency) {
                 vpnManager.addExtraRoute(vpnName, nextHop.getIpAddress(), nextHop.getNextHopIpList().get(0), rd,
-                        vpnName, l3vni, origin,
-                        interfaceName, operationalAdjacency, encapType, writeConfigTxn);
+                    vpnName, l3vni, origin, interfaceName, operationalAdjacency, encapType, prefixListForRefreshFib,
+                    writeConfigTxn);
             }
             value.add(operationalAdjacency);
         }
@@ -1749,60 +1760,65 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
             String primaryRd = vpnUtil.getPrimaryRd(newVpnName);
             if (!vpnUtil.isVpnPendingDelete(primaryRd)) {
                 // TODO Deal with sequencing â€” the config tx must only submitted if the oper tx goes in
-                futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION, confTx -> {
-                    futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL, operTx -> {
-                        InstanceIdentifier<VpnInterfaceOpDataEntry> vpnInterfaceOpIdentifier =
+                //set of prefix used as entry in prefix-to-interface datastore
+                // is prerequisite for refresh Fib to avoid race condition leading to missing remote next hop
+                // in bucket actions on bgp-vpn delete
+                Set<String> prefixListForRefreshFib = new HashSet<>();
+                ListenableFuture<Void> configTxFuture = txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
+                    confTx -> futures.add(txRunner.callWithNewReadWriteTransactionAndSubmit(OPERATIONAL,
+                        operTx -> {
+                            InstanceIdentifier<VpnInterfaceOpDataEntry> vpnInterfaceOpIdentifier =
                                 VpnUtil.getVpnInterfaceOpDataEntryIdentifier(vpnInterfaceName, newVpnName);
-                        LOG.info("VPN Interface update event - intfName {} onto vpnName {} running config-driven",
-                                update.getName(), newVpnName);
-                        //handle both addition and removal of adjacencies
-                        //currently, new adjacency may be an extra route
-                        boolean isBgpVpnInternetVpn = vpnUtil.isBgpVpnInternet(newVpnName);
-                        if (!oldAdjs.equals(newAdjs)) {
-                            for (Adjacency adj : copyNewAdjs) {
-                                if (copyOldAdjs.contains(adj)) {
-                                    copyOldAdjs.remove(adj);
-                                } else {
-                                    // add new adjacency
-                                    if (!isBgpVpnInternetVpn || vpnUtil.isAdjacencyEligibleToVpnInternet(adj)) {
-                                        addNewAdjToVpnInterface(vpnInterfaceOpIdentifier, primaryRd, adj,
-                                                dpnId, operTx, confTx, confTx);
+                            LOG.info("VPN Interface update event-intfName {} onto vpnName {} running config-driven",
+                                    update.getName(), newVpnName);
+                            //handle both addition and removal of adjacencies
+                            // currently, new adjacency may be an extra route
+                            boolean isBgpVpnInternetVpn = vpnUtil.isBgpVpnInternet(newVpnName);
+                            if (!oldAdjs.equals(newAdjs)) {
+                                for (Adjacency adj : copyNewAdjs) {
+                                    if (copyOldAdjs.contains(adj)) {
+                                        copyOldAdjs.remove(adj);
+                                    } else {
+                                        // add new adjacency
+                                        if (!isBgpVpnInternetVpn || vpnUtil.isAdjacencyEligibleToVpnInternet(adj)) {
+                                            addNewAdjToVpnInterface(vpnInterfaceOpIdentifier, primaryRd, adj,
+                                                    dpnId, operTx, confTx, confTx, prefixListForRefreshFib);
+                                        }
+                                        LOG.info("update: new Adjacency {} with nextHop {} label {} subnet {} "
+                                            + " added to vpn interface {} on vpn {} dpnId {}",
+                                            adj.getIpAddress(), adj.getNextHopIpList(), adj.getLabel(),
+                                            adj.getSubnetId(), update.getName(), newVpnName, dpnId);
                                     }
-                                    LOG.info("update: new Adjacency {} with nextHop {} label {} subnet {} added to"
-                                                    + " vpn interface {} on vpn {} dpnId {}",
-                                            adj.getIpAddress(), adj.getNextHopIpList(),
-                                            adj.getLabel(), adj.getSubnetId(), update.getName(),
-                                            newVpnName, dpnId);
                                 }
-                            }
-                            for (Adjacency adj : copyOldAdjs) {
-                                if (!isBgpVpnInternetVpn || vpnUtil.isAdjacencyEligibleToVpnInternet(adj)) {
-                                    if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency
+                                for (Adjacency adj : copyOldAdjs) {
+                                    if (!isBgpVpnInternetVpn || vpnUtil.isAdjacencyEligibleToVpnInternet(adj)) {
+                                        if (adj.getAdjacencyType() == AdjacencyType.PrimaryAdjacency
                                             && !adj.isPhysNetworkFunc()) {
-                                        delAdjFromVpnInterface(vpnInterfaceOpIdentifier, adj, dpnId,
-                                                operTx, confTx);
-                                        //remove FIB entry
-                                        String vpnRd = vpnUtil.getVpnRd(newVpnName);
-                                        LOG.debug("update: remove prefix {} from the FIB and BGP entry "
+                                            delAdjFromVpnInterface(vpnInterfaceOpIdentifier, adj, dpnId, operTx,
+                                                confTx);
+                                            //remove FIB entry
+                                            String vpnRd = vpnUtil.getVpnRd(newVpnName);
+                                            LOG.debug("update: remove prefix {} from the FIB and BGP entry "
                                                 + "for the Vpn-Rd {} ", adj.getIpAddress(), vpnRd);
-                                        //remove BGP entry
-                                        fibManager.removeFibEntry(vpnRd, adj.getIpAddress(), confTx);
-                                        if (vpnRd != null && !vpnRd.equalsIgnoreCase(newVpnName)) {
-                                            bgpManager.withdrawPrefix(vpnRd, adj.getIpAddress());
-                                        }
-                                    } else {
-                                        delAdjFromVpnInterface(vpnInterfaceOpIdentifier, adj, dpnId,
+                                            //remove BGP entry
+                                            fibManager.removeFibEntry(vpnRd, adj.getIpAddress(), confTx);
+                                            if (vpnRd != null && !vpnRd.equalsIgnoreCase(newVpnName)) {
+                                                bgpManager.withdrawPrefix(vpnRd, adj.getIpAddress());
+                                            }
+                                        } else {
+                                            delAdjFromVpnInterface(vpnInterfaceOpIdentifier, adj, dpnId,
                                                 operTx, confTx);
+                                        }
                                     }
-                                }
-                                LOG.info("update: Adjacency {} with nextHop {} label {} subnet {} removed from"
-                                                + " vpn interface {} on vpn {}", adj.getIpAddress(), adj
-                                                .getNextHopIpList(),
+                                    LOG.info("update: Adjacency {} with nextHop {} label {} subnet {} removed from"
+                                        + " vpn interface {} on vpn {}", adj.getIpAddress(), adj.getNextHopIpList(),
                                         adj.getLabel(), adj.getSubnetId(), update.getName(), newVpnName);
+                                }
                             }
-                        }
-                    }));
-                }));
+                        })));
+                Futures.addCallback(configTxFuture, new VpnInterfaceCallBackHandler(primaryRd, prefixListForRefreshFib),
+                    MoreExecutors.directExecutor());
+                futures.add(configTxFuture);
                 for (ListenableFuture<Void> future : futures) {
                     ListenableFutures.addErrorLogging(future, LOG, "update: failed for interface {} on vpn {}",
                             update.getName(), update.getVpnInstanceNames());
@@ -1865,7 +1881,8 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                                            Adjacency adj, BigInteger dpnId,
                                            TypedWriteTransaction<Operational> writeOperTxn,
                                            TypedWriteTransaction<Configuration> writeConfigTxn,
-                                           TypedReadWriteTransaction<Configuration> writeInvTxn)
+                                           TypedReadWriteTransaction<Configuration> writeInvTxn,
+                                           Set<String> prefixListForRefreshFib)
             throws ExecutionException, InterruptedException {
         String interfaceName = identifier.firstKeyOf(VpnInterfaceOpDataEntry.class).getName();
         String configVpnName = identifier.firstKeyOf(VpnInterfaceOpDataEntry.class).getVpnInstanceName();
@@ -1900,8 +1917,8 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                             currVpnIntf.getName());
                     if (interfaceState != null) {
                         processVpnInterfaceAdjacencies(dpnId, currVpnIntf.getLportTag().intValue(), vpnName, primaryRd,
-                                currVpnIntf.getName(),
-                                vpnId, writeConfigTxn, writeOperTxn, writeInvTxn, interfaceState);
+                            currVpnIntf.getName(), vpnId, writeConfigTxn, writeOperTxn, writeInvTxn, interfaceState,
+                            prefixListForRefreshFib);
                     }
                 }
                 if (adj.getNextHopIpList() != null && !adj.getNextHopIpList().isEmpty()
@@ -1919,7 +1936,8 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                             int label = operationalAdjacency.getLabel().intValue();
                             vpnManager.addExtraRoute(vpnName, adj.getIpAddress(), nh, rdToAllocate.get(),
                                     currVpnIntf.getVpnInstanceName(), l3vni, origin,
-                                    currVpnIntf.getName(), operationalAdjacency, encapType, writeConfigTxn);
+                                    currVpnIntf.getName(), operationalAdjacency, encapType,
+                                    prefixListForRefreshFib, writeConfigTxn);
                             LOG.info("addNewAdjToVpnInterface: Added extra route ip {} nh {} rd {} vpnname {} label {}"
                                             + " Interface {} on dpn {}", adj.getIpAddress(), nh, rdToAllocate.get(),
                                     vpnName, label, currVpnIntf.getName(), dpnId);
@@ -1940,11 +1958,10 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                                             vpnUtil.getVpnName(vpn.getVpnId()), nh, dpnId)
                                             .ifPresent(
                                                 rds -> vpnManager.addExtraRoute(
-                                                        vpnUtil.getVpnName(vpn.getVpnId()),
-                                                        adj.getIpAddress(), nh, rds,
-                                                        currVpnIntf.getVpnInstanceName(), l3vni,
-                                                        RouteOrigin.SELF_IMPORTED, currVpnIntf.getName(),
-                                                        opAdjacency, encapType, writeConfigTxn));
+                                                        vpnUtil.getVpnName(vpn.getVpnId()), adj.getIpAddress(),
+                                                        nh, rds, currVpnIntf.getVpnInstanceName(), l3vni,
+                                                        RouteOrigin.SELF_IMPORTED, currVpnIntf.getName(), opAdjacency,
+                                                        encapType, prefixListForRefreshFib, writeConfigTxn));
                                 }
                             });
                         }
@@ -2312,12 +2329,21 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
                                     if (vpnUtil.isAdjacencyEligibleToVpn(adjacency, vpnName)) {
                                         List<ListenableFuture<Void>> futures = new ArrayList<>();
                                         futures.add(
-                                            txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx ->
-                                                futures.add(
+                                            txRunner.callWithNewWriteOnlyTransactionAndSubmit(OPERATIONAL, operTx -> {
+                                                //set of prefix used, as entry in prefix-to-interface datastore
+                                                // is prerequisite for refresh Fib to avoid race condition leading
+                                                // to missing remote next hop in bucket actions on bgp-vpn delete
+                                                Set<String> prefixListForRefreshFib = new HashSet<>();
+                                                ListenableFuture<Void> configTxFuture =
                                                     txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
                                                         confTx -> addNewAdjToVpnInterface(existingVpnInterfaceId,
-                                                            primaryRd, adjacency, vpnInterfaceOptional.get()
-                                                                .getDpnId(), operTx, confTx, confTx)))));
+                                                            primaryRd, adjacency, vpnInterfaceOptional.get().getDpnId(),
+                                                                operTx, confTx, confTx, prefixListForRefreshFib));
+                                                Futures.addCallback(configTxFuture,
+                                                    new VpnInterfaceCallBackHandler(primaryRd, prefixListForRefreshFib),
+                                                    MoreExecutors.directExecutor());
+                                                futures.add(configTxFuture);
+                                            }));
                                         return futures;
                                     } else {
                                         return emptyList();
@@ -2364,4 +2390,26 @@ public class VpnInterfaceManager extends AsyncDataTreeChangeListenerBase<VpnInte
             }
         }
     }
+
+    private class VpnInterfaceCallBackHandler implements FutureCallback<Void> {
+        private final String primaryRd;
+        private final Set<String> prefixListForRefreshFib;
+
+        VpnInterfaceCallBackHandler(String primaryRd, Set<String> prefixListForRefreshFib) {
+            this.primaryRd = primaryRd;
+            this.prefixListForRefreshFib = prefixListForRefreshFib;
+        }
+
+        @Override
+        public void onSuccess(Void voidObj) {
+            prefixListForRefreshFib.forEach(prefix -> {
+                fibManager.refreshVrfEntry(primaryRd, prefix);
+            });
+        }
+
+        @Override
+        public void onFailure(Throwable throwable) {
+            LOG.debug("write Tx config operation failed {}", throwable);
+        }
+    }
 }
index 5f0f01393d5e3b45e5291ee29a29771df9da8776..3a8192124f5674d38cbf785c196c8344c88c6257 100644 (file)
@@ -196,7 +196,8 @@ public class VpnManagerImpl implements IVpnManager {
     @Override
     public void addExtraRoute(String vpnName, String destination, String nextHop, String rd, @Nullable String routerID,
         Long l3vni, RouteOrigin origin, @Nullable String intfName, @Nullable Adjacency operationalAdj,
-        VrfEntry.EncapType encapType, @Nonnull TypedWriteTransaction<Configuration> confTx) {
+        VrfEntry.EncapType encapType, Set<String> prefixListForRefreshFib,
+        @Nonnull TypedWriteTransaction<Configuration> confTx) {
         //add extra route to vpn mapping; advertise with nexthop as tunnel ip
         vpnUtil.syncUpdate(LogicalDatastoreType.OPERATIONAL,
                 VpnExtraRouteHelper.getVpnToExtrarouteVrfIdIdentifier(vpnName, rd != null ? rd : routerID,
@@ -246,7 +247,7 @@ public class VpnManagerImpl implements IVpnManager {
                 List<String> nhList = optVpnExtraRoutes.get().getNexthopIpList();
                 if (nhList != null && nhList.size() > 1) {
                     // If nhList is greater than one for vpnextraroute, a call to populatefib doesn't update vrfentry.
-                    fibManager.refreshVrfEntry(primaryRd, destination);
+                    prefixListForRefreshFib.add(destination);
                 } else {
                     L3vpnInput input = new L3vpnInput().setNextHop(operationalAdj).setNextHopIp(nextHop).setL3vni(l3vni)
                             .setPrimaryRd(primaryRd).setVpnName(vpnName).setDpnId(dpnId)
index 47c8f1d1d702ffc27f6b382bc770dc9defcf32ba..941a0223adf1c23de53de7a2a54f7db32289bd02 100644 (file)
@@ -14,6 +14,7 @@ import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.SettableFuture;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.concurrent.ExecutionException;
 import java.util.function.Consumer;
 import javax.inject.Inject;
@@ -189,7 +190,7 @@ public class VpnRpcServiceImpl implements VpnRpcService {
                 txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION,
                     confTx -> vpnManager.addExtraRoute(vpnInstanceName, destination, nexthop, vpnRd,
                             null /* routerId */, vpnOpEntry.getL3vni(), RouteOrigin.STATIC, null /* intfName */,
-                        null /*Adjacency*/, encapType, confTx)).get();
+                        null /*Adjacency*/, encapType, new HashSet<>() /*prefixListForRefreshFib*/,confTx)).get();
             } catch (InterruptedException | ExecutionException e) {
                 LOG.error("Error adding static route {}", input, e);
                 result.set(RpcResultBuilder.<AddStaticRouteOutput>failed().withError(ErrorType.APPLICATION,