YANG revision dates mass-update
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPPeer.java
index a14f54c6012c79438ae2250b2a5be38646474283..937382a0f9a71c5ae1eede7e28d7eff88dd949fd 100644 (file)
@@ -10,14 +10,17 @@ package org.opendaylight.protocol.bgp.rib.impl;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects;
 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;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import com.google.common.collect.ImmutableMap;
 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.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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -25,20 +28,28 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.Optional;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
-import javax.annotation.concurrent.GuardedBy;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
-import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
-import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
-import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.checkerframework.checker.lock.qual.GuardedBy;
+import org.checkerframework.checker.lock.qual.Holding;
+import org.opendaylight.mdsal.binding.api.RpcProviderService;
+import org.opendaylight.mdsal.binding.api.Transaction;
+import org.opendaylight.mdsal.binding.api.TransactionChain;
 import org.opendaylight.mdsal.common.api.CommitInfo;
 import org.opendaylight.mdsal.common.api.CommitInfo;
+import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
+import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryConsumer;
 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.openconfig.spi.BGPTableTypeRegistryConsumer;
 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.parser.spi.RevisedErrorHandlingSupport;
+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.protocol.bgp.rib.impl.spi.RIB;
 import org.opendaylight.protocol.bgp.rib.impl.state.BGPSessionStateProvider;
 import org.opendaylight.protocol.bgp.rib.spi.BGPSession;
@@ -49,23 +60,23 @@ import org.opendaylight.protocol.bgp.rib.spi.RouterIds;
 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
 import org.opendaylight.protocol.bgp.rib.spi.state.BGPSessionState;
 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTimersState;
 import org.opendaylight.protocol.bgp.rib.spi.state.BGPTransportState;
-import org.opendaylight.protocol.bgp.route.targetcontrain.spi.ClientRouteTargetContrainCache;
-import org.opendaylight.protocol.concepts.AbstractRegistration;
 import org.opendaylight.protocol.util.Ipv4Util;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
 import org.opendaylight.protocol.util.Ipv4Util;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
-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.IpAddressNoZone;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
 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.inet.rev180329.ipv4.prefixes.DestinationIpv4Builder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4Prefixes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.inet.rev180329.ipv4.prefixes.destination.ipv4.Ipv4PrefixesBuilder;
 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.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;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.Update;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.open.message.BgpParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.path.attributes.AttributesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev200120.update.message.Nlri;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpAddPathTableType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
 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.multiprotocol.rev180329.SendReceive;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpAddPathTableType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
 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.multiprotocol.rev180329.SendReceive;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.GracefulRestartCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.add.path.capability.AddressFamilies;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.add.path.capability.AddressFamilies;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
@@ -74,19 +85,19 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.mult
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.BgpPeerRpcService;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.BgpPeerRpcService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.peer.rpc.rev180329.PeerContext;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibOut;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.PeerRole;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.PeerKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.peer.AdjRibOut;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.route.target.constrain.rev180618.route.target.constrain.routes.route.target.constrain.routes.RouteTargetConstrainRoute;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.ClusterIdentifier;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteTarget;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.ClusterIdentifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.Ipv4AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.RouteTarget;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.SubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev200120.UnicastSubsequentAddressFamily;
+import org.opendaylight.yangtools.concepts.ObjectRegistration;
+import org.opendaylight.yangtools.concepts.Registration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.Notification;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.Notification;
@@ -100,16 +111,21 @@ import org.slf4j.LoggerFactory;
  */
 public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
  */
 public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     private static final Logger LOG = LoggerFactory.getLogger(BGPPeer.class);
+    private static final TablesKey IPV4_UCAST_TABLE_KEY = new TablesKey(Ipv4AddressFamily.class,
+        UnicastSubsequentAddressFamily.class);
 
 
-    private Set<TablesKey> tables = Collections.emptySet();
+    private ImmutableSet<TablesKey> tables = ImmutableSet.of();
     private final RIB rib;
     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
     private final List<RouteTarget> rtMemberships = new ArrayList<>();
     private final RIB rib;
     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
     private final List<RouteTarget> rtMemberships = new ArrayList<>();
