YANG revision dates mass-update
[bgpcep.git] / bgp / rib-impl / src / main / java / org / opendaylight / protocol / bgp / rib / impl / BGPPeer.java
index eda7ecaf8cb772c04cf92de061274e2318015d54..937382a0f9a71c5ae1eede7e28d7eff88dd949fd 100644 (file)
@@ -10,15 +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.MoreObjects.ToStringHelper;
-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;
@@ -26,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.md.sal.dom.api.DOMTransactionChain;
-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.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;
@@ -50,22 +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.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,17 +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.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.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;
@@ -98,14 +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 RIB rib;
     private final Map<TablesKey, AdjRibOutListener> adjRibOutListenerSet = new HashMap<>();
-    private final RpcProviderRegistry rpcRegistry;
+    private final List<RouteTarget> rtMemberships = new ArrayList<>();
+    private final RpcProviderService rpcRegistry;
+    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>>() {
@@ -116,47 +136,41 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
             });
 
     @GuardedBy("this")
             });
 
     @GuardedBy("this")
-    private BGPSession session;
-    @GuardedBy("this")
-    private final DOMTransactionChain chain;
+    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(
 
     public BGPPeer(
-            final IpAddress neighborAddress,
+            final BGPTableTypeRegistryConsumer tableTypeRegistry,
+            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.rib = requireNonNull(rib);
         this.rpcRegistry = rpcRegistry;
-        this.chain = rib.createPeerDOMChain(this);
-    }
-
-    BGPPeer(
-            final IpAddress neighborAddress,
-            final RIB rib,
-            final PeerRole role,
-            final RpcProviderRegistry rpcRegistry,
-            final Set<TablesKey> afiSafisAdvertized,
-            final Set<TablesKey> afiSafisGracefulAdvertized) {
-        this(neighborAddress, null, rib, role, null, null, rpcRegistry, afiSafisAdvertized,
-                afiSafisGracefulAdvertized);
+        this.bgpPeer = bgpPeer;
     }
 
     }
 
-
     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);
@@ -200,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()) {
@@ -221,28 +235,27 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     }
 
     public synchronized void instantiateServiceInstance() {
     }
 
     public synchronized void instantiateServiceInstance() {
-        this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this.chain);
+        createDomChain();
+        this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this);
         setActive(true);
     }
 
     @Override
     public synchronized FluentFuture<? extends CommitInfo> close() {
         final FluentFuture<? extends CommitInfo> future = releaseConnection();
         setActive(true);
     }
 
     @Override
     public synchronized FluentFuture<? extends CommitInfo> close() {
         final FluentFuture<? extends CommitInfo> future = releaseConnection();
-        this.chain.close();
+        closeDomChain();
         setActive(false);
         return future;
     }
 
     @Override
     public void onMessage(final BGPSession session, final Notification msg) throws BGPDocumentedException {
         setActive(false);
         return future;
     }
 
     @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());
         }
     }
 
         }
     }
 
@@ -251,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);
+            }
         }
     }
 
         }
     }
 
@@ -298,88 +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.sessionUp = true;
-        if (this.session instanceof BGPSessionStateProvider) {
-            ((BGPSessionStateProvider) this.session).registerMessagesCounter(this);
+        this.bindingChain = this.rib.createPeerChain(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);
-        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) {
-        final RIBSupport ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
+    @Holding("this")
+    private void createAdjRibOutListener(final TablesKey key, final boolean mpSupport) {
+        final RIBSupport<?, ?, ?, ?> ribSupport = this.rib.getRibSupportContext().getRIBSupport(key);
 
         // not particularly nice
 
         // 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);
@@ -388,72 +501,135 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
         }
     }
 
         }
     }
 
-    private synchronized FluentFuture<? extends CommitInfo> cleanup() {
-        // FIXME: BUG-196: support graceful
-        this.adjRibOutListenerSet.values().forEach(AdjRibOutListener::close);
-        this.adjRibOutListenerSet.clear();
-        if (this.effRibInWriter != null) {
-            this.effRibInWriter.close();
-        }
-        this.tables = Collections.emptySet();
-        this.addPathTableMaps = Collections.emptyMap();
-        return removePeer(this.chain, this.peerPath);
-    }
-
     @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 addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
-    }
-
-    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
-        toStringHelper.add("name", this.name);
-        toStringHelper.add("tables", this.tables);
-        return toStringHelper;
+        return MoreObjects.toStringHelper(this).add("name", this.name).add("tables", this.tables).toString();
     }
 
     @Override
     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
         LOG.info("Closing session with peer");
         this.sessionUp = false;
     }
 
     @Override
     public synchronized FluentFuture<? extends CommitInfo> releaseConnection() {
         LOG.info("Closing session with peer");
         this.sessionUp = false;
-        closeRegistration();
-        if (this.rpcRegistration != null) {
-            this.rpcRegistration.close();
+        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();
+            }
         }
         }
-        final FluentFuture<? extends CommitInfo> future = cleanup();
+        releaseBindingChain();
 
 
-        if (this.session != null) {
+        closeSession();
+        return future;
+    }
+
+    @Holding("this")
+    @SuppressWarnings("checkstyle:illegalCatch")
+    private void closeSession() {
+        if (this.currentSession != null) {
             try {
             try {
-                this.session.close();
+                if (isRestartingGracefully()) {
+                    this.currentSession.closeWithoutMessage();
+                } else {
+                    this.currentSession.close();
+                }
             } catch (final Exception e) {
                 LOG.warn("Error closing session with peer", e);
             }
             } catch (final Exception e) {
                 LOG.warn("Error closing session with peer", e);
             }
-            this.session = null;
+            this.currentSession = null;
         }
         }
-        resetState();
-        return future;
     }
 
     }
 
-    private synchronized void closeRegistration() {
+    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;
         }
+        if (this.rpcRegistration != null) {
+            this.rpcRegistration.close();
+        }
+        this.ribWriter.releaseChain();
+
+        if (this.effRibInWriter != null) {
+            this.effRibInWriter.close();
+        }
+        this.tables = ImmutableSet.of();
+        this.addPathTableMaps = Collections.emptyMap();
+        future = removePeer(this.peerPath);
+        resetState();
+
+        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")
     }
 
     @SuppressFBWarnings("IS2_INCONSISTENT_SYNC")
@@ -464,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
@@ -473,15 +649,17 @@ public class BGPPeer extends AbstractPeer implements BGPSessionListener {
     }
 
     @Override
     }
 
     @Override
-    public synchronized void onTransactionChainFailed(final TransactionChain<?, ?> chain,
-            final AsyncTransaction<?, ?> transaction, final Throwable cause) {
-        LOG.error("Transaction chain failed.", cause);
-        this.chain.close();
-        //FIXME BGPCEP-731
-        /*
-        this.chain = this.rib.createPeerDOMChain(this);
-        this.ribWriter = AdjRibInWriter.create(this.rib.getYangRibId(), this.peerRole, this.chain);
-        releaseConnection();*/
+    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();
     }
 
     @Override
     }
 
     @Override
@@ -491,25 +669,70 @@ 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;
     }
+
+    @Override
+    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);
+    }
 }
 }