introduce graceful restart speaker capability 45/76245/16
authorMatej Perina <matej.perina@pantheon.tech>
Wed, 19 Sep 2018 06:48:18 +0000 (08:48 +0200)
committerRobert Varga <nite@hq.sk>
Mon, 3 Dec 2018 10:54:07 +0000 (10:54 +0000)
JIRA: BGPCEP-808

Change-Id: I6c4ac1b7697311c31405d0c660fb3e53ca1d8e78
Signed-off-by: Matej Perina <matej.perina@pantheon.tech>
15 files changed:
bgp/rib-api/src/main/yang/bgp-peer-rpc.yang
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BGPPeer.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/BgpPeerRpc.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/StrictBGPPeerRegistry.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/config/BgpPeer.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/config/GracefulRestartUtil.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/spi/BGPPeerRegistry.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/state/BGPPeerStateImpl.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/AbstractAddPathTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/BgpPeerRpcTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/CheckUtil.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/GracefulRestartTest.java
bgp/rib-impl/src/test/java/org/opendaylight/protocol/bgp/rib/impl/SimpleSessionListener.java
bgp/rib-spi/src/main/java/org/opendaylight/protocol/bgp/rib/spi/PeerRPCs.java
bgp/testtool/src/main/java/org/opendaylight/protocol/bgp/testtool/TestingListener.java

index ea48805529a01003dbd65bb8e183a25d809adaf0..dbc7a979425bfad85240f4a687872b158816ad61 100644 (file)
@@ -73,4 +73,19 @@ module bgp-peer-rpc {
             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;
+            }
+        }
+    }
 }
index 06323f349327cb7516f231d06b5e6d2099df4895..2153eb836e2eda9322c323070042559d07040c0e 100644 (file)
@@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
 
 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;
@@ -19,6 +20,8 @@ import com.google.common.collect.ImmutableSet;
 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;
@@ -42,6 +45,9 @@ import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
 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;
@@ -62,6 +68,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet
 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;
@@ -113,7 +120,10 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     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
@@ -135,7 +145,9 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     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,
@@ -147,12 +159,14 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
             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(
@@ -164,7 +178,7 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
             final Set<TablesKey> afiSafisAdvertized,
             final Set<TablesKey> afiSafisGracefulAdvertized) {
         this(tableTypeRegistry, neighborAddress, null, rib, role, null, null, rpcRegistry,
-                afiSafisAdvertized, afiSafisGracefulAdvertized);
+                afiSafisAdvertized, afiSafisGracefulAdvertized, null);
     }
 
 
@@ -318,12 +332,30 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
             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());
+            }
         }
     }
 
@@ -347,25 +379,20 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
                 .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);
 
@@ -390,6 +417,11 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
                         .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()) {
@@ -399,13 +431,27 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
                     .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)) {
@@ -461,20 +507,25 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
         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();
@@ -524,15 +575,29 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
             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) {
@@ -618,4 +683,39 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     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);
+    }
 }
index 5db48bf2b22e059971f59bd3f636388b3cf26cd4..146a1d51ae13dcba610d434e3d3e247991323bb0 100644 (file)
@@ -9,12 +9,14 @@ package org.opendaylight.protocol.bgp.rib.impl;
 
 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;
@@ -23,6 +25,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer
 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;
@@ -38,6 +43,7 @@ public class BgpPeerRpc implements BgpPeerRpcService {
     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;
@@ -61,6 +67,31 @@ public class BgpPeerRpc implements BgpPeerRpcService {
         }, 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) {
index 0fa2fa7f14a1a6846a1cfb8b64c0bfad79ae2539..4a39a232e930294864a9bb56cd2999621d28a8f6 100644 (file)
@@ -388,4 +388,11 @@ public final class StrictBGPPeerRegistry implements BGPPeerRegistry {
             }
         };
     }
+
+    @Override
+    public void updatePeerPreferences(final IpAddress address, final BGPSessionPreferences preferences) {
+        if (this.peerPreferences.containsKey(address)) {
+            this.peerPreferences.put(address, preferences);
+        }
+    }
 }
index eb8e9f1257e49b296ca50962cd192d5f7feef710..7a534ad9ab7b08252e3deff9104afa09f69b5529 100644 (file)
@@ -66,7 +66,7 @@ import org.osgi.framework.ServiceRegistration;
 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);
 
@@ -274,7 +274,7 @@ public final class BgpPeer implements PeerBean, BGPPeerStateConsumer {
             }
 
             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);
@@ -329,4 +329,11 @@ public final class BgpPeer implements PeerBean, BGPPeerStateConsumer {
             return this.bgpPeer.getPeerState();
         }
     }