-    private final RpcProviderRegistry rpcRegistry;
+    private final RpcProviderService rpcRegistry;
     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
     private final BGPTableTypeRegistryConsumer tableTypeRegistry;
+    private final BgpPeer bgpPeer;
     private InstanceIdentifier<AdjRibOut> peerRibOutIId;
     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")
     @GuardedBy("this")
-    private AbstractRegistration trackerRegistration;
+    private Registration trackerRegistration;
     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
             = CacheBuilder.newBuilder()
             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
     private final LoadingCache<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>> tablesIId
             = CacheBuilder.newBuilder()
             .build(new CacheLoader<TablesKey, KeyedInstanceIdentifier<Tables, TablesKey>>() {
@@ -120,47 +136,41 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
             });
 
     @GuardedBy("this")
             });
 
     @GuardedBy("this")
-    private BGPSession session;
+    private BGPSession currentSession;
     @GuardedBy("this")
     private AdjRibInWriter ribWriter;
     @GuardedBy("this")
     private EffectiveRibInWriter effRibInWriter;
     @GuardedBy("this")
     private AdjRibInWriter ribWriter;
     @GuardedBy("this")
     private EffectiveRibInWriter effRibInWriter;
-    private RoutedRpcRegistration<BgpPeerRpcService> rpcRegistration;
+    private ObjectRegistration<BgpPeerRpcService> rpcRegistration;
     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
     private YangInstanceIdentifier peerPath;
     private boolean sessionUp;
     private Map<TablesKey, SendReceive> addPathTableMaps = Collections.emptyMap();
     private YangInstanceIdentifier peerPath;
     private boolean sessionUp;
+    private boolean llgrSupport;
+    private Stopwatch peerRestartStopwatch;
+    private long currentSelectionDeferralTimerSeconds;
+    private final List<TablesKey> missingEOT = new ArrayList<>();
 
     public BGPPeer(
             final BGPTableTypeRegistryConsumer tableTypeRegistry,
 
     public BGPPeer(
             final BGPTableTypeRegistryConsumer tableTypeRegistry,
-            final IpAddress neighborAddress,
+            final IpAddressNoZone neighborAddress,
             final String peerGroupName,
             final RIB rib,
             final PeerRole role,
             final ClusterIdentifier clusterId,
             final AsNumber localAs,
             final String peerGroupName,
             final RIB rib,
             final PeerRole role,
             final ClusterIdentifier clusterId,
             final AsNumber localAs,
-            final RpcProviderRegistry rpcRegistry,
+            final RpcProviderService rpcRegistry,
             final Set<TablesKey> afiSafisAdvertized,
             final Set<TablesKey> afiSafisAdvertized,
-            final Set<TablesKey> afiSafisGracefulAdvertized) {
+            final Set<TablesKey> afiSafisGracefulAdvertized,
+            final Map<TablesKey, Integer> llGracefulTablesAdvertised,
+            final BgpPeer bgpPeer) {
         super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
         super(rib, Ipv4Util.toStringIP(neighborAddress), peerGroupName, role, clusterId,
-                localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized);
+                localAs, neighborAddress, afiSafisAdvertized, afiSafisGracefulAdvertized, llGracefulTablesAdvertised);
         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
         this.rib = requireNonNull(rib);
         this.rpcRegistry = rpcRegistry;
         this.tableTypeRegistry = requireNonNull(tableTypeRegistry);
         this.rib = requireNonNull(rib);
         this.rpcRegistry = rpcRegistry;
+        this.bgpPeer = bgpPeer;
     }
 
     }
 
