uses peer-reference;
}
}
+
+ rpc restart-gracefully {
+ description "Perform graceful restart with peer";
+ input {
+ uses peer-reference;
+ leaf selection-deferral-time {
+ description
+ "Time to wait with route selection in seconds. Route selection is postponed until
+ End of Rib marker is received or this timer expires.";
+ type uint32;
+ units seconds;
+ mandatory true;
+ }
+ }
+ }
}
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
+import com.google.common.base.Stopwatch;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Sets;
import com.google.common.net.InetAddresses;
import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.ArrayList;
import java.util.Collections;
import org.opendaylight.protocol.bgp.parser.BGPError;
import org.opendaylight.protocol.bgp.parser.impl.message.update.LocalPreferenceAttributeParser;
import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
+import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
+import org.opendaylight.protocol.bgp.rib.impl.config.GracefulRestartUtil;
+import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
import org.opendaylight.protocol.bgp.rib.impl.spi.RIB;
import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateProvider;
import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationIpv4CaseBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.UpdateMessage;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.Nlri;
private final List<RouteTarget> rtMemberships = new ArrayList<>();
private final RpcProviderRegistry rpcRegistry;
private final BGPTableTypeRegistryConsumer tableTypeRegistry;
+ private final BgpPeer bgpPeer;
private InstanceIdentifier<AdjRibOut> peerRibOutIId;
+ private KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
+ .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId;
@GuardedBy("this")
private AbstractRegistration trackerRegistration;
private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
private YangInstanceIdentifier peerPath;
private boolean sessionUp;
- private long peerRestartStart;
+ private Stopwatch peerRestartStopwatch;
+ private long selectionDeferralTimerSeconds;
+ private final List<TablesKey> missingEOT = new ArrayList<>();
public BGPPeer(
final BGPTableTypeRegistryConsumer tableTypeRegistry,
final AsNumber localAs,
final RpcProviderRegistry rpcRegistry,
final Set<TablesKey> afiSafisAdvertized,
- final Set<TablesKey> afiSafisGracefulAdvertized) {
+ final Set<TablesKey> afiSafisGracefulAdvertized,
+ final BgpPeer bgpPeer) {
super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized);
this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
this.rib = requireNonNull(rib);
this.rpcRegistry = rpcRegistry;
+ this.bgpPeer = bgpPeer;
}
BGPPeer(
final Set<TablesKey> afiSafisAdvertized,
final Set<TablesKey> afiSafisGracefulAdvertized) {
this(tableTypeRegistry, neighborAddress, null, rib, role, null, null, rpcRegistry,
- afiSafisAdvertized, afiSafisGracefulAdvertized);
+ afiSafisAdvertized, afiSafisGracefulAdvertized, null);
}
if (endOfRib) {
final TablesKey tablesKey = new TablesKey(mpUnreach.getAfi(), mpUnreach.getSafi());
this.ribWriter.removeStaleRoutes(tablesKey);
+ this.missingEOT.remove(tablesKey);
+ handleGracefulEndOfRib();
} else {
this.ribWriter.removeRoutes(mpUnreach);
}
} else if (endOfRib) {
-
this.ribWriter.removeStaleRoutes(IPV4_UCAST_TABLE_KEY);
+ this.missingEOT.remove(IPV4_UCAST_TABLE_KEY);
+ handleGracefulEndOfRib();
+ }
+ }
+
+ private synchronized void handleGracefulEndOfRib() {
+ if (isLocalRestarting()) {
+ if (this.missingEOT.isEmpty()) {
+ createEffRibInWriter();
+ this.effRibInWriter.init();
+ registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
+ for (final TablesKey key : this.tables) {
+ createAdjRibOutListener(key, true);
+ }
+ setLocalRestartingState(false);
+ setGracefulPreferences(false, Collections.emptySet());
+ }
}
}
.collect(Collectors.toSet());
this.tables = ImmutableSet.copyOf(setTables);
this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
+ final boolean restartingLocally = isLocalRestarting();
- if (!isPeerRestarting()) {
+ if (!isRestartingGracefully()) {
this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
this.peerId = RouterIds.createPeerId(session.getBgpId());
- final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
- .rev180329.bgp.rib.rib.Peer, PeerKey> peerIId =
- getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
+ this.peerIId = getInstanceIdentifier().child(org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns
.yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
this.peerPath = createPeerPath();
this.peerRibOutIId = peerIId.child(AdjRibOut.class);
this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
- this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
- this.rib.createPeerChain(this),
- peerIId, this.tables, this.tableTypeRegistry,
- this.rtMemberships,
- this.rtCache);
+ createEffRibInWriter();
registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
- this.effRibInWriter.init();
+ this.effRibInWriter.init();
this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
this.tables, this.addPathTableMaps);
.collect(Collectors.toSet());
}
this.ribWriter.clearTables(Sets.difference(this.tables, forwardingTables));
+ if (restartingLocally) {
+ this.peerRestartStopwatch = Stopwatch.createStarted();
+ handleSelectionReferralTimer();
+ this.missingEOT.addAll(this.tables);
+ }
}
if (advertisedTables == null ||
advertisedTables.isEmpty()) {
.map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
}
final int restartTime = advertisedGracefulRestartCapability.getRestartTime();
- setAfiSafiGracefulRestartState(restartTime, false,false);
- addBgp4Support();
- for (final TablesKey key : this.tables) {
- createAdjRibOutListener(key, true);
+ setAfiSafiGracefulRestartState(restartTime, false, restartingLocally);
+ if (!restartingLocally) {
+ addBgp4Support();
+ for (final TablesKey key : this.tables) {
+ createAdjRibOutListener(key, true);
+ }
}
}
+ private boolean isRestartingGracefully() {
+ return isLocalRestarting() || isPeerRestarting();
+ }
+
+ private synchronized void createEffRibInWriter() {
+ this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
+ this.rib.createPeerChain(this),
+ this.peerIId, this.tables, this.tableTypeRegistry,
+ this.rtMemberships,
+ this.rtCache);
+ }
+
//try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
private synchronized void addBgp4Support() {
if (!this.tables.contains(IPV4_UCAST_TABLE_KEY)) {
this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
this.adjRibOutListenerSet.clear();
final FluentFuture<? extends CommitInfo> future;
- if (!isPeerRestarting()) {
+ if (!isRestartingGracefully()) {
future = terminateConnection();
} else {
+ if (isLocalRestarting()){
+ this.effRibInWriter.close();
+ }
final Set<TablesKey> gracefulTables = getGracefulTables();
this.ribWriter.storeStaleRoutes(gracefulTables);
future = this.ribWriter.clearTables(Sets.difference(this.tables, gracefulTables));
- this.peerRestartStart = System.nanoTime();
- handleRestartTimer();
+ if (isPeerRestarting()) {
+ this.peerRestartStopwatch = Stopwatch.createStarted();
+ handleRestartTimer();
+ }
}
releaseBindingChain();
if (this.session != null) {
try {
- if (isPeerRestarting()) {
+ if (isRestartingGracefully()) {
this.session.closeWithoutMessage();
} else {
this.session.close();
return;
}
- final long ct = System.nanoTime();
- final long restartExpire = this.peerRestartStart + TimeUnit.SECONDS.toNanos(getPeerRestartTime());
-
- if (ct >= restartExpire) {
+ final long peerRestartTimeNanos = TimeUnit.SECONDS.toNanos(getPeerRestartTime());
+ final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
+ if (elapsedNanos >= peerRestartTimeNanos) {
setAfiSafiGracefulRestartState(0, false, false);
onSessionTerminated(this.session, new BGPTerminationReason(BGPError.HOLD_TIMER_EXPIRED));
}
new ScheduledThreadPoolExecutor(1)
- .schedule(this::handleRestartTimer, restartExpire - ct, TimeUnit.NANOSECONDS);
+ .schedule(this::handleRestartTimer, peerRestartTimeNanos - elapsedNanos, TimeUnit.NANOSECONDS);
+ }
+
+ private synchronized void handleSelectionReferralTimer() {
+ if (!isLocalRestarting()) {
+ return;
+ }
+
+ final long referalTimerNanos = TimeUnit.SECONDS.toNanos(this.selectionDeferralTimerSeconds);
+ final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
+ if (elapsedNanos >= referalTimerNanos) {
+ this.missingEOT.clear();
+ handleGracefulEndOfRib();
+ }
+ new ScheduledThreadPoolExecutor(1)
+ .schedule(this::handleSelectionReferralTimer, referalTimerNanos - elapsedNanos, TimeUnit.NANOSECONDS);
}
private boolean isEndOfRib(final UpdateMessage msg) {
public List<RouteTarget> getMemberships() {
return this.rtMemberships;
}
+
+ @Override
+ public synchronized ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
+ final Set<TablesKey> tablesToPreserve = getGracefulTables();
+ if (tablesToPreserve == null || tablesToPreserve.isEmpty()) {
+ LOG.info("Peer {} is not capable of graceful restart or have no matching graceful tables.", this.peerId);
+ return Futures.immediateFailedFuture(new UnsupportedOperationException(
+ "Peer is not capable of graceful restart"));
+ }
+ setGracefulPreferences(true, tablesToPreserve);
+ this.selectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
+ setLocalRestartingState(true);
+ return releaseConnection();
+ }
+
+ private synchronized void setGracefulPreferences(final boolean localRestarting,
+ final Set<TablesKey> preservedTables) {
+ final Set<TablesKey> gracefulTables = this.tables.stream()
+ .filter(this::isGracefulRestartAdvertized)
+ .collect(Collectors.toSet());
+ final BgpParameters bgpParameters = GracefulRestartUtil.getGracefulBgpParameters(
+ this.bgpPeer.getBgpFixedCapabilities(), gracefulTables, preservedTables,
+ this.bgpPeer.getGracefulRestartTimer(), localRestarting);
+ final BGPSessionPreferences oldPrefs = this.rib.getDispatcher().getBGPPeerRegistry()
+ .getPeerPreferences(getNeighborAddress());
+ final BGPSessionPreferences newPrefs = new BGPSessionPreferences(
+ oldPrefs.getMyAs(),
+ oldPrefs.getHoldTime(),
+ oldPrefs.getBgpId(),
+ oldPrefs.getExpectedRemoteAs(),
+ Collections.singletonList(bgpParameters),
+ oldPrefs.getMd5Password());
+ this.rib.getDispatcher().getBGPPeerRegistry()
+ .updatePeerPreferences(getNeighborAddress(), newPrefs);
+ }
}
import static java.util.Objects.requireNonNull;
+import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.JdkFutureAdapters;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import io.netty.channel.ChannelFuture;
import java.util.Set;
+import java.util.concurrent.ExecutionException;
import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
import org.opendaylight.protocol.bgp.rib.spi.PeerRPCs;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.RouteRefresh;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.ResetSessionInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.ResetSessionOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.ResetSessionOutputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RestartGracefullyInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RestartGracefullyOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RestartGracefullyOutputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RouteRefreshRequestInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RouteRefreshRequestOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RouteRefreshRequestOutputBuilder;
private static final Logger LOG = LoggerFactory.getLogger(BgpPeerRpc.class);
private static final String FAILURE_MSG = "Failed to send Route Refresh message";
private static final String FAILURE_RESET_SESSION_MSG = "Failed to reset session";
+ private static final String FAILURE_GRACEFUL_RESTART_MSG = "Failed to perform graceful restart";
private final BGPSession session;
private final Set<TablesKey> supportedFamilies;
}, MoreExecutors.directExecutor());
}
+ @Override
+ public ListenableFuture<RpcResult<RestartGracefullyOutput>> restartGracefully(RestartGracefullyInput input) {
+ final ListenableFuture<?> f = this.peerRPCs.restartGracefully(input.getSelectionDeferralTime());
+ final RpcResultBuilder<RestartGracefullyOutput> rpcResult = RpcResultBuilder.failed();
+ Futures.addCallback(f, new FutureCallback<Object>() {
+ @Override
+ public void onSuccess(Object result) {
+ rpcResult.success(new RestartGracefullyOutputBuilder().build()).build();
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ rpcResult.withError(ErrorType.RPC, String.format("%s: %s", FAILURE_GRACEFUL_RESTART_MSG,
+ throwable.getMessage()));
+ }
+ },
+ MoreExecutors.directExecutor());
+ try {
+ f.get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.error(FAILURE_GRACEFUL_RESTART_MSG, e);
+ }
+ return Futures.immediateFuture(rpcResult.build());
+ }
+
@Override
public ListenableFuture<RpcResult<RouteRefreshRequestOutput>> routeRefreshRequest(
final RouteRefreshRequestInput input) {
}
};
}
+
+ @Override
+ public void updatePeerPreferences(final IpAddress address, final BGPSessionPreferences preferences) {
+ if (this.peerPreferences.containsKey(address)) {
+ this.peerPreferences.put(address, preferences);
+ }
+ }
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public final class BgpPeer implements PeerBean, BGPPeerStateConsumer {
+public class BgpPeer implements PeerBean, BGPPeerStateConsumer {
private static final Logger LOG = LoggerFactory.getLogger(BgpPeer.class);
}
this.bgpPeer = new BGPPeer(tableTypeRegistry, this.neighborAddress, peerGroupName, rib, role, clusterId,
- neighborLocalAs, BgpPeer.this.rpcRegistry, afiSafisAdvertized, gracefulTables);
+ neighborLocalAs, BgpPeer.this.rpcRegistry, afiSafisAdvertized, gracefulTables, BgpPeer.this);
this.prefs = new BGPSessionPreferences(neighborLocalAs, hold, rib.getBgpIdentifier(),
neighborRemoteAs, bgpParameters, getPassword(keyMapping));
this.activeConnection = OpenConfigMappingUtil.isActive(neighbor, peerGroup);
return this.bgpPeer.getPeerState();
}
}
+ public synchronized List<OptionalCapabilities> getBgpFixedCapabilities() {
+ return this.bgpPeerSingletonService.finalCapabilities;
+ }
+
+ public synchronized int getGracefulRestartTimer() {
+ return this.bgpPeerSingletonService.gracefulRestartTimer;
+ }
}
return gracefulTables;
}
- static BgpParameters getGracefulBgpParameters(final List<OptionalCapabilities> fixedCapabilities,
+ public static BgpParameters getGracefulBgpParameters(final List<OptionalCapabilities> fixedCapabilities,
final Set<TablesKey> gracefulTables,
final Set<TablesKey> preservedTables,
final int gracefulRestartTimer,
*/
@Nonnull AutoCloseable registerPeerSessionListener(PeerRegistrySessionListener listener);
+ /**
+ * Set new preferences. In case of graceful restart execution we need to send
+ * updated GracefulRestartCapability when sesison re-establish, information
+ * which tables where preserved during restart will change.
+ * Note that this method only updates preferences of already registered peer.
+ * To add new peer to registry use addPeer().
+ *
+ * @param ip of neighbor
+ * @param preferences to send in OPEN message
+ */
+ void updatePeerPreferences(IpAddress ip, BGPSessionPreferences preferences);
}
this.peerRestarting = true;
}
+ protected final synchronized void setLocalRestartingState(final boolean restarting) {
+ this.localRestarting = restarting;
+ }
+
@Override
public final BGPPeerState getPeerState() {
return this;
import org.opendaylight.protocol.bgp.parser.impl.BGPActivator;
import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderContext;
import org.opendaylight.protocol.bgp.parser.spi.pojo.SimpleBGPExtensionProviderContext;
+import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
import org.opendaylight.protocol.bgp.rib.impl.spi.BGPPeerRegistry;
import org.opendaylight.protocol.bgp.rib.impl.spi.BGPSessionPreferences;
import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderContext;
final Ipv4Address peerAddress, final RIBImpl ribImpl, final BgpParameters bgpParameters,
final PeerRole peerRole, final BGPPeerRegistry bgpPeerRegistry, final Set<TablesKey> afiSafiAdvertised,
final Set<TablesKey> gracefulAfiSafiAdvertised) {
+ return configurePeer(tableRegistry, peerAddress, ribImpl, bgpParameters, peerRole, bgpPeerRegistry,
+ afiSafiAdvertised, gracefulAfiSafiAdvertised, null);
+ }
+
+ static BGPPeer configurePeer(final BGPTableTypeRegistryConsumer tableRegistry,
+ final Ipv4Address peerAddress, final RIBImpl ribImpl, final BgpParameters bgpParameters,
+ final PeerRole peerRole, final BGPPeerRegistry bgpPeerRegistry,
+ final Set<TablesKey> afiSafiAdvertised, final Set<TablesKey> gracefulAfiSafiAdvertised,
+ final BgpPeer peer) {
final IpAddress ipAddress = new IpAddress(peerAddress);
- final BGPPeer bgpPeer = new BGPPeer(tableRegistry, new IpAddress(peerAddress), ribImpl, peerRole,
- null, afiSafiAdvertised, gracefulAfiSafiAdvertised);
+ final BGPPeer bgpPeer = new BGPPeer(tableRegistry, new IpAddress(peerAddress), null, ribImpl, peerRole,
+ null, null, null, afiSafiAdvertised, gracefulAfiSafiAdvertised, peer);
final List<BgpParameters> tlvs = Lists.newArrayList(bgpParameters);
bgpPeerRegistry.addPeer(ipAddress, bgpPeer,
new BGPSessionPreferences(AS_NUMBER, HOLDTIMER, new BgpId(RIB_ID), AS_NUMBER, tlvs));
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import io.netty.channel.ChannelFuture;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.ResetSessionInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.ResetSessionInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.ResetSessionOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RestartGracefullyInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RestartGracefullyInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RestartGracefullyOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RouteRefreshRequestInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RouteRefreshRequestInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.RouteRefreshRequestOutput;
final ChannelOutputLimiter limiter = new ChannelOutputLimiter(this.session);
Mockito.doReturn(limiter).when(this.session).getLimiter();
- Mockito.doReturn(this.future).when(this.session).writeAndFlush(Mockito.any(Notification.class));
+ Mockito.doReturn(this.future).when(this.session).writeAndFlush(any(Notification.class));
Mockito.doReturn(true).when(this.future).isDone();
Mockito.doReturn(null).when(this.future).get();
Mockito.doReturn(true).when(this.future).isSuccess();
final Future<RpcResult<ResetSessionOutput>> result = this.rpc.resetSession(input);
assertTrue(result.get().getErrors().isEmpty());
}
+
+ @Test
+ public void testRestartGracefullyRequestFailedRequest() throws ExecutionException, InterruptedException {
+ final long referraltimerSeconds = 10L;
+ Mockito.doReturn(new SimpleSessionListener().restartGracefully(referraltimerSeconds))
+ .when(this.peerRpcs).restartGracefully(referraltimerSeconds);
+ final RestartGracefullyInput input = new RestartGracefullyInputBuilder()
+ .setSelectionDeferralTime(referraltimerSeconds)
+ .build();
+ final ListenableFuture<RpcResult<RestartGracefullyOutput>> result = this.rpc.restartGracefully(input);
+ assertTrue(!result.get().getErrors().isEmpty());
+ }
}
import java.util.function.Function;
import org.junit.Assert;
import org.opendaylight.protocol.bgp.rib.spi.State;
+import org.opendaylight.protocol.bgp.rib.spi.state.BGPGracelfulRestartState;
public final class CheckUtil {
final long restartTimeNanos = TimeUnit.SECONDS.toNanos(restartTimeSeconds + 1);
final Stopwatch sw = Stopwatch.createStarted();
while (sw.elapsed(TimeUnit.NANOSECONDS) <= restartTimeNanos) {
- if (peer.getPeerState().getBGPGracelfulRestart().isPeerRestarting()) {
+ final BGPGracelfulRestartState restartState = peer.getPeerState().getBGPGracelfulRestart();
+ if (restartState.isPeerRestarting() || restartState.isLocalRestarting()) {
Uninterruptibles.sleepUninterruptibly(SLEEP_FOR_MILLIS, TimeUnit.MILLISECONDS);
} else {
return;
*/
package org.opendaylight.protocol.bgp.rib.impl;
-import static org.junit.Assert.assertNull;
import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkIdleState;
import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkStateIsNotRestarting;
import static org.opendaylight.protocol.bgp.rib.impl.CheckUtil.checkUpState;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
import org.opendaylight.protocol.bgp.mode.api.PathSelectionMode;
import org.opendaylight.protocol.bgp.mode.impl.add.all.paths.AllPathSelection;
import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.rib.impl.config.BgpPeer;
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.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
public class GracefulRestartTest extends AbstractAddPathTest {
private BGPSessionImpl session;
+ private BGPSessionImpl session2;
private BGPPeer peer;
+ private BgpPeer bgpPeer;
+ private BgpParameters parameters;
private final Set<TablesKey> afiSafiAdvertised = new HashSet<>();
private final Set<TablesKey> gracefulAfiSafiAdvertised = new HashSet<>();
private RIBImpl ribImpl;
private Channel serverChannel;
private SimpleSessionListener listener = new SimpleSessionListener();
+ private SimpleSessionListener listener2 = new SimpleSessionListener();
+ private static final int DEFERRAL_TIMER = 5;
private static final RibId RIBID = new RibId("test-rib");
private final Ipv4Prefix PREFIX2 = new Ipv4Prefix("2.2.2.2/32");
private final Ipv6Prefix PREFIX3 = new Ipv6Prefix("dead:beef::/64");
+ private final Ipv6Address IPV6_NEXT_HOP = new Ipv6Address("dead:beef::1");
private static final TablesKey IPV6_TABLES_KEY = new TablesKey(Ipv6AddressFamily.class,
UnicastSubsequentAddressFamily.class);
gracefulAfiSafiAdvertised.add(new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class));
afiSafiAdvertised.addAll(gracefulAfiSafiAdvertised);
afiSafiAdvertised.add(IPV6_TABLES_KEY);
- final BgpParameters parameters = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
+ this.parameters = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
+ this.bgpPeer = Mockito.mock(BgpPeer.class);
+ Mockito.doReturn(GRACEFUL_RESTART_TIME).when(this.bgpPeer).getGracefulRestartTimer();
+ Mockito.doReturn(createParameter(false, true, Collections.singletonMap(TABLES_KEY, false))
+ .getOptionalCapabilities()).when(this.bgpPeer).getBgpFixedCapabilities();
this.peer = configurePeer(this.tableRegistry, PEER1, this.ribImpl, parameters, PeerRole.Ibgp,
- this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised);
+ this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised, bgpPeer);
this.session = createPeerSession(PEER1, parameters, this.listener);
+ final BgpParameters parameters2 = createParameter(false, true, Collections.singletonMap(TABLES_KEY, true));
+ configurePeer(this.tableRegistry, PEER2, this.ribImpl, parameters2, PeerRole.Ibgp,
+ this.serverRegistry, afiSafiAdvertised, gracefulAfiSafiAdvertised);
+ this.session2 = createPeerSession(PEER2, parameters2, this.listener2);
}
@After
public void retainRoutesOnPeerRestartTest() throws Exception {
final List<Ipv4Prefix> ipv4Prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1), new Ipv4Prefix(PREFIX2));
final List<Ipv6Prefix> ipv6Prefixes = Collections.singletonList(new Ipv6Prefix(PREFIX3));
- insertRoutes(ipv4Prefixes, ipv6Prefixes);
+ insertRoutes(ipv4Prefixes, PEER1, ipv6Prefixes, IPV6_NEXT_HOP, this.session);
checkLocRibIpv4Routes(2);
checkLocRibIpv6Routes(1);
public void removeRoutesOnHoldTimeExpireTest() throws Exception {
retainRoutesOnPeerRestartTest();
checkStateIsNotRestarting(peer, GRACEFUL_RESTART_TIME);
- assertNull(getLocalRib().getRib().get(0).getPeer());
+ checkLocRibIpv4Routes(0);
+ checkLocRibIpv6Routes(0);
}
/**
retainRoutesOnPeerRestartTest();
this.session = createPeerSession(PEER1, createParameter(false, true,
Collections.singletonMap(TABLES_KEY, true)), this.listener);
- checkUpState(listener);
- insertRoutes(Collections.singletonList(new Ipv4Prefix(PREFIX1)), null);
- insertRoutes(null, null);
+ checkUpState(this.listener);
+ insertRoutes(Collections.singletonList(new Ipv4Prefix(PREFIX1)), PEER1, null, null, this.session);
+ insertRoutes(null, null, null, null, this.session);
checkLocRibIpv4Routes(1);
checkLocRibIpv6Routes(0);
}
- private BgpRib getLocalRib() throws Exception {
- return readDataOperational(getDataBroker(), BGP_IID, bgpRib -> bgpRib);
+ /**
+ * Perform local graceful restart and verify routes are preserved.
+ *
+ * @throws Exception on reading Rib failure
+ */
+ @Test
+ public void performLocalGracefulRestart() throws Exception {
+ final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
+ final List<Ipv4Prefix> ipv4prefixes2 = Arrays.asList(new Ipv4Prefix(PREFIX2));
+ final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
+ insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
+ insertRoutes(ipv4prefixes2, PEER2, null, null, this.session2);
+ checkLocRibIpv4Routes(2);
+ checkLocRibIpv6Routes(1);
+
+ this.peer.restartGracefully(DEFERRAL_TIMER).get();
+ this.session = createPeerSession(PEER1, this.parameters, this.listener);
+ checkUpState(this.listener);
+ checkUpState(this.peer);
+ checkLocRibIpv4Routes(2);
+ checkLocRibIpv6Routes(0);
+ }
+
+ /**
+ * Wait with route selection until EOT is received.
+ *
+ * @throws Exception on reading Rib failure
+ */
+ @Test
+ public void waitForEORonLocalGracefulRestart() throws Exception {
+ performLocalGracefulRestart();
+ final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
+ final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
+ insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
+ checkLocRibIpv4Routes(2);
+ checkLocRibIpv6Routes(0);
+ insertRoutes(null, null,null, null, this.session);
+ checkLocRibIpv4Routes(2);
+ checkLocRibIpv6Routes(1);
+ }
+
+ /**
+ * Wait with route selection until deferral time is expired.
+ *
+ * @throws Exception on reading Rib failure
+ */
+ @Test
+ public void waitForDeferralTimerOnLocalGracefulRestart() throws Exception {
+ performLocalGracefulRestart();
+ final List<Ipv4Prefix> ipv4prefixes = Arrays.asList(new Ipv4Prefix(PREFIX1));
+ final List<Ipv6Prefix> ipv6prefixes = Arrays.asList(new Ipv6Prefix(PREFIX3));
+ insertRoutes(ipv4prefixes, PEER1, ipv6prefixes, IPV6_NEXT_HOP, this.session);
+ checkLocRibIpv4Routes(2);
+ checkLocRibIpv6Routes(0);
+ checkStateIsNotRestarting(this.peer, DEFERRAL_TIMER);
+ checkLocRibIpv4Routes(2);
+ checkLocRibIpv6Routes(1);
}
private void checkLocRibIpv4Routes(final int expectedRoutesOnDS) throws Exception {
});
}
- private void insertRoutes(List<Ipv4Prefix> ipv4prefixes, List<Ipv6Prefix> ipv6prefixes) {
+ private void insertRoutes(List<Ipv4Prefix> ipv4prefixes, Ipv4Address ipv4NextHop, List<Ipv6Prefix> ipv6prefixes,
+ Ipv6Address ipv6NextHop, BGPSessionImpl session) {
if (ipv4prefixes == null && ipv6prefixes == null) {
- waitFutureSuccess(this.session.writeAndFlush(PeerUtil.createEndOfRib(TABLES_KEY)));
- waitFutureSuccess(this.session.writeAndFlush(PeerUtil.createEndOfRib(IPV6_TABLES_KEY)));
+ waitFutureSuccess(session.writeAndFlush(PeerUtil.createEndOfRib(TABLES_KEY)));
+ waitFutureSuccess(session.writeAndFlush(PeerUtil.createEndOfRib(IPV6_TABLES_KEY)));
return;
}
if (ipv4prefixes != null && !ipv4prefixes.isEmpty()) {
- final MpReachNlri reachIpv4 = PeerUtil.createMpReachNlri(new IpAddress(PEER1), 0,
+ final MpReachNlri reachIpv4 = PeerUtil.createMpReachNlri(new IpAddress(ipv4NextHop), 0,
ipv4prefixes.stream()
.map(IpPrefix::new)
.collect(Collectors.toList()));
final Update update1 = PeerUtil.createUpdate(BgpOrigin.Igp, Collections.emptyList(), 100, reachIpv4, null);
- waitFutureSuccess(this.session.writeAndFlush(update1));
+ waitFutureSuccess(session.writeAndFlush(update1));
}
if (ipv6prefixes != null && !ipv4prefixes.isEmpty()) {
- final MpReachNlri reachIpv6 = PeerUtil.createMpReachNlri(new IpAddress(new Ipv6Address("DEAD:BEEF::1")), 0,
+ final MpReachNlri reachIpv6 = PeerUtil.createMpReachNlri(new IpAddress(ipv6NextHop), 0,
ipv6prefixes.stream()
.map(IpPrefix::new)
.collect(Collectors.toList()));
final Update update2 = PeerUtil.createUpdate(BgpOrigin.Igp, Collections.emptyList(), 100, reachIpv6, null);
- waitFutureSuccess(this.session.writeAndFlush(update2));
+ waitFutureSuccess(session.writeAndFlush(update2));
}
}
return Futures.immediateFuture(null);
}
+ @Override
+ public ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
+ return Futures.immediateFailedFuture(
+ new UnsupportedOperationException("SimpleSessionListener doesn't support graceful restart"));
+ }
+
public State getState() {
return getSession().getState();
}
*/
@Nonnull
ListenableFuture<?> releaseConnection();
+
+ /**
+ * Perform graceful restart. Wait with route selection until EOR is received or
+ * selection-deferral-timer expires.
+ *
+ * @param selectionDeferralTimerSeconds time to wait in seconds
+ */
+ @Nonnull
+ ListenableFuture<?> restartGracefully(long selectionDeferralTimerSeconds);
}
package org.opendaylight.protocol.bgp.testtool;
import com.google.common.util.concurrent.FluentFuture;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;
import java.util.concurrent.atomic.LongAdder;
import org.opendaylight.mdsal.common.api.CommitInfo;
return CommitInfo.emptyFluentFuture();
}
+ @Override
+ public ListenableFuture<?> restartGracefully(final long selectionDeferralTimerSeconds) {
+ return Futures.immediateFailedFuture(
+ new UnsupportedOperationException("Testtool doesn't support graceful restart"));
+ }
+
void printCount(final String localAddress) {
LOG.info("Peer {} received {} update messages.", localAddress, this.messageCounter.longValue());
}