+    public synchronized List<OptionalCapabilities> getBgpFixedCapabilities() {
+        return this.bgpPeerSingletonService.finalCapabilities;
+    }
+
+    public synchronized int getGracefulRestartTimer() {
+        return this.bgpPeerSingletonService.gracefulRestartTimer;
+    }
 }
index 15004a4e3fa53bdcc709ccc24ed7caf9de617583..f5ae70e5df5d2a0795bbd1dc14adcedb005a1834 100644 (file)
@@ -83,7 +83,7 @@ public final class GracefulRestartUtil {
         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,
index 1ce4ec6741586e95655b756a7d8a1dc2327aba29..2bb2fcc1638019ec49e9aa338e9e99a453e11ff5 100644 (file)
@@ -95,4 +95,15 @@ public interface BGPPeerRegistry extends AutoCloseable {
      */
     @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);
 }
index 5dbe58f66bfe255fecae93e184d3f3e3f2449646..713a0f39d863a85bb3711cf8267520ad03557e15 100644 (file)
@@ -214,6 +214,10 @@ public abstract class BGPPeerStateImpl extends DefaultRibReference implements BG
         this.peerRestarting = true;
     }
 
+    protected final synchronized void setLocalRestartingState(final boolean restarting) {
+        this.localRestarting = restarting;
+    }
+
     @Override
     public final BGPPeerState getPeerState() {
         return this;
index 71bb6a1310ad80a0723cee229e4ddd8aecd7b932..54274e4d1e190e733129b48b0c9b9f03b27e388c 100644 (file)
@@ -39,6 +39,7 @@ import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
 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;
@@ -252,10 +253,19 @@ public abstract class AbstractAddPathTest extends DefaultRibPoliciesMockTest {
             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));
index 267ea7e9b9b03732760ad878653b994055f1f5d7..3180b7ff144a9b6e6b278abf27edcb6fd30841c8 100644 (file)
@@ -9,8 +9,10 @@ package org.opendaylight.protocol.bgp.rib.impl;
 
 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;
@@ -25,6 +27,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer
 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;
@@ -54,7 +59,7 @@ public final class BgpPeerRpcTest {
         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();
@@ -90,4 +95,16 @@ public final class BgpPeerRpcTest {
         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());
+    }
 }
index 75fa2737417d802006ee7f33aabba2799a3d405d..7fad72a19619d2c473a2b5b4e549812146f3da21 100644 (file)
@@ -13,6 +13,7 @@ import java.util.concurrent.TimeUnit;
 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 {
 
@@ -73,7 +74,8 @@ 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;
index 62d4d783d53c68dbe4b2864e6eeeec38b51ddc1d..a76f0e4d61cafdc6efb7d542f4b828c53bbe75d1 100644 (file)
@@ -7,7 +7,6 @@
  */
 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;
@@ -33,11 +32,14 @@ import org.junit.After;
 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;
@@ -67,15 +69,21 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 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);
 
@@ -111,10 +119,18 @@ public class GracefulRestartTest extends AbstractAddPathTest {
         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
@@ -148,7 +164,7 @@ public class GracefulRestartTest extends AbstractAddPathTest {
     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);
 
@@ -169,7 +185,8 @@ public class GracefulRestartTest extends AbstractAddPathTest {
     public void removeRoutesOnHoldTimeExpireTest() throws Exception {
         retainRoutesOnPeerRestartTest();
         checkStateIsNotRestarting(peer, GRACEFUL_RESTART_TIME);
-        assertNull(getLocalRib().getRib().get(0).getPeer());
+        checkLocRibIpv4Routes(0);
+        checkLocRibIpv6Routes(0);
     }
 
     /**
@@ -221,15 +238,70 @@ public class GracefulRestartTest extends AbstractAddPathTest {
         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 {
@@ -258,29 +330,30 @@ public class GracefulRestartTest extends AbstractAddPathTest {
         });
     }
 
-    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));
         }
     }
 
index 401c7679c020fc4a1249cc3ab41158c3355538fb..8826ddfdbadb57b4269464753fd21aca32da0221 100644 (file)
@@ -87,6 +87,12 @@ public final class SimpleSessionListener implements BGPSessionListener, Listener
         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();
     }
index 4636137c63463091bc872d89e02b96e8d122337e..3783bda691c11c6b1f608956cac81058d566cd08 100644 (file)
@@ -19,4 +19,13 @@ public interface PeerRPCs {
      */
     @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);
 }
index 4d0158b2db7faa436064dd91be5fa87e8c6503c6..55a117bd4740e54840a98e7b1fc345d7e90a6869 100644 (file)
@@ -8,6 +8,8 @@
 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;
@@ -81,6 +83,12 @@ final class TestingListener implements BGPSessionListener {
         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());
     }