-    BGPPeer(
-            final BGPTableTypeRegistryConsumer tableTypeRegistry,
-            final IpAddress neighborAddress,
-            final RIB rib,
-            final PeerRole role,
-            final RpcProviderRegistry rpcRegistry,
-            final Set<TablesKey> afiSafisAdvertized,
-            final Set<TablesKey> afiSafisGracefulAdvertized) {
-        this(tableTypeRegistry, neighborAddress, null, rib, role, null, null, rpcRegistry,
-                afiSafisAdvertized, afiSafisGracefulAdvertized);
-    }
-
-
     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
     private static Attributes nextHopToAttribute(final Attributes attrs, final MpReachNlri mpReach) {
         if (attrs.getCNextHop() == null && mpReach.getCNextHop() != null) {
             final AttributesBuilder attributesBuilder = new AttributesBuilder(attrs);
@@ -204,8 +214,8 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
 
             Optional<Nlri> nlriAnounced = Optional.empty();
             if (isAnyNlriAnnounced) {
 
             Optional<Nlri> nlriAnounced = Optional.empty();
             if (isAnyNlriAnnounced) {
-                nlriAnounced = message.getNlri().stream().filter(n -> Objects.equal(n.getPrefix(), w.getPrefix())
-                        && Objects.equal(n.getPathId(), w.getPathId()))
+                nlriAnounced = message.getNlri().stream().filter(n -> Objects.equals(n.getPrefix(), w.getPrefix())
+                        && Objects.equals(n.getPathId(), w.getPathId()))
                         .findAny();
             }
             if (!nlriAnounced.isPresent()) {
                         .findAny();
             }
             if (!nlriAnounced.isPresent()) {
@@ -225,6 +235,7 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     }
 
     public synchronized void instantiateServiceInstance() {
     }
 
     public synchronized void instantiateServiceInstance() {
+        createDomChain();
         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
         setActive(true);
     }
         this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
         setActive(true);
     }
@@ -239,14 +250,12 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
 
     @Override
     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
 
     @Override
     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
-        if (!(msg instanceof Update) && !(msg instanceof RouteRefresh)) {
-            LOG.info("Ignoring unhandled message class {}", msg.getClass());
-            return;
-        }
         if (msg instanceof Update) {
             onUpdateMessage((Update) msg);
         if (msg instanceof Update) {
             onUpdateMessage((Update) msg);
-        } else {
+        } else if (msg instanceof RouteRefresh) {
             onRouteRefreshMessage((RouteRefresh) msg);
             onRouteRefreshMessage((RouteRefresh) msg);
+        } else {
+            LOG.info("Ignoring unhandled message class {}", msg.getClass());
         }
     }
 
         }
     }
 
@@ -255,13 +264,14 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
 
         final TablesKey key = new TablesKey(rrAfi, rrSafi);
         final Class<? extends SubsequentAddressFamily> rrSafi = message.getSafi();
 
         final TablesKey key = new TablesKey(rrAfi, rrSafi);
-        final AdjRibOutListener listener = this.adjRibOutListenerSet.get(key);
-        if (listener != null) {
-            listener.close();
-            this.adjRibOutListenerSet.remove(key);
-            createAdjRibOutListener(key, listener.isMpSupported());
-        } else {
-            LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
+        synchronized (this) {
+            final AdjRibOutListener listener = this.adjRibOutListenerSet.remove(key);
+            if (listener != null) {
+                listener.close();
+                createAdjRibOutListener(key, listener.isMpSupported());
+            } else {
+                LOG.info("Ignoring RouteRefresh message. Afi/Safi is not supported: {}, {}.", rrAfi, rrSafi);
+            }
         }
     }
 
         }
     }
 
@@ -302,92 +312,187 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
         if (mpReach != null) {
             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
         }
         if (mpReach != null) {
             this.ribWriter.updateRoutes(mpReach, nextHopToAttribute(attrs, mpReach));
         }
-        MpUnreachNlri mpUnreach;
+        final MpUnreachNlri mpUnreach;
         if (message.getWithdrawnRoutes() != null) {
             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
         } else {
             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
         }
         if (message.getWithdrawnRoutes() != null) {
             mpUnreach = prefixesToMpUnreach(message, isAnyNlriAnnounced);
         } else {
             mpUnreach = MessageUtil.getMpUnreachNlri(attrs);
         }
