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,
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;
private final VpnInterfaceManager vpnInterfaceManager;
private final VpnUtil vpnUtil;
private final JobCoordinator jobCoordinator;
+ private final IFibManager fibManager;
Table<OperStatus, OperStatus, IntfTransitionState> stateTable = HashBasedTable.create();
@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();
}
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
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);
}
}
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,
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;
});
}
}
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);
+ }
+ }
}
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;
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;
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
}
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;
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;
// 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,
+ " 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);
if (!isBgpVpnInternetVpn
|| vpnUtil.isAdjacencyEligibleToVpnInternet(adj)) {
addNewAdjToVpnInterface(vpnInterfaceOpIdentifier,
- primaryRd, adj, dpnId, operTx, confTx, invTx);
+ primaryRd, adj, dpnId, operTx, confTx, invTx,
+ prefixListForRefreshFib);
}
}
}
}
}
})))));
+ Futures.addCallback(confFuture,
+ new VpnInterfaceCallBackHandler(primaryRd, prefixListForRefreshFib),
+ MoreExecutors.directExecutor());
futures.add(confFuture);
Futures.addCallback(confFuture, new PostVpnInterfaceWorker(interfaceName, true, "Config"),
MoreExecutors.directExecutor());
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);
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*/);
}
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*/);
}
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 {}",
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
}
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);
}
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());
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();
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()
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);
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));
}
});
}
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();
}
}
}
+
+ 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);
+ }
+ }
}
@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,
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)
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;
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,