+        final boolean endOfRib = BgpPeerUtil.isEndOfRib(message);
         if (mpUnreach != null) {
         if (mpUnreach != null) {
-            this.ribWriter.removeRoutes(mpUnreach);
+            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();
+        }
+    }
+
+    @Holding("this")
+    private void handleGracefulEndOfRib() {
+        if (isLocalRestarting()) {
+            if (this.missingEOT.isEmpty()) {
+                createEffRibInWriter();
+                this.effRibInWriter.init();
+                registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
+                for (final TablesKey key : getAfiSafisAdvertized()) {
+                    createAdjRibOutListener(key, true);
+                }
+                setLocalRestartingState(false);
+                setGracefulPreferences(false, Collections.emptySet());
+            }
         }
     }
 
     @Override
     public synchronized void onSessionUp(final BGPSession session) {
         }
     }
 
     @Override
     public synchronized void onSessionUp(final BGPSession session) {
-        this.session = session;
+        this.currentSession = session;
         this.sessionUp = true;
         this.bindingChain = this.rib.createPeerChain(this);
         this.sessionUp = true;
         this.bindingChain = this.rib.createPeerChain(this);
-        if (this.session instanceof BGPSessionStateProvider) {
-            ((BGPSessionStateProvider) this.session).registerMessagesCounter(this);
+        if (this.currentSession instanceof BGPSessionStateProvider) {
+            ((BGPSessionStateProvider) this.currentSession).registerMessagesCounter(this);
         }
         }
+        final GracefulRestartCapability advertisedGracefulRestartCapability =
+                session.getAdvertisedGracefulRestartCapability();
+        final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp
+                .capabilities.graceful.restart.capability.Tables> advertisedTables =
+                    advertisedGracefulRestartCapability.getTables();
+        final List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp
+                .capabilities.ll.graceful.restart.capability.Tables> advertisedLLTables =
+                    session.getAdvertisedLlGracefulRestartCapability().getTables();
 
         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
 
         final List<AddressFamilies> addPathTablesType = session.getAdvertisedAddPathTableTypes();
         final Set<BgpTableType> advertizedTableTypes = session.getAdvertisedTableTypes();
-        final List<BgpTableType> advertizedGracefulRestartTableTypes = session.getAdvertisedGracefulRestartTableTypes();
         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
                 advertizedTableTypes, addPathTablesType);
         LOG.info("Session with peer {} went up with tables {} and Add Path tables {}", this.name,
                 advertizedTableTypes, addPathTablesType);
-        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
-                .yang.bgp.rib.rev180329.bgp.rib.rib.Peer.class, new PeerKey(this.peerId));
         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
                 .collect(Collectors.toSet());
         this.tables = ImmutableSet.copyOf(setTables);
         final Set<TablesKey> setTables = advertizedTableTypes.stream().map(t -> new TablesKey(t.getAfi(), t.getSafi()))
                 .collect(Collectors.toSet());
         this.tables = ImmutableSet.copyOf(setTables);
-        this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
-                this.rib.createPeerChain(this),
-                peerIId, this.tables, this.tableTypeRegistry,
-                this.rtMemberships,
-                this.rtCache);
-        registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
-        this.peerRibOutIId = peerIId.child(AdjRibOut.class);
-        this.effRibInWriter.init();
-        setAdvertizedGracefulRestartTableTypes(advertizedGracefulRestartTableTypes.stream()
-                .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
-        this.addPathTableMaps = ImmutableMap.copyOf(mapTableTypesFamilies(addPathTablesType));
-        this.trackerRegistration = this.rib.getPeerTracker().registerPeer(this);
 
 
-        for (final TablesKey key : this.tables) {
-            createAdjRibOutListener(key, true);
+        this.addPathTableMaps = mapTableTypesFamilies(addPathTablesType);
+        final boolean restartingLocally = isLocalRestarting();
+
+        if (!isRestartingGracefully()) {
+            this.rawIdentifier = InetAddresses.forString(session.getBgpId().getValue()).getAddress();
+            this.peerId = RouterIds.createPeerId(session.getBgpId());
+            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);
+            createEffRibInWriter();
+            registerPrefixesCounters(this.effRibInWriter, this.effRibInWriter);
+
+            this.effRibInWriter.init();
+            this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
+                    this.tables, this.addPathTableMaps);
+
+            if (this.rpcRegistry != null) {
+                this.rpcRegistration = this.rpcRegistry.registerRpcImplementation(BgpPeerRpcService.class,
+                    new BgpPeerRpc(this, session, this.tables), ImmutableSet.of(this.rib.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))));
+            }
+        } else {
+            final Set<TablesKey> forwardingTables;
+            if (advertisedTables == null) {
+                forwardingTables = Collections.emptySet();
+            } else {
+                forwardingTables = advertisedTables.stream()
+                        .filter(table -> table.getAfiFlags() != null)
+                        .filter(table -> table.getAfiFlags().isForwardingState())
+                        .map(table -> new TablesKey(table.getAfi(), table.getSafi()))
+                        .collect(Collectors.toSet());
+            }
+            this.ribWriter.clearTables(Sets.difference(this.tables, forwardingTables));
+            if (restartingLocally) {
+                this.effRibInWriter.close();
+                this.peerRestartStopwatch = Stopwatch.createStarted();
+                handleSelectionReferralTimer();
+                this.missingEOT.addAll(this.tables);
+            }
         }
         }
+        if (advertisedTables == null || advertisedTables.isEmpty()) {
+            setAdvertizedGracefulRestartTableTypes(Collections.emptyList());
+        } else {
+            setAdvertizedGracefulRestartTableTypes(advertisedTables.stream()
+                    .map(t -> new TablesKey(t.getAfi(), t.getSafi())).collect(Collectors.toList()));
+        }
+        setAfiSafiGracefulRestartState(advertisedGracefulRestartCapability.getRestartTime().toJava(), false,
+            restartingLocally);
+
+        final Map<TablesKey, Integer> llTablesReceived;
+        if (advertisedLLTables != null) {
+            llTablesReceived = new HashMap<>();
+            for (org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp
+                    .capabilities.ll.graceful.restart.capability.Tables table : advertisedLLTables) {
+                llTablesReceived.put(new TablesKey(table.getAfi(), table.getSafi()),
+                    table.getLongLivedStaleTime().getValue().intValue());
+            }
+        } else {
+            llTablesReceived = Collections.emptyMap();
+        }
+        setAdvertizedLlGracefulRestartTableTypes(llTablesReceived);
 
 
-        addBgp4Support();
+        if (!llTablesReceived.isEmpty()) {
+            llgrSupport = true;
+            // FIXME: propagate preserved tables
+        } else {
+            // FIXME: clear preserved tables
+            llgrSupport = false;
+        }
 
 
-        this.peerPath = createPeerPath();
-        this.ribWriter = this.ribWriter.transform(this.peerId, this.peerPath, this.rib.getRibSupportContext(),
-                this.tables, this.addPathTableMaps);
+        if (!restartingLocally) {
+            addBgp4Support();
+            for (final TablesKey key : getAfiSafisAdvertized()) {
+                createAdjRibOutListener(key, true);
+            }
+        }
 
 
-        if (this.rpcRegistry != null) {
-            this.rpcRegistration = this.rpcRegistry.addRoutedRpcImplementation(BgpPeerRpcService.class,
-                    new BgpPeerRpc(this, session, this.tables));
-            final KeyedInstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib
-                    .rev180329.bgp.rib.rib.Peer, PeerKey> path = this.rib.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.rpcRegistration.registerPath(PeerContext.class, path);
+        // SpotBugs does not grok Optional.ifPresent() and thinks we are using unsynchronized access
+        final Optional<RevisedErrorHandlingSupport> errorHandling = this.bgpPeer.getErrorHandling();
+        if (errorHandling.isPresent()) {
+            this.currentSession.addDecoderConstraint(RevisedErrorHandlingSupport.class, errorHandling.get());
         }
     }
 
         }
     }
 
+    private boolean isRestartingGracefully() {
+        return isLocalRestarting() || isPeerRestarting();
+    }
+
+    private synchronized void createEffRibInWriter() {
+        this.effRibInWriter = new EffectiveRibInWriter(this, this.rib,
+            this.rib.createPeerDOMChain(this),
+            this.peerPath, 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
     //try to add a support for old-school BGP-4, if peer did not advertise IPv4-Unicast MP capability
-    private synchronized void addBgp4Support() {
-        final TablesKey key = new TablesKey(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class);
-        if (!this.tables.contains(key)) {
+    @Holding("this")
+    private void addBgp4Support() {
+        if (!this.tables.contains(IPV4_UCAST_TABLE_KEY)) {
             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
             final HashSet<TablesKey> newSet = new HashSet<>(this.tables);
-            newSet.add(key);
+            newSet.add(IPV4_UCAST_TABLE_KEY);
             this.tables = ImmutableSet.copyOf(newSet);
             this.tables = ImmutableSet.copyOf(newSet);
-            createAdjRibOutListener(key, false);
+            createAdjRibOutListener(IPV4_UCAST_TABLE_KEY, false);
         }
     }
 
         }
     }
 
-    private synchronized void createAdjRibOutListener(final TablesKey key,
-            final boolean mpSupport) {
+    @Holding("this")
+    private void createAdjRibOutListener(final TablesKey key, final boolean mpSupport) {
         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
 
         // not particularly nice
         final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
 
         // not particularly nice
-        if (ribSupport != null && this.session instanceof BGPSessionImpl) {
-            final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.session).getLimiter();
+        if (ribSupport != null && this.currentSession instanceof BGPSessionImpl) {
+            final ChannelOutputLimiter limiter = ((BGPSessionImpl) this.currentSession).getLimiter();
             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
                     this.rib.getService(), limiter, mpSupport);
             final AdjRibOutListener adjRibOut = AdjRibOutListener.create(this.peerId, key,
                     this.rib.getYangRibId(), this.rib.getCodecsRegistry(), ribSupport,
                     this.rib.getService(), limiter, mpSupport);
@@ -397,27 +502,25 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     }
 
     @Override
     }
 
     @Override
-    public synchronized void onSessionDown(final BGPSession session, final Exception e) {
-        if (e.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
+    public synchronized void onSessionDown(final BGPSession session, final Exception exc) {
+        if (exc.getMessage().equals(BGPSessionImpl.END_OF_INPUT)) {
             LOG.info("Session with peer {} went down", this.name);
         } else {
             LOG.info("Session with peer {} went down", this.name);
         } else {
-            LOG.info("Session with peer {} went down", this.name, e);
+            LOG.info("Session with peer {} went down", this.name, exc);
         }
         }
-        releaseConnection();
+        releaseConnectionGracefully();
     }
 
     @Override
     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
         LOG.info("Session with peer {} terminated: {}", this.name, cause);
     }
 
     @Override
     public synchronized void onSessionTerminated(final BGPSession session, final BGPTerminationReason cause) {
         LOG.info("Session with peer {} terminated: {}", this.name, cause);
-        releaseConnection();
+        releaseConnectionGracefully();
     }
 
     @Override
     public String toString() {
     }
 
     @Override
     public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("name", this.name)
-                .add("tables", this.tables).toString();
-        }
+        return MoreObjects.toStringHelper(this).add("name", this.name).add("tables", this.tables).toString();
+    }
 
     @Override
     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
 
     @Override
     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
@@ -425,6 +528,50 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
         this.sessionUp = false;
         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
         this.adjRibOutListenerSet.clear();
         this.sessionUp = false;
         this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
         this.adjRibOutListenerSet.clear();
+        final FluentFuture<? extends CommitInfo> future;
+        if (!isRestartingGracefully()) {
+            future = terminateConnection();
+        } else {
+            final Set<TablesKey> gracefulTables = getGracefulTables();
+            this.ribWriter.storeStaleRoutes(gracefulTables);
+            future = this.ribWriter.clearTables(Sets.difference(this.tables, gracefulTables));
+            if (isPeerRestarting()) {
+                this.peerRestartStopwatch = Stopwatch.createStarted();
+                handleRestartTimer();
+            }
+        }
+        releaseBindingChain();
+
+        closeSession();
+        return future;
+    }
+
+    @Holding("this")
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private void closeSession() {
+        if (this.currentSession != null) {
+            try {
+                if (isRestartingGracefully()) {
+                    this.currentSession.closeWithoutMessage();
+                } else {
+                    this.currentSession.close();
+                }
+            } catch (final Exception e) {
+                LOG.warn("Error closing session with peer", e);
+            }
+            this.currentSession = null;
+        }
+    }
+
+    private Set<TablesKey> getGracefulTables() {
+        return this.tables.stream()
+                .filter(this::isGracefulRestartReceived)
+                .filter(this::isGracefulRestartAdvertized)
+                .collect(Collectors.toSet());
+    }
+
+    private synchronized FluentFuture<? extends CommitInfo> terminateConnection() {
+        final FluentFuture<? extends CommitInfo> future;
         if (this.trackerRegistration != null) {
             this.trackerRegistration.close();
             this.trackerRegistration = null;
         if (this.trackerRegistration != null) {
             this.trackerRegistration.close();
             this.trackerRegistration = null;
@@ -432,29 +579,59 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
         if (this.rpcRegistration != null) {
             this.rpcRegistration.close();
         }
         if (this.rpcRegistration != null) {
             this.rpcRegistration.close();
         }
-        releaseBindingChain();
-
         this.ribWriter.releaseChain();
         this.ribWriter.releaseChain();
-        // FIXME: BUG-196: support graceful
 
         if (this.effRibInWriter != null) {
             this.effRibInWriter.close();
         }
 
         if (this.effRibInWriter != null) {
             this.effRibInWriter.close();
         }
-        this.tables = Collections.emptySet();
+        this.tables = ImmutableSet.of();
         this.addPathTableMaps = Collections.emptyMap();
         this.addPathTableMaps = Collections.emptyMap();
-        final FluentFuture<? extends CommitInfo> future = removePeer(this.peerPath);
-        if (this.session != null) {
-            try {
-                this.session.close();
-            } catch (final Exception e) {
-                LOG.warn("Error closing session with peer", e);
-            }
-            this.session = null;
-        }
+        future = removePeer(this.peerPath);
         resetState();
         resetState();
+
         return future;
     }
 
         return future;
     }
 
+    /**
+     * If Graceful Restart Timer expires, remove all routes advertised by peer.
+     */
+    private synchronized void handleRestartTimer() {
+        if (!isPeerRestarting()) {
+            return;
+        }
+
+        final long peerRestartTimeNanos = TimeUnit.SECONDS.toNanos(getPeerRestartTime());
+        final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
+        if (elapsedNanos >= peerRestartTimeNanos) {
+            setAfiSafiGracefulRestartState(0, false, false);
+            onSessionTerminated(this.currentSession, new BGPTerminationReason(BGPError.HOLD_TIMER_EXPIRED));
+        }
+
+        currentSession.schedule(this::handleRestartTimer, peerRestartTimeNanos - elapsedNanos, TimeUnit.NANOSECONDS);
+    }
+
+    private synchronized void handleSelectionReferralTimer() {
+        if (!isLocalRestarting()) {
+            return;
+        }
+
+        final long referalTimerNanos = TimeUnit.SECONDS.toNanos(this.currentSelectionDeferralTimerSeconds);
+        final long elapsedNanos = this.peerRestartStopwatch.elapsed(TimeUnit.NANOSECONDS);
+        if (elapsedNanos >= referalTimerNanos) {
+            this.missingEOT.clear();
+            handleGracefulEndOfRib();
+        }
+        currentSession.schedule(this::handleSelectionReferralTimer, referalTimerNanos - elapsedNanos,
+            TimeUnit.NANOSECONDS);
+    }
+
+    private void releaseConnectionGracefully() {
+        if (getPeerRestartTime() > 0) {
+            setRestartingState();
+        }
+        releaseConnection();
+    }
+
     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
     @Override
     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
     @Override
     public SendReceive getSupportedAddPathTables(final TablesKey tableKey) {
@@ -463,7 +640,7 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
 
     @Override
     public boolean supportsTable(final TablesKey tableKey) {
 
     @Override
     public boolean supportsTable(final TablesKey tableKey) {
-        return this.tables.contains(tableKey) && this.sessionUp;
+        return this.sessionUp && getAfiSafisAdvertized().contains(tableKey);
     }
 
     @Override
     }
 
     @Override
@@ -472,8 +649,15 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     }
 
     @Override
     }
 
     @Override
-    public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
-            final AsyncTransaction<?, ?> transaction, final Throwable cause) {
+    public synchronized void onTransactionChainFailed(final DOMTransactionChain chain,
+            final DOMDataTreeTransaction transaction, final Throwable cause) {
+        LOG.error("Transaction domChain failed.", cause);
+        releaseConnection();
+    }
+
+    @Override
+    public synchronized void onTransactionChainFailed(final TransactionChain chain, final Transaction transaction,
+            final Throwable cause) {
         LOG.error("Transaction domChain failed.", cause);
         releaseConnection();
     }
         LOG.error("Transaction domChain failed.", cause);
         releaseConnection();
     }
@@ -485,24 +669,24 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
 
     @Override
     public synchronized BGPSessionState getBGPSessionState() {
 
     @Override
     public synchronized BGPSessionState getBGPSessionState() {
-        if (this.session instanceof BGPSessionStateProvider) {
-            return ((BGPSessionStateProvider) this.session).getBGPSessionState();
+        if (this.currentSession instanceof BGPSessionStateProvider) {
+            return ((BGPSessionStateProvider) this.currentSession).getBGPSessionState();
         }
         return null;
     }
 
     @Override
     public synchronized BGPTimersState getBGPTimersState() {
         }
         return null;
     }
 
     @Override
     public synchronized BGPTimersState getBGPTimersState() {
-        if (this.session instanceof BGPSessionStateProvider) {
-            return ((BGPSessionStateProvider) this.session).getBGPTimersState();
+        if (this.currentSession instanceof BGPSessionStateProvider) {
+            return ((BGPSessionStateProvider) this.currentSession).getBGPTimersState();
         }
         return null;
     }
 
     @Override
     public synchronized BGPTransportState getBGPTransportState() {
         }
         return null;
     }
 
     @Override
     public synchronized BGPTransportState getBGPTransportState() {
-        if (this.session instanceof BGPSessionStateProvider) {
-            return ((BGPSessionStateProvider) this.session).getBGPTransportState();
+        if (this.currentSession instanceof BGPSessionStateProvider) {
+            return ((BGPSessionStateProvider) this.currentSession).getBGPTransportState();
         }
         return null;
     }
         }
         return null;
     }
@@ -511,4 +695,44 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     public List<RouteTarget> getMemberships() {
         return this.rtMemberships;
     }
     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.currentSelectionDeferralTimerSeconds = selectionDeferralTimerSeconds;
+        setLocalRestartingState(true);
+        return releaseConnection();
+    }
+
+    @Override
+    boolean supportsLLGR() {
+        return this.llgrSupport;
+    }
+
+    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, Collections.emptySet());
+        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);
+    }
 }